PageRenderTime 61ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/coral/tracker/models.py

https://github.com/robgolding63/coral
Python | 203 lines | 172 code | 30 blank | 1 comment | 15 complexity | f47a8b13dffc729f2c2d40de9541dac4 MD5 | raw file
  1. ## Skeleton models file for Bug Tracker
  2. from django.db import models
  3. from django.db.models import get_model
  4. from django.db.models import signals
  5. from django.db.models.signals import pre_save
  6. from django.contrib.auth.models import User
  7. from tagging.fields import TagField
  8. from managers import IssueManager, IssuePriorityManager
  9. import settings
  10. from utils import get_changes_between_models
  11. import datetime
  12. ISSUE_PRIORITY_LEVELS = (
  13. (1, 1),
  14. (2, 2),
  15. (3, 3),
  16. (4, 4),
  17. (5, 5)
  18. )
  19. ISSUE_STATUS_CHOICES = (
  20. ('active', 'Active'),
  21. ('resolved', 'Resolved'),
  22. ('closed', 'Closed'),
  23. )
  24. ISSUE_CHANGE_FIELDS = (
  25. ('assigned_to', 'Assigned to'),
  26. ('status', 'Status'),
  27. ('priority', 'Priority'),
  28. )
  29. class IssueChange(models.Model):
  30. action = models.ForeignKey('IssueAction', related_name='changes')
  31. change_field = models.CharField(max_length=255, choices=ISSUE_CHANGE_FIELDS)
  32. old_value = models.CharField(max_length=255)
  33. new_value = models.CharField(max_length=255)
  34. def __unicode__(self):
  35. return '[IssueChange] %s changed from %s to %s' % (self.get_change_field_display(), self.old_value, self.new_value)
  36. def get_old_or_new_value(self, old_or_new_value):
  37. if old_or_new_value == 'old_value' or old_or_new_value == 'new_value':
  38. model = self.action.issue.__class__
  39. if isinstance(model._meta.get_field(self.change_field), models.ForeignKey):
  40. return self.action.issue.__getattribute__(self.change_field).__class__._default_manager.get(pk=self.__getattribute__(old_or_new_value))
  41. elif hasattr(model, 'get_%s_display' % self.change_field):
  42. issue = model()
  43. value = self.__getattribute__(old_or_new_value)
  44. if isinstance(issue._meta.get_field(self.change_field), models.IntegerField):
  45. value = int(value)
  46. issue.__setattr__(self.change_field, value)
  47. return issue._get_FIELD_display(issue._meta.get_field(self.change_field))
  48. else:
  49. return self.__getattribute__(old_or_new_value)
  50. else:
  51. raise ValueError, 'get_old_or_new_value must be supplied either "old_value" or "new_value"'
  52. def get_old_value(self):
  53. return self.get_old_or_new_value('old_value')
  54. def get_new_value(self):
  55. return self.get_old_or_new_value('new_value')
  56. class IssueAction(models.Model):
  57. issue = models.ForeignKey('Issue', related_name='actions')
  58. notes = models.TextField()
  59. performed_at = models.DateTimeField(auto_now=True)
  60. performed_by = models.ForeignKey(User)
  61. @property
  62. def has_content(self):
  63. return self.notes or self.changes.all()
  64. def __unicode__(self):
  65. return '[IssueAction] on issue #%s at %s' % (self.issue.id, self.performed_at)
  66. class Meta:
  67. ordering = ['performed_at']
  68. class Star(models.Model):
  69. issue = models.ForeignKey('Issue', related_name='stars')
  70. user = models.ForeignKey(User, related_name='stars')
  71. class Meta:
  72. unique_together = ('issue', 'user',)
  73. def __unicode__(self):
  74. return '%s [%s]' % (self.issue, self.user)
  75. class IssuePriority(models.Model):
  76. name = models.CharField(max_length=255)
  77. level = models.IntegerField(choices=ISSUE_PRIORITY_LEVELS)
  78. def __unicode__(self):
  79. return self.name
  80. class Meta:
  81. verbose_name_plural = 'Issue priorities'
  82. ordering = ['level', 'name']
  83. class Issue(models.Model):
  84. raised_by = models.ForeignKey(User, related_name='raised')
  85. assigned_to = models.ForeignKey(User, related_name='assigned')
  86. originally_assigned_to = models.ForeignKey(User, editable=False)
  87. priority = models.ForeignKey(IssuePriority)
  88. title = models.CharField(max_length=255)
  89. description = models.TextField()
  90. status = models.CharField(max_length=20, choices=ISSUE_STATUS_CHOICES)
  91. created_at = models.DateTimeField(auto_now_add=True)
  92. last_updated = models.DateTimeField(auto_now=True, auto_now_add=True)
  93. viewed_by = models.ManyToManyField(User, related_name="viewed_issues", editable=False)
  94. tags = TagField()
  95. objects = IssueManager()
  96. def __unicode__(self):
  97. return '%s - %s' % (self.id, self.title)
  98. def save(self, force_insert=False, force_update=False):
  99. if not self.id:
  100. self.originally_assigned_to = self.assigned_to
  101. super(self.__class__, self).save(force_insert, force_update)
  102. def _get_changes(self, new_issue):
  103. changes = {}
  104. for field, _ in ISSUE_CHANGE_FIELDS:
  105. old_value = self.__getattribute__(field)
  106. new_value = new_issue.__getattribute__(field)
  107. if old_value != new_value:
  108. if isinstance(Issue._meta.get_field(field), models.ForeignKey):
  109. old_value = old_value._get_pk_val()
  110. new_value = new_value._get_pk_val()
  111. changes[field] = (old_value, new_value)
  112. return changes
  113. def register_action(self, new_issue, notes, user):
  114. changes = self._get_changes(new_issue)
  115. if notes or changes:
  116. if not notes: notes = ''
  117. action = IssueAction(issue=self, notes=notes, performed_by=user)
  118. action.save()
  119. for field, values in changes.items():
  120. IssueChange(action=action, change_field=field, old_value=values[0], new_value=values[1]).save()
  121. def add_note(self, note, user):
  122. IssueAction(issue=self, notes=note, performed_by=user).save()
  123. def is_starred_for(self, user):
  124. return user in self.starred_for
  125. def star_for(self, user):
  126. return Star.objects.get_or_create(issue=self, user=user)
  127. def unstar_for(self, user):
  128. try:
  129. star = Star.objects.get(issue=self, user=user)
  130. except Star.DoesNotExist:
  131. return
  132. star.delete()
  133. @models.permalink
  134. def get_absolute_url(self):
  135. return ('tracker_issue_detail', (), {'object_id': self.id})
  136. @property
  137. def is_editable(self):
  138. delta = datetime.timedelta(seconds=settings.ISSUE_DESCRIPTION_EDITABLE_SECONDS)
  139. return self.created_at > datetime.datetime.now() - delta
  140. @property
  141. def editable_timeleft(self):
  142. delta = datetime.timedelta(seconds=settings.ISSUE_DESCRIPTION_EDITABLE_SECONDS)
  143. if not self.is_editable:
  144. return None
  145. return int(round(((delta - (datetime.datetime.now() - self.created_at)).seconds/60)))
  146. @property
  147. def starred_for(self):
  148. return list(set([star.user for star in self.stars.all()]))
  149. @property
  150. def has_history(self):
  151. if not self.actions.count():
  152. return False
  153. for action in self.actions.all():
  154. if action.has_content:
  155. return True
  156. return False
  157. def get_tags(self):
  158. from tagging.models import Tag
  159. return Tag.objects.get_for_object(self)
  160. class Meta:
  161. verbose_name_plural = 'Issues'
  162. ordering = ['-last_updated', '-priority__level']