service.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. # Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
  2. # Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
  3. # Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU Affero General Public License as
  6. # published by the Free Software Foundation, either version 3 of the
  7. # License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU Affero General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. import uuid
  17. import os.path as path
  18. from unidecode import unidecode
  19. from django.template.defaultfilters import slugify
  20. from django.contrib.contenttypes.models import ContentType
  21. from django.core.exceptions import ObjectDoesNotExist
  22. from taiga.projects.history.services import make_key_from_model_object, take_snapshot
  23. from taiga.timeline.service import build_project_namespace
  24. from taiga.projects.references import sequences as seq
  25. from taiga.projects.references import models as refs
  26. from taiga.projects.userstories.models import RolePoints
  27. from taiga.projects.services import find_invited_user
  28. from . import serializers
  29. _errors_log = {}
  30. def get_errors(clear=True):
  31. _errors = _errors_log.copy()
  32. if clear:
  33. _errors_log.clear()
  34. return _errors
  35. def add_errors(section, errors):
  36. if section in _errors_log:
  37. _errors_log[section].append(errors)
  38. else:
  39. _errors_log[section] = [errors]
  40. def project_to_dict(project):
  41. return serializers.ProjectExportSerializer(project).data
  42. def store_project(data):
  43. project_data = {}
  44. for key, value in data.items():
  45. excluded_fields = [
  46. "default_points", "default_us_status", "default_task_status",
  47. "default_priority", "default_severity", "default_issue_status",
  48. "default_issue_type", "memberships", "points", "us_statuses",
  49. "task_statuses", "issue_statuses", "priorities", "severities",
  50. "issue_types", "userstorycustomattributes", "taskcustomattributes",
  51. "issuecustomattributes", "roles", "milestones", "wiki_pages",
  52. "wiki_links", "notify_policies", "user_stories", "issues", "tasks",
  53. ]
  54. if key not in excluded_fields:
  55. project_data[key] = value
  56. serialized = serializers.ProjectExportSerializer(data=project_data)
  57. if serialized.is_valid():
  58. serialized.object._importing = True
  59. serialized.object.save()
  60. serialized.save_watchers()
  61. return serialized
  62. add_errors("project", serialized.errors)
  63. return None
  64. def _store_choice(project, data, field, serializer):
  65. serialized = serializer(data=data)
  66. if serialized.is_valid():
  67. serialized.object.project = project
  68. serialized.object._importing = True
  69. serialized.save()
  70. return serialized.object
  71. add_errors(field, serialized.errors)
  72. return None
  73. def store_choices(project, data, field, serializer):
  74. result = []
  75. for choice_data in data.get(field, []):
  76. result.append(_store_choice(project, choice_data, field, serializer))
  77. return result
  78. def _store_custom_attribute(project, data, field, serializer):
  79. serialized = serializer(data=data)
  80. if serialized.is_valid():
  81. serialized.object.project = project
  82. serialized.object._importing = True
  83. serialized.save()
  84. return serialized.object
  85. add_errors(field, serialized.errors)
  86. return None
  87. def store_custom_attributes(project, data, field, serializer):
  88. result = []
  89. for custom_attribute_data in data.get(field, []):
  90. result.append(_store_custom_attribute(project, custom_attribute_data, field, serializer))
  91. return result
  92. def store_custom_attributes_values(obj, data_values, obj_field, serializer_class):
  93. data = {
  94. obj_field: obj.id,
  95. "attributes_values": data_values,
  96. }
  97. try:
  98. custom_attributes_values = obj.custom_attributes_values
  99. serializer = serializer_class(custom_attributes_values, data=data)
  100. except ObjectDoesNotExist:
  101. serializer = serializer_class(data=data)
  102. if serializer.is_valid():
  103. serializer.save()
  104. return serializer
  105. add_errors("custom_attributes_values", serializer.errors)
  106. return None
  107. def _use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes, values):
  108. ret = {}
  109. for attr in custom_attributes:
  110. value = values.get(attr["name"], None)
  111. if value is not None:
  112. ret[str(attr["id"])] = value
  113. return ret
  114. def store_role(project, role):
  115. serialized = serializers.RoleExportSerializer(data=role)
  116. if serialized.is_valid():
  117. serialized.object.project = project
  118. serialized.object._importing = True
  119. serialized.save()
  120. return serialized
  121. add_errors("roles", serialized.errors)
  122. return None
  123. def store_roles(project, data):
  124. results = []
  125. for role in data.get("roles", []):
  126. serialized = store_role(project, role)
  127. if serialized:
  128. results.append(serialized)
  129. return results
  130. def store_default_choices(project, data):
  131. def helper(project, field, related, data):
  132. if field in data:
  133. value = related.all().get(name=data[field])
  134. else:
  135. value = related.all().first()
  136. setattr(project, field, value)
  137. helper(project, "default_points", project.points, data)
  138. helper(project, "default_issue_type", project.issue_types, data)
  139. helper(project, "default_issue_status", project.issue_statuses, data)
  140. helper(project, "default_us_status", project.us_statuses, data)
  141. helper(project, "default_task_status", project.task_statuses, data)
  142. helper(project, "default_priority", project.priorities, data)
  143. helper(project, "default_severity", project.severities, data)
  144. project._importing = True
  145. project.save()
  146. def store_membership(project, membership):
  147. serialized = serializers.MembershipExportSerializer(data=membership, context={"project": project})
  148. if serialized.is_valid():
  149. serialized.object.project = project
  150. serialized.object._importing = True
  151. serialized.object.token = str(uuid.uuid1())
  152. serialized.object.user = find_invited_user(serialized.object.email,
  153. default=serialized.object.user)
  154. serialized.save()
  155. return serialized
  156. add_errors("memberships", serialized.errors)
  157. return None
  158. def store_memberships(project, data):
  159. results = []
  160. for membership in data.get("memberships", []):
  161. results.append(store_membership(project, membership))
  162. return results
  163. def store_task(project, data):
  164. if "status" not in data and project.default_task_status:
  165. data["status"] = project.default_task_status.name
  166. serialized = serializers.TaskExportSerializer(data=data, context={"project": project})
  167. if serialized.is_valid():
  168. serialized.object.project = project
  169. if serialized.object.owner is None:
  170. serialized.object.owner = serialized.object.project.owner
  171. serialized.object._importing = True
  172. serialized.object._not_notify = True
  173. serialized.save()
  174. serialized.save_watchers()
  175. if serialized.object.ref:
  176. sequence_name = refs.make_sequence_name(project)
  177. if not seq.exists(sequence_name):
  178. seq.create(sequence_name)
  179. seq.set_max(sequence_name, serialized.object.ref)
  180. else:
  181. serialized.object.ref, _ = refs.make_reference(serialized.object, project)
  182. serialized.object.save()
  183. for task_attachment in data.get("attachments", []):
  184. store_attachment(project, serialized.object, task_attachment)
  185. history_entries = data.get("history", [])
  186. for history in history_entries:
  187. store_history(project, serialized.object, history)
  188. if not history_entries:
  189. take_snapshot(serialized.object, user=serialized.object.owner)
  190. custom_attributes_values = data.get("custom_attributes_values", None)
  191. if custom_attributes_values:
  192. custom_attributes = serialized.object.project.taskcustomattributes.all().values('id', 'name')
  193. custom_attributes_values = _use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes,
  194. custom_attributes_values)
  195. store_custom_attributes_values(serialized.object, custom_attributes_values,
  196. "task", serializers.TaskCustomAttributesValuesExportSerializer)
  197. return serialized
  198. add_errors("tasks", serialized.errors)
  199. return None
  200. def store_milestone(project, milestone):
  201. serialized = serializers.MilestoneExportSerializer(data=milestone, project=project)
  202. if serialized.is_valid():
  203. serialized.object.project = project
  204. serialized.object._importing = True
  205. serialized.save()
  206. serialized.save_watchers()
  207. for task_without_us in milestone.get("tasks_without_us", []):
  208. task_without_us["user_story"] = None
  209. store_task(project, task_without_us)
  210. return serialized
  211. add_errors("milestones", serialized.errors)
  212. return None
  213. def store_attachment(project, obj, attachment):
  214. serialized = serializers.AttachmentExportSerializer(data=attachment)
  215. if serialized.is_valid():
  216. serialized.object.content_type = ContentType.objects.get_for_model(obj.__class__)
  217. serialized.object.object_id = obj.id
  218. serialized.object.project = project
  219. if serialized.object.owner is None:
  220. serialized.object.owner = serialized.object.project.owner
  221. serialized.object._importing = True
  222. serialized.object.size = serialized.object.attached_file.size
  223. serialized.object.name = path.basename(serialized.object.attached_file.name)
  224. serialized.save()
  225. return serialized
  226. add_errors("attachments", serialized.errors)
  227. return serialized
  228. def store_timeline_entry(project, timeline):
  229. serialized = serializers.TimelineExportSerializer(data=timeline, context={"project": project})
  230. if serialized.is_valid():
  231. serialized.object.project = project
  232. serialized.object.namespace = build_project_namespace(project)
  233. serialized.object.object_id = project.id
  234. serialized.object._importing = True
  235. serialized.save()
  236. return serialized
  237. add_errors("timeline", serialized.errors)
  238. return serialized
  239. def store_history(project, obj, history):
  240. serialized = serializers.HistoryExportSerializer(data=history, context={"project": project})
  241. if serialized.is_valid():
  242. serialized.object.key = make_key_from_model_object(obj)
  243. if serialized.object.diff is None:
  244. serialized.object.diff = []
  245. serialized.object._importing = True
  246. serialized.save()
  247. return serialized
  248. add_errors("history", serialized.errors)
  249. return serialized
  250. def store_wiki_page(project, wiki_page):
  251. wiki_page["slug"] = slugify(unidecode(wiki_page.get("slug", "")))
  252. serialized = serializers.WikiPageExportSerializer(data=wiki_page)
  253. if serialized.is_valid():
  254. serialized.object.project = project
  255. if serialized.object.owner is None:
  256. serialized.object.owner = serialized.object.project.owner
  257. serialized.object._importing = True
  258. serialized.object._not_notify = True
  259. serialized.save()
  260. serialized.save_watchers()
  261. for attachment in wiki_page.get("attachments", []):
  262. store_attachment(project, serialized.object, attachment)
  263. history_entries = wiki_page.get("history", [])
  264. for history in history_entries:
  265. store_history(project, serialized.object, history)
  266. if not history_entries:
  267. take_snapshot(serialized.object, user=serialized.object.owner)
  268. return serialized
  269. add_errors("wiki_pages", serialized.errors)
  270. return None
  271. def store_wiki_link(project, wiki_link):
  272. serialized = serializers.WikiLinkExportSerializer(data=wiki_link)
  273. if serialized.is_valid():
  274. serialized.object.project = project
  275. serialized.object._importing = True
  276. serialized.save()
  277. return serialized
  278. add_errors("wiki_links", serialized.errors)
  279. return None
  280. def store_role_point(project, us, role_point):
  281. serialized = serializers.RolePointsExportSerializer(data=role_point, context={"project": project})
  282. if serialized.is_valid():
  283. try:
  284. existing_role_point = us.role_points.get(role=serialized.object.role)
  285. existing_role_point.points = serialized.object.points
  286. existing_role_point.save()
  287. return existing_role_point
  288. except RolePoints.DoesNotExist:
  289. serialized.object.user_story = us
  290. serialized.save()
  291. return serialized.object
  292. add_errors("role_points", serialized.errors)
  293. return None
  294. def store_user_story(project, data):
  295. if "status" not in data and project.default_us_status:
  296. data["status"] = project.default_us_status.name
  297. us_data = {key: value for key, value in data.items() if key not in ["role_points", "custom_attributes_values"]}
  298. serialized = serializers.UserStoryExportSerializer(data=us_data, context={"project": project})
  299. if serialized.is_valid():
  300. serialized.object.project = project
  301. if serialized.object.owner is None:
  302. serialized.object.owner = serialized.object.project.owner
  303. serialized.object._importing = True
  304. serialized.object._not_notify = True
  305. serialized.save()
  306. serialized.save_watchers()
  307. if serialized.object.ref:
  308. sequence_name = refs.make_sequence_name(project)
  309. if not seq.exists(sequence_name):
  310. seq.create(sequence_name)
  311. seq.set_max(sequence_name, serialized.object.ref)
  312. else:
  313. serialized.object.ref, _ = refs.make_reference(serialized.object, project)
  314. serialized.object.save()
  315. for us_attachment in data.get("attachments", []):
  316. store_attachment(project, serialized.object, us_attachment)
  317. for role_point in data.get("role_points", []):
  318. store_role_point(project, serialized.object, role_point)
  319. history_entries = data.get("history", [])
  320. for history in history_entries:
  321. store_history(project, serialized.object, history)
  322. if not history_entries:
  323. take_snapshot(serialized.object, user=serialized.object.owner)
  324. custom_attributes_values = data.get("custom_attributes_values", None)
  325. if custom_attributes_values:
  326. custom_attributes = serialized.object.project.userstorycustomattributes.all().values('id', 'name')
  327. custom_attributes_values = _use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes,
  328. custom_attributes_values)
  329. store_custom_attributes_values(serialized.object, custom_attributes_values,
  330. "user_story", serializers.UserStoryCustomAttributesValuesExportSerializer)
  331. return serialized
  332. add_errors("user_stories", serialized.errors)
  333. return None
  334. def store_issue(project, data):
  335. serialized = serializers.IssueExportSerializer(data=data, context={"project": project})
  336. if "type" not in data and project.default_issue_type:
  337. data["type"] = project.default_issue_type.name
  338. if "status" not in data and project.default_issue_status:
  339. data["status"] = project.default_issue_status.name
  340. if "priority" not in data and project.default_priority:
  341. data["priority"] = project.default_priority.name
  342. if "severity" not in data and project.default_severity:
  343. data["severity"] = project.default_severity.name
  344. if serialized.is_valid():
  345. serialized.object.project = project
  346. if serialized.object.owner is None:
  347. serialized.object.owner = serialized.object.project.owner
  348. serialized.object._importing = True
  349. serialized.object._not_notify = True
  350. serialized.save()
  351. serialized.save_watchers()
  352. if serialized.object.ref:
  353. sequence_name = refs.make_sequence_name(project)
  354. if not seq.exists(sequence_name):
  355. seq.create(sequence_name)
  356. seq.set_max(sequence_name, serialized.object.ref)
  357. else:
  358. serialized.object.ref, _ = refs.make_reference(serialized.object, project)
  359. serialized.object.save()
  360. for attachment in data.get("attachments", []):
  361. store_attachment(project, serialized.object, attachment)
  362. history_entries = data.get("history", [])
  363. for history in history_entries:
  364. store_history(project, serialized.object, history)
  365. if not history_entries:
  366. take_snapshot(serialized.object, user=serialized.object.owner)
  367. custom_attributes_values = data.get("custom_attributes_values", None)
  368. if custom_attributes_values:
  369. custom_attributes = serialized.object.project.issuecustomattributes.all().values('id', 'name')
  370. custom_attributes_values = _use_id_instead_name_as_key_in_custom_attributes_values(custom_attributes,
  371. custom_attributes_values)
  372. store_custom_attributes_values(serialized.object, custom_attributes_values,
  373. "issue", serializers.IssueCustomAttributesValuesExportSerializer)
  374. return serialized
  375. add_errors("issues", serialized.errors)
  376. return None