models.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. from __future__ import unicode_literals
  2. from django.db import models
  3. from django.utils import timezone
  4. # Create your models here.
  5. class Project(models.Model):
  6. '''
  7. Represents a project comprised of several tasks, a beginning and an end,
  8. and metadata such as scope, resources allocated, etc.
  9. Models Task, Risk, Issue and Status all depend on this as a foreign key.
  10. '''
  11. name = models.CharField(max_length=255)
  12. scope = models.TextField()
  13. resources = models.TextField(blank=True)
  14. def __unicode__(self):
  15. return self.name
  16. class Task(models.Model):
  17. '''
  18. Represents a single task within an overall project.
  19. Can have a status between not started, work in progress and complete.
  20. Automatically gets flagged as delayed.
  21. '''
  22. title = models.CharField(max_length=255)
  23. description = models.TextField()
  24. start = models.DateField(default=timezone.now)
  25. end = models.DateField(default=timezone.now)
  26. STATUSES = (
  27. ('NS', 'Not started'),
  28. ('ST', 'Work in progress'),
  29. ('CO', 'Complete'),
  30. )
  31. status = models.CharField(
  32. max_length=2,
  33. choices=STATUSES,
  34. default='NS',
  35. )
  36. project = models.ForeignKey(Project, on_delete=models.CASCADE, editable=False)
  37. def is_late(self):
  38. '''
  39. Tasks that should have started but didn't, or that should have ended
  40. but didn't get flagged here.
  41. FIXME: these perform some kind of conversion I can't seem to match.
  42. '''
  43. if self.status != 'CO':
  44. return self.start > timezone.now() or self.end > timezone.now()
  45. else:
  46. return False
  47. def __unicode__(self):
  48. return self.title
  49. # Issues, Risks, etc.
  50. class Risk(models.Model):
  51. '''
  52. Represents a single project risk containing a description, impact,
  53. urgency, response strategy, and response plan due date.
  54. '''
  55. IMPACT_ROSTER = (
  56. ('1', 'Low impact'),
  57. ('2', 'Medium impact'),
  58. ('3', 'Severe impact'),
  59. )
  60. RESPONSE_ROSTER = (
  61. ('AC', 'Accept risk'),
  62. ('MT', 'Mitigate risk'),
  63. ('EL', 'Eliminate risk'),
  64. )
  65. STATUSES = (
  66. ('NS', "Not started"),
  67. ('WP', "Work in progress"),
  68. ('CO', "Closed"),
  69. )
  70. name = models.CharField(max_length=100)
  71. description = models.TextField()
  72. impact = models.CharField(
  73. max_length=1,
  74. choices=IMPACT_ROSTER,
  75. )
  76. response = models.CharField(
  77. max_length=2,
  78. choices=RESPONSE_ROSTER,
  79. )
  80. response_plan = models.TextField()
  81. response_plan_due = models.DateField(default=timezone.now)
  82. status = models.CharField(
  83. max_length=2,
  84. choices=STATUSES,
  85. default='NS'
  86. )
  87. project = models.ForeignKey(Project, on_delete=models.CASCADE, editable=False)
  88. def __unicode__(self):
  89. return "[%s] - %s (%s)" % (self.project, self.name, self.impact)
  90. class Issue(models.Model):
  91. '''
  92. A single project issue with description, severity, action plan and action
  93. plan due date.
  94. '''
  95. SEVERITY_ROSTER = (
  96. ('1', 'Low impact'),
  97. ('2', 'Medium impact'),
  98. ('3', 'Severe impact'),
  99. )
  100. STATUSES = (
  101. ('NS', "Open"),
  102. ('WP', "Work in progress"),
  103. ('CO', "Closed"),
  104. )
  105. name = models.CharField(max_length=255)
  106. description = models.TextField()
  107. severity = models.CharField(
  108. max_length=1,
  109. choices=SEVERITY_ROSTER
  110. )
  111. status = models.CharField(
  112. max_length=2,
  113. choices=STATUSES,
  114. default='NS'
  115. )
  116. action_plan = models.TextField()
  117. action_plan_due = models.DateField(default=timezone.now)
  118. project = models.ForeignKey(Project, on_delete=models.CASCADE, editable=False)
  119. def __unicode__(self):
  120. return "[%s] - %s (%s)" % (self.project, self.name, self.severity)
  121. class Status(models.Model):
  122. '''
  123. Represents a status issued for a project with detailed status, next moves,
  124. and individual statuses for cost, time and quality dimensions.
  125. '''
  126. STATUSES = (
  127. ('G', 'Green'),
  128. ('A', 'Amber'),
  129. ('R', 'Red'),
  130. )
  131. date = models.DateField(default=timezone.now)
  132. status = models.TextField()
  133. next_moves = models.TextField()
  134. cost = models.CharField(
  135. max_length=1,
  136. choices=STATUSES,
  137. default='G',
  138. )
  139. time = models.CharField(
  140. max_length=1,
  141. choices=STATUSES,
  142. default='G',
  143. )
  144. quality = models.CharField(
  145. max_length=1,
  146. choices=STATUSES,
  147. default='G',
  148. )
  149. project = models.ForeignKey(Project, on_delete=models.CASCADE)
  150. def __unicode__(self):
  151. return "Status for '%s' on %s" % (self.project, self.date)
  152. class Meta:
  153. verbose_name_plural = "statuses"