PageRenderTime 37ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/timepiece/tests/reports/productivity.py

https://gitlab.com/tsltd/django-timepiece
Python | 212 lines | 193 code | 15 blank | 4 comment | 6 complexity | 0465e038f3a4657794c8b761ed5517cd MD5 | raw file
  1. import datetime
  2. from dateutil.relativedelta import relativedelta
  3. import json
  4. from urllib import urlencode
  5. from django.contrib.auth.models import Permission
  6. from django.core.urlresolvers import reverse
  7. from timepiece import models as timepiece
  8. from timepiece.tests.base import TimepieceDataTestCase
  9. class TestProductivityReport(TimepieceDataTestCase):
  10. url_name = 'productivity_report'
  11. def setUp(self):
  12. self.username = 'test_user'
  13. self.password = 'password'
  14. self.user = self.create_user(username=self.username,
  15. password=self.password)
  16. self.permission = Permission.objects.get(codename='view_entry_summary')
  17. self.user.user_permissions.add(self.permission)
  18. self.client.login(username=self.username, password=self.password)
  19. self.project = self.create_project()
  20. self.users = []
  21. self.users.append(self.create_user(first_name='Person', last_name='1'))
  22. self.users.append(self.create_user(first_name='Person', last_name='2'))
  23. self.users.append(self.create_user(first_name='Person', last_name='3'))
  24. self.weeks = []
  25. self.weeks.append(datetime.datetime(2012, 9, 24))
  26. self.weeks.append(datetime.datetime(2012, 10, 1))
  27. self.weeks.append(datetime.datetime(2012, 10, 8))
  28. self.weeks.append(datetime.datetime(2012, 10, 15))
  29. self._create_entries()
  30. self._create_assignments()
  31. def _create_entries(self):
  32. for start_time in (self.weeks[1], self.weeks[3]):
  33. for user in (self.users[1], self.users[2]):
  34. end_time = start_time + relativedelta(hours=2)
  35. data = {'user': user, 'start_time': start_time,
  36. 'end_time': end_time, 'project': self.project}
  37. self.create_entry(data)
  38. def _create_assignments(self):
  39. for week_start in (self.weeks[0], self.weeks[1]):
  40. for user in (self.users[0], self.users[1]):
  41. data = {'user': user, 'week_start': week_start,
  42. 'project': self.project, 'hours': 2}
  43. self.create_project_hours_entry(**data)
  44. def _get(self, url_name=None, url_kwargs=None, get_kwargs=None, **kwargs):
  45. """Convenience wrapper for test client GET request."""
  46. url_name = url_name or self.url_name
  47. url = reverse(url_name, kwargs=url_kwargs)
  48. if get_kwargs:
  49. url += '?' + urlencode(get_kwargs)
  50. return self.client.get(url, **kwargs)
  51. def _unpack(self, response):
  52. form = response.context['form']
  53. report = json.loads(response.context['report'])
  54. organize_by = response.context['type']
  55. worked = response.context['total_worked']
  56. assigned = response.context['total_assigned']
  57. return form, report, organize_by, worked, assigned
  58. def _check_row(self, row, correct):
  59. self.assertEqual(len(row), 3)
  60. self.assertEqual(row[0], correct[0])
  61. self.assertEqual(float(row[1]), correct[1])
  62. self.assertEqual(float(row[2]), correct[2])
  63. def test_not_authenticated(self):
  64. """User must be logged in to see this report."""
  65. self.client.logout()
  66. response = self._get()
  67. self.assertEqual(response.status_code, 302) # Redirects to login
  68. def test_no_permission(self):
  69. """This report requires permission to view entry summaries."""
  70. self.user.user_permissions.remove(self.permission)
  71. response = self._get()
  72. self.assertEqual(response.status_code, 302) # Redirects to login
  73. def test_retrieval(self):
  74. """No report data should be returned upon initial retrieval."""
  75. response = self._get()
  76. self.assertEqual(response.status_code, 200)
  77. form, report, organize_by, worked, assigned = self._unpack(response)
  78. self.assertEqual(len(form.errors), 0)
  79. self.assertEqual(len(report), 0)
  80. self.assertEqual(organize_by, '')
  81. self.assertEqual(float(worked), 0.0)
  82. self.assertEqual(float(assigned), 0.0)
  83. def test_no_project(self):
  84. """Form requires specification of project."""
  85. data = {'organize_by': 'week'}
  86. response = self._get(data=data)
  87. self.assertEqual(response.status_code, 200)
  88. form, report, organize_by, worked, assigned = self._unpack(response)
  89. self.assertEqual(len(form.errors), 1)
  90. self.assertTrue('project' in form.errors)
  91. self.assertEqual(len(report), 0)
  92. self.assertEqual(organize_by, '')
  93. self.assertEqual(float(worked), 0.0)
  94. self.assertEqual(float(assigned), 0.0)
  95. def test_invalid_project_id(self):
  96. """Form requires specification of valid project."""
  97. data = {'organize_by': 'week', 'project_1': 12345}
  98. response = self._get(data=data)
  99. self.assertEqual(response.status_code, 200)
  100. form, report, organize_by, worked, assigned = self._unpack(response)
  101. self.assertEqual(len(form.errors), 1)
  102. self.assertTrue('project' in form.errors)
  103. self.assertEqual(len(report), 0)
  104. self.assertEqual(organize_by, '')
  105. self.assertEqual(float(worked), 0.0)
  106. self.assertEqual(float(assigned), 0.0)
  107. def test_no_organize_by(self):
  108. """Form requires specification of organization method."""
  109. data = {'project_1': self.project.pk}
  110. response = self._get(data=data)
  111. self.assertEqual(response.status_code, 200)
  112. form, report, organize_by, worked, assigned = self._unpack(response)
  113. self.assertEqual(len(form.errors), 1)
  114. self.assertTrue('organize_by' in form.errors)
  115. self.assertEqual(len(report), 0)
  116. self.assertEqual(organize_by, '')
  117. self.assertEqual(float(worked), 0.0)
  118. self.assertEqual(float(assigned), 0.0)
  119. def test_invalid_organize_by(self):
  120. """Form requires specification of valid organization method."""
  121. data = {'project_1': self.project.pk, 'organize_by': 'invalid'}
  122. response = self._get(data=data)
  123. self.assertEqual(response.status_code, 200)
  124. form, report, organize_by, worked, assigned = self._unpack(response)
  125. self.assertEqual(len(form.errors), 1)
  126. self.assertTrue('organize_by' in form.errors)
  127. self.assertEqual(len(report), 0)
  128. self.assertEqual(organize_by, '')
  129. self.assertEqual(float(worked), 0.0)
  130. self.assertEqual(float(assigned), 0.0)
  131. def test_no_data(self):
  132. """If no data, report should contain header row only."""
  133. timepiece.Entry.objects.filter(project=self.project).delete()
  134. timepiece.ProjectHours.objects.filter(project=self.project).delete()
  135. data = {'project_1': self.project.pk, 'organize_by': 'week'}
  136. response = self._get(data=data)
  137. self.assertEqual(response.status_code, 200)
  138. form, report, organize_by, worked, assigned = self._unpack(response)
  139. self.assertEqual(len(form.errors), 0)
  140. self.assertEqual(len(report), 1)
  141. self.assertEqual(organize_by, 'week')
  142. self.assertEqual(float(worked), 0.0)
  143. self.assertEqual(float(assigned), 0.0)
  144. def test_organize_by_week(self):
  145. """Report should contain hours per week on the project."""
  146. data = {'project_1': self.project.pk, 'organize_by': 'week'}
  147. response = self._get(data=data)
  148. self.assertEqual(response.status_code, 200)
  149. form, report, organize_by, worked, assigned = self._unpack(response)
  150. self.assertEqual(len(form.errors), 0)
  151. self.assertEqual(organize_by, 'week')
  152. self.assertEqual(float(worked), 8.0)
  153. self.assertEqual(float(assigned), 8.0)
  154. self.assertEqual(len(report), 1 + 4) # Include header row
  155. self._check_row(report[1], ['2012-09-24', 0.0, 4.0])
  156. self._check_row(report[2], ['2012-10-01', 4.0, 4.0])
  157. self._check_row(report[3], ['2012-10-08', 0.0, 0.0])
  158. self._check_row(report[4], ['2012-10-15', 4.0, 0.0])
  159. def test_organize_by_people(self):
  160. """Report should contain hours per peron on the project."""
  161. data = {'project_1': self.project.pk, 'organize_by': 'person'}
  162. response = self._get(data=data)
  163. self.assertEqual(response.status_code, 200)
  164. form, report, organize_by, worked, assigned = self._unpack(response)
  165. self.assertEqual(len(form.errors), 0)
  166. self.assertEqual(organize_by, 'person')
  167. self.assertEqual(float(worked), 8.0)
  168. self.assertEqual(float(assigned), 8.0)
  169. self.assertEqual(len(report), 1 + 3) # Include header row
  170. self._check_row(report[1], ['Person 1', 0.0, 4.0])
  171. self._check_row(report[2], ['Person 2', 4.0, 4.0])
  172. self._check_row(report[3], ['Person 3', 4.0, 0.0])
  173. def test_export(self):
  174. """Data should be exported in CSV format."""
  175. data = {'project_1': self.project.pk, 'organize_by': 'week',
  176. 'export': True}
  177. response = self._get(data=data)
  178. self.assertEqual(response.status_code, 200)
  179. data = dict(response.items())
  180. self.assertEqual(data['Content-Type'], 'text/csv')
  181. disposition = 'attachment; filename={0}_productivity.csv'.format(
  182. self.project.name)
  183. self.assertTrue(data['Content-Disposition'].startswith(disposition))
  184. report = response.content.splitlines()
  185. self.assertEqual(len(report), 1 + 4) # Include header row
  186. self._check_row(report[1].split(','), ['2012-09-24', 0.0, 4.0])
  187. self._check_row(report[2].split(','), ['2012-10-01', 4.0, 4.0])
  188. self._check_row(report[3].split(','), ['2012-10-08', 0.0, 0.0])
  189. self._check_row(report[4].split(','), ['2012-10-15', 4.0, 0.0])