• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rdmorganiser / rdmo / 20428555173

22 Dec 2025 10:02AM UTC coverage: 94.693% (-0.1%) from 94.814%
20428555173

Pull #1436

github

web-flow
Merge 0ffb48a5d into 57a75b09e
Pull Request #1436: Draft: add `rdmo.config` app for `Plugin` model (plugin managament)

2191 of 2304 branches covered (95.1%)

23411 of 24723 relevant lines covered (94.69%)

3.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
rdmo/projects/models/integration.py
1
from django.db import models
4✔
2
from django.urls import reverse
4✔
3
from django.utils.translation import gettext_lazy as _
4✔
4

5
from rdmo.config.constants import PluginType
4✔
6
from rdmo.config.models import Plugin
4✔
7

8
from ..managers import IntegrationManager
4✔
9

10

11
class Integration(models.Model):
4✔
12

13
    project = models.ForeignKey(
4✔
14
        'Project', on_delete=models.CASCADE, related_name='integrations',
15
        verbose_name=_('Project'),
16
        help_text=_('The project for this integration.')
17
    )
18
    provider_key = models.TextField(
4✔
19
        verbose_name=_('Provider key'),
20
        help_text=_('The key of the provider for this integration.')
21
    )
22

23
    objects = IntegrationManager()
4✔
24

25
    class Meta:
4✔
26
        ordering = ('project__title', )
4✔
27
        verbose_name = _('Integration')
4✔
28
        verbose_name_plural = _('Integrations')
4✔
29

30
    def __str__(self):
4✔
31
        return f'{self.project.title} / {self.provider_key}'
4✔
32

33
    def get_absolute_url(self):
4✔
34
        return reverse('project', kwargs={'pk': self.project.pk})
×
35

36
    @property
4✔
37
    def provider(self):
4✔
38
        plugins = (
4✔
39
            Plugin.objects.for_context(
40
                plugin_type=PluginType.PROJECT_ISSUE_PROVIDER,
41
                project=self.project,
42
                format=self.provider_key)
43
        )
44
        if plugins.exists():
4✔
45
            return plugins.first().initialize_class()
4✔
46
        else:
47
            return None
×
48

49
    def get_option_value(self, key):
4✔
50
        try:
4✔
51
            return self.options.get(key=key).value
4✔
52
        except IntegrationOption.DoesNotExist:
×
53
            return None
×
54

55
    def save_options(self, options):
4✔
56
        if self.provider is None:
4✔
57
            raise ValueError(_('The provider key is required.'))
×
58
        for field in self.provider.fields:
4✔
59
            try:
4✔
60
                integration_option = IntegrationOption.objects.get(integration=self, key=field.get('key'))
4✔
61
            except IntegrationOption.DoesNotExist:
4✔
62
                integration_option = IntegrationOption(integration=self, key=field.get('key'))
4✔
63

64
            integration_option.value = options.get(field.get('key')) or ''  # required was (hopefully) checked before
4✔
65
            integration_option.secret = field.get('secret', False)
4✔
66
            integration_option.save()
4✔
67

68

69
class IntegrationOption(models.Model):
4✔
70

71
    integration = models.ForeignKey(
4✔
72
        'Integration', on_delete=models.CASCADE, related_name='options',
73
        verbose_name=_('Integration'),
74
        help_text=_('The integration for this integration option.')
75
    )
76
    key = models.SlugField(
4✔
77
        max_length=128,
78
        verbose_name=_('Key'),
79
        help_text=_('The key for this integration option.')
80
    )
81
    value = models.TextField(
4✔
82
        verbose_name=_('Value'),
83
        help_text=_('The value for this integration option.')
84
    )
85
    secret = models.BooleanField(
4✔
86
        default=False,
87
        verbose_name=_('Secret'),
88
        help_text=_('Designates whether this integration option is hidden from regular users.')
89
    )
90

91
    class Meta:
4✔
92
        ordering = ('integration__project__title', )
4✔
93
        verbose_name = _('Integration option')
4✔
94
        verbose_name_plural = _('Integration options')
4✔
95

96
    def __str__(self):
4✔
97
        return f'{self.integration.project.title} / {self.integration.provider_key} / {self.key} = {self.value}'
×
98

99
    @property
4✔
100
    def title(self):
4✔
101
        return self.key.title().replace('_', ' ')
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc