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

rdmorganiser / rdmo / 17951596797

23 Sep 2025 03:46PM UTC coverage: 94.624% (-0.2%) from 94.796%
17951596797

Pull #1432

github

web-flow
Merge 98f3be8fc into 79917de8d
Pull Request #1432: RDMO 3.0.0 🌠

2110 of 2226 branches covered (94.79%)

22634 of 23920 relevant lines covered (94.62%)

3.78 hits per line

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

84.38
rdmo/projects/models/issue.py
1
from datetime import date, timedelta
4✔
2
from itertools import zip_longest
4✔
3

4
from django.db import models
4✔
5
from django.urls import reverse
4✔
6
from django.utils.translation import gettext_lazy as _
4✔
7

8
from rdmo.tasks.models import Task
4✔
9

10
from ..managers import IssueManager
4✔
11

12

13
class Issue(models.Model):
4✔
14

15
    ISSUE_STATUS_OPEN = 'open'
4✔
16
    ISSUE_STATUS_IN_PROGRESS = 'in_progress'
4✔
17
    ISSUE_STATUS_CLOSED = 'closed'
4✔
18
    ISSUE_STATUS_CHOICES = (
4✔
19
        (ISSUE_STATUS_OPEN, _('open')),
20
        (ISSUE_STATUS_IN_PROGRESS, _('in progress')),
21
        (ISSUE_STATUS_CLOSED, _('closed'))
22
    )
23

24
    project = models.ForeignKey(
4✔
25
        'Project', on_delete=models.CASCADE, related_name='issues',
26
        verbose_name=_('Project'),
27
        help_text=_('The project for this issue.')
28
    )
29
    task = models.ForeignKey(
4✔
30
        Task, on_delete=models.CASCADE, related_name='issues',
31
        verbose_name=_('Task'),
32
        help_text=_('The task for this issue.')
33
    )
34
    status = models.CharField(
4✔
35
        max_length=12, choices=ISSUE_STATUS_CHOICES, default=ISSUE_STATUS_OPEN,
36
        verbose_name=_('Status'),
37
        help_text=_('The status for this issue.')
38
    )
39

40
    objects = IssueManager()
4✔
41
    class Meta:
4✔
42
        ordering = ('project__title', 'task__uri')
4✔
43
        verbose_name = _('Issue')
4✔
44
        verbose_name_plural = _('Issues')
4✔
45

46
    def __str__(self):
4✔
47
        return f'{self.project.title} / {self.task} / {self.status}'
4✔
48

49
    def get_absolute_url(self):
4✔
50
        return reverse('project', kwargs={'pk': self.project.pk})
4✔
51

52
    def resolve(self, values):
4✔
53
        for condition in self.task.conditions.all():
×
54
            if condition.resolve(values):
×
55
                return True
×
56

57
    @property
4✔
58
    def dates(self):
4✔
59
        values = self.project.values.filter(snapshot=None)
4✔
60

61
        if self.task.start_attribute:
4✔
62
            start_values = values.filter(attribute=self.task.start_attribute)
4✔
63
        else:
64
            start_values = []
×
65

66
        if self.task.end_attribute:
4✔
67
            end_values = values.filter(attribute=self.task.end_attribute)
4✔
68
        else:
69
            end_values = []
×
70

71
        days_before = timedelta(self.task.days_before) if self.task.days_before else timedelta()
4✔
72
        days_after = timedelta(self.task.days_after) if self.task.days_after else timedelta()
4✔
73

74
        dates = []
4✔
75
        for start_value, end_value in zip_longest(start_values, end_values):
4✔
76

77
            if start_value and start_value.value and isinstance(start_value.value, date):
4✔
78
                start_date = start_value.value
×
79
            else:
80
                start_date = None
4✔
81

82
            if end_value and end_value.value and isinstance(end_value.value, date):
4✔
83
                end_date = end_value.value
4✔
84
            else:
85
                end_date = None
×
86

87
            if start_date and end_date:
4✔
88
                dates.append((start_date - days_before, end_date + days_after))
×
89
            elif start_date:
4✔
90
                dates.append((start_date - days_before + days_after, ))
×
91
            elif end_date:
4✔
92
                dates.append((end_date - days_before + days_after, ))
4✔
93

94
        return dates
4✔
95

96

97
class IssueResource(models.Model):
4✔
98

99
    issue = models.ForeignKey(
4✔
100
        'Issue', on_delete=models.CASCADE, related_name='resources',
101
        verbose_name=_('Issue'),
102
        help_text=_('The issue for this issue resource.')
103
    )
104
    integration = models.ForeignKey(
4✔
105
        'Integration', on_delete=models.CASCADE, related_name='resources',
106
        verbose_name=_('Integration'),
107
        help_text=_('The integration for this issue resource.')
108
    )
109
    url = models.URLField(
4✔
110
        verbose_name=_('URL'),
111
        help_text=_('The URL of this issue resource.')
112
    )
113

114
    class Meta:
4✔
115
        ordering = ('issue__project__title', 'issue')
4✔
116
        verbose_name = _('Issue resource')
4✔
117
        verbose_name_plural = _('Issue resources')
4✔
118

119
    def __str__(self):
4✔
120
        return f'{self.issue.project.title} / {self.issue} / {self.url}'
×
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