123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- # Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
- # Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
- # Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Affero General Public License as
- # published by the Free Software Foundation, either version 3 of the
- # License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Affero General Public License for more details.
- #
- # You should have received a copy of the GNU Affero General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- from contextlib import suppress
- from functools import partial
- from django.apps import apps
- from django.contrib.contenttypes.models import ContentType
- from django.core.exceptions import ObjectDoesNotExist
- from easy_thumbnails.files import get_thumbnailer
- from easy_thumbnails.exceptions import InvalidImageFormatError
- from taiga.base.utils.urls import get_absolute_url
- from taiga.base.utils.iterators import as_tuple
- from taiga.base.utils.iterators import as_dict
- from taiga.mdrender.service import render as mdrender
- import os
- ####################
- # Values
- ####################
- @as_dict
- def _get_generic_values(ids:tuple, *, typename=None, attr:str="name") -> tuple:
- app_label, model_name = typename.split(".", 1)
- content_type = ContentType.objects.get(app_label=app_label, model=model_name)
- model_cls = content_type.model_class()
- ids = filter(lambda x: x is not None, ids)
- qs = model_cls.objects.filter(pk__in=ids)
- for instance in qs:
- yield str(instance.pk), getattr(instance, attr)
- @as_dict
- def _get_users_values(ids:set) -> dict:
- user_model = apps.get_model("users", "User")
- ids = filter(lambda x: x is not None, ids)
- qs = user_model.objects.filter(pk__in=tuple(ids))
- for user in qs:
- yield str(user.pk), user.get_full_name()
- @as_dict
- def _get_user_story_values(ids:set) -> dict:
- userstory_model = apps.get_model("userstories", "UserStory")
- ids = filter(lambda x: x is not None, ids)
- qs = userstory_model.objects.filter(pk__in=tuple(ids))
- for userstory in qs:
- yield str(userstory.pk), "#{} {}".format(userstory.ref, userstory.subject)
- _get_us_status_values = partial(_get_generic_values, typename="projects.userstorystatus")
- _get_task_status_values = partial(_get_generic_values, typename="projects.taskstatus")
- _get_issue_status_values = partial(_get_generic_values, typename="projects.issuestatus")
- _get_issue_type_values = partial(_get_generic_values, typename="projects.issuetype")
- _get_role_values = partial(_get_generic_values, typename="users.role")
- _get_points_values = partial(_get_generic_values, typename="projects.points")
- _get_priority_values = partial(_get_generic_values, typename="projects.priority")
- _get_severity_values = partial(_get_generic_values, typename="projects.severity")
- _get_milestone_values = partial(_get_generic_values, typename="milestones.milestone")
- def _common_users_values(diff):
- """
- Groups common values resolver logic of userstories,
- issues and tasks.
- """
- values = {}
- users = set()
- if "owner" in diff:
- users.update(diff["owner"])
- if "watchers" in diff:
- for ids in diff["watchers"]:
- if not ids:
- continue
- users.update(ids)
- if "assigned_to" in diff:
- users.update(diff["assigned_to"])
- if users:
- values["users"] = _get_users_values(users)
- return values
- def project_values(diff):
- values = _common_users_values(diff)
- return values
- def milestone_values(diff):
- values = _common_users_values(diff)
- return values
- def userstory_values(diff):
- values = _common_users_values(diff)
- if "status" in diff:
- values["status"] = _get_us_status_values(diff["status"])
- if "milestone" in diff:
- values["milestone"] = _get_milestone_values(diff["milestone"])
- if "points" in diff:
- points, roles = set(), set()
- for pointsentry in diff["points"]:
- if pointsentry is None:
- continue
- for role_id, point_id in pointsentry.items():
- points.add(point_id)
- roles.add(role_id)
- values["roles"] = _get_role_values(roles)
- values["points"] = _get_points_values(points)
- return values
- def issue_values(diff):
- values = _common_users_values(diff)
- if "status" in diff:
- values["status"] = _get_issue_status_values(diff["status"])
- if "milestone" in diff:
- values["milestone"] = _get_milestone_values(diff["milestone"])
- if "priority" in diff:
- values["priority"] = _get_priority_values(diff["priority"])
- if "severity" in diff:
- values["severity"] = _get_severity_values(diff["severity"])
- if "type" in diff:
- values["type"] = _get_issue_type_values(diff["type"])
- return values
- def task_values(diff):
- values = _common_users_values(diff)
- if "status" in diff:
- values["status"] = _get_task_status_values(diff["status"])
- if "milestone" in diff:
- values["milestone"] = _get_milestone_values(diff["milestone"])
- if "user_story" in diff:
- values["user_story"] = _get_user_story_values(diff["user_story"])
- return values
- def wikipage_values(diff):
- values = _common_users_values(diff)
- return values
- ####################
- # Freezes
- ####################
- def _generic_extract(obj:object, fields:list, default=None) -> dict:
- result = {}
- for fieldname in fields:
- result[fieldname] = getattr(obj, fieldname, default)
- return result
- @as_tuple
- def extract_attachments(obj) -> list:
- for attach in obj.attachments.all():
- try:
- thumb_url = get_thumbnailer(attach.attached_file)['timeline-image'].url
- thumb_url = get_absolute_url(thumb_url)
- except InvalidImageFormatError as e:
- thumb_url = None
- yield {"id": attach.id,
- "filename": os.path.basename(attach.attached_file.name),
- "url": attach.attached_file.url,
- "thumb_url": thumb_url,
- "is_deprecated": attach.is_deprecated,
- "description": attach.description,
- "order": attach.order}
- @as_tuple
- def extract_user_story_custom_attributes(obj) -> list:
- with suppress(ObjectDoesNotExist):
- custom_attributes_values = obj.custom_attributes_values.attributes_values
- for attr in obj.project.userstorycustomattributes.all():
- with suppress(KeyError):
- value = custom_attributes_values[str(attr.id)]
- yield {"id": attr.id,
- "name": attr.name,
- "value": value}
- @as_tuple
- def extract_task_custom_attributes(obj) -> list:
- with suppress(ObjectDoesNotExist):
- custom_attributes_values = obj.custom_attributes_values.attributes_values
- for attr in obj.project.taskcustomattributes.all():
- with suppress(KeyError):
- value = custom_attributes_values[str(attr.id)]
- yield {"id": attr.id,
- "name": attr.name,
- "value": value}
- @as_tuple
- def extract_issue_custom_attributes(obj) -> list:
- with suppress(ObjectDoesNotExist):
- custom_attributes_values = obj.custom_attributes_values.attributes_values
- for attr in obj.project.issuecustomattributes.all():
- with suppress(KeyError):
- value = custom_attributes_values[str(attr.id)]
- yield {"id": attr.id,
- "name": attr.name,
- "value": value}
- def project_freezer(project) -> dict:
- fields = ("name",
- "slug",
- "created_at",
- "owner_id",
- "public",
- "total_milestones",
- "total_story_points",
- "tags",
- "is_backlog_activated",
- "is_kanban_activated",
- "is_wiki_activated",
- "is_issues_activated")
- return _generic_extract(project, fields)
- def milestone_freezer(milestone) -> dict:
- snapshot = {
- "name": milestone.name,
- "slug": milestone.slug,
- "owner": milestone.owner_id,
- "estimated_start": milestone.estimated_start,
- "estimated_finish": milestone.estimated_finish,
- "closed": milestone.closed,
- "disponibility": milestone.disponibility
- }
- return snapshot
- def userstory_freezer(us) -> dict:
- rp_cls = apps.get_model("userstories", "RolePoints")
- rpqsd = rp_cls.objects.filter(user_story=us)
- points = {}
- for rp in rpqsd:
- points[str(rp.role_id)] = rp.points_id
- snapshot = {
- "ref": us.ref,
- "owner": us.owner_id,
- "status": us.status_id,
- "is_closed": us.is_closed,
- "finish_date": str(us.finish_date),
- "backlog_order": us.backlog_order,
- "sprint_order": us.sprint_order,
- "kanban_order": us.kanban_order,
- "subject": us.subject,
- "description": us.description,
- "description_html": mdrender(us.project, us.description),
- "assigned_to": us.assigned_to_id,
- "milestone": us.milestone_id,
- "client_requirement": us.client_requirement,
- "team_requirement": us.team_requirement,
- "watchers": [x.id for x in us.get_watchers()],
- "attachments": extract_attachments(us),
- "tags": us.tags,
- "points": points,
- "from_issue": us.generated_from_issue_id,
- "is_blocked": us.is_blocked,
- "blocked_note": us.blocked_note,
- "blocked_note_html": mdrender(us.project, us.blocked_note),
- "custom_attributes": extract_user_story_custom_attributes(us),
- }
- return snapshot
- def issue_freezer(issue) -> dict:
- snapshot = {
- "ref": issue.ref,
- "owner": issue.owner_id,
- "status": issue.status_id,
- "priority": issue.priority_id,
- "severity": issue.severity_id,
- "type": issue.type_id,
- "milestone": issue.milestone_id,
- "subject": issue.subject,
- "description": issue.description,
- "description_html": mdrender(issue.project, issue.description),
- "assigned_to": issue.assigned_to_id,
- "watchers": [x.pk for x in issue.get_watchers()],
- "attachments": extract_attachments(issue),
- "tags": issue.tags,
- "is_blocked": issue.is_blocked,
- "blocked_note": issue.blocked_note,
- "blocked_note_html": mdrender(issue.project, issue.blocked_note),
- "custom_attributes": extract_issue_custom_attributes(issue),
- }
- return snapshot
- def task_freezer(task) -> dict:
- snapshot = {
- "ref": task.ref,
- "owner": task.owner_id,
- "status": task.status_id,
- "milestone": task.milestone_id,
- "subject": task.subject,
- "description": task.description,
- "description_html": mdrender(task.project, task.description),
- "assigned_to": task.assigned_to_id,
- "watchers": [x.pk for x in task.get_watchers()],
- "attachments": extract_attachments(task),
- "taskboard_order": task.taskboard_order,
- "us_order": task.us_order,
- "tags": task.tags,
- "user_story": task.user_story_id,
- "is_iocaine": task.is_iocaine,
- "is_blocked": task.is_blocked,
- "blocked_note": task.blocked_note,
- "blocked_note_html": mdrender(task.project, task.blocked_note),
- "custom_attributes": extract_task_custom_attributes(task),
- }
- return snapshot
- def wikipage_freezer(wiki) -> dict:
- snapshot = {
- "slug": wiki.slug,
- "owner": wiki.owner_id,
- "content": wiki.content,
- "content_html": mdrender(wiki.project, wiki.content),
- "watchers": [x.pk for x in wiki.get_watchers()],
- "attachments": extract_attachments(wiki),
- }
- return snapshot
|