예제 #1
0
    def _import_action(self, us, action, statuses, options):
        key = make_key_from_model_object(us)
        typename = get_typename_for_model_class(UserStory)
        action_data = self._transform_action_data(us, action, statuses, options)
        if action_data is None:
            return

        change_old = action_data['change_old']
        change_new = action_data['change_new']
        hist_type = action_data['hist_type']
        comment = action_data['comment']
        user = action_data['user']

        diff = make_diff_from_dicts(change_old, change_new)
        fdiff = FrozenDiff(key, diff, {})

        entry = HistoryEntry.objects.create(
            user=user,
            project_id=us.project.id,
            key=key,
            type=hist_type,
            snapshot=None,
            diff=fdiff.diff,
            values=make_diff_values(typename, fdiff),
            comment=comment,
            comment_html=mdrender(us.project, comment),
            is_hidden=False,
            is_snapshot=False,
        )
        HistoryEntry.objects.filter(id=entry.id).update(created_at=action['date'])
        return HistoryEntry.objects.get(id=entry.id)
예제 #2
0
    def _import_history(self, project, obj, history, options):
        key = make_key_from_model_object(obj)
        typename = get_typename_for_model_class(obj.__class__)
        history_data = self._transform_history_data(project, obj, history, options)
        if history_data is None:
            return

        change_old = history_data['change_old']
        change_new = history_data['change_new']
        hist_type = history_data['hist_type']
        comment = history_data['comment']
        user = history_data['user']

        diff = make_diff_from_dicts(change_old, change_new)
        fdiff = FrozenDiff(key, diff, {})

        values = make_diff_values(typename, fdiff)
        values.update(history_data['update_values'])

        entry = HistoryEntry.objects.create(
            user=user,
            project_id=obj.project.id,
            key=key,
            type=hist_type,
            snapshot=None,
            diff=fdiff.diff,
            values=values,
            comment=comment,
            comment_html=mdrender(obj.project, comment),
            is_hidden=False,
            is_snapshot=False,
        )
        HistoryEntry.objects.filter(id=entry.id).update(created_at=history['created'])
        return HistoryEntry.objects.get(id=entry.id)
예제 #3
0
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

    assigned_users = [u.id for u in us.assigned_users.all()]
    # Due to multiple assignment migration, for new snapshots we add to
    # assigned users a list with the 'assigned to' value
    if us.assigned_to_id and not assigned_users:
        assigned_users = [us.assigned_to_id]

    snapshot = {
        "ref": us.ref,
        "owner": us.owner_id,
        "status": us.status.id if us.status else None,
        "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,
        "assigned_users": assigned_users,
        "milestone": us.milestone_id,
        "client_requirement": us.client_requirement,
        "team_requirement": us.team_requirement,
        "attachments": extract_attachments(us),
        "tags": us.tags,
        "points": points,
        "from_issue": us.generated_from_issue_id,
        "from_task": us.generated_from_task_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),
        "tribe_gig": us.tribe_gig,
        "due_date": str(us.due_date) if us.due_date else None
    }

    return snapshot
예제 #4
0
def wikipage_freezer(wiki) -> dict:
    snapshot = {
        "slug": wiki.slug,
        "owner": wiki.owner_id,
        "content": wiki.content,
        "content_html": mdrender(wiki.project, wiki.content),
        "attachments": extract_attachments(wiki),
    }

    return snapshot
예제 #5
0
def epic_freezer(epic) -> dict:
    snapshot = {
        "ref": epic.ref,
        "color": epic.color,
        "owner": epic.owner_id,
        "status": epic.status.id if epic.status else None,
        "epics_order": epic.epics_order,
        "subject": epic.subject,
        "description": epic.description,
        "description_html": mdrender(epic.project, epic.description),
        "assigned_to": epic.assigned_to_id,
        "client_requirement": epic.client_requirement,
        "team_requirement": epic.team_requirement,
        "attachments": extract_attachments(epic),
        "tags": epic.tags,
        "is_blocked": epic.is_blocked,
        "blocked_note": epic.blocked_note,
        "blocked_note_html": mdrender(epic.project, epic.blocked_note),
        "custom_attributes": extract_epic_custom_attributes(epic)
    }

    return snapshot
예제 #6
0
def issue_freezer(issue) -> dict:
    snapshot = {
        "ref": issue.ref,
        "owner": issue.owner_id,
        "status": issue.status.id if issue.status else None,
        "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,
        "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),
        "due_date": str(issue.due_date) if issue.due_date else None
    }

    return snapshot
예제 #7
0
def task_freezer(task) -> dict:
    snapshot = {
        "ref": task.ref,
        "owner": task.owner_id,
        "status": task.status.id if task.status else None,
        "milestone": task.milestone_id,
        "subject": task.subject,
        "description": task.description,
        "description_html": mdrender(task.project, task.description),
        "assigned_to": task.assigned_to_id,
        "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),
        "due_date": str(task.due_date) if task.due_date else None
    }

    return snapshot
예제 #8
0
    def edit_comment(self, request, pk):
        obj = self.get_object()
        history_entry_id = request.QUERY_PARAMS.get('id', None)
        history_entry = services.get_history_queryset_by_model_instance(obj).filter(id=history_entry_id).first()
        if history_entry is None:
            return response.NotFound()

        obj = services.get_instance_from_key(history_entry.key)
        comment = request.DATA.get("comment", None)

        self.check_permissions(request, 'edit_comment', history_entry)

        if history_entry is None:
            return response.NotFound()

        if comment is None:
            return response.BadRequest({"error": _("comment is required")})

        if history_entry.delete_comment_date or history_entry.delete_comment_user:
            return response.BadRequest({"error": _("deleted comments can't be edited")})

        # comment_versions can be None if there are no historic versions of the comment
        comment_versions = history_entry.comment_versions or []
        comment_versions.append({
            "date": history_entry.created_at,
            "comment": history_entry.comment,
            "comment_html": history_entry.comment_html,
            "user": {
                "id": request.user.pk,
            }
        })

        new_mentions = self._get_new_mentions(obj, history_entry.comment, comment)

        history_entry.edit_comment_date = timezone.now()
        history_entry.comment = comment
        history_entry.comment_html = mdrender(obj.project, comment)
        history_entry.comment_versions = comment_versions
        history_entry.save()

        if new_mentions:
            signal_mentions.send(sender=self.__class__,
                                 user=self.request.user,
                                 obj=obj,
                                 mentions=new_mentions)

        return response.Ok()
예제 #9
0
    def render(self, request, **kwargs):
        content = request.DATA.get("content", None)
        project_id = request.DATA.get("project_id", None)

        if not content:
            raise exc.WrongArguments(
                {"content": _("'content' parameter is mandatory")})

        if not project_id:
            raise exc.WrongArguments(
                {"project_id": _("'project_id' parameter is mandatory")})

        project = get_object_or_404(Project, pk=project_id)

        self.check_permissions(request, "render", project)

        data = mdrender(project, content)
        return response.Ok({"data": data})
예제 #10
0
    def _transform_history_data(self, project, obj, history, options):
        users_bindings = options.get('users_bindings', {})

        user = {"pk": None, "name": history.get('author', {}).get('displayName', None)}
        tuesmon_user = users_bindings.get(history.get('author', {}).get('key', None), None)
        if tuesmon_user:
            user = {"pk": tuesmon_user.id, "name": tuesmon_user.get_full_name()}

        result = {
            "change_old": {},
            "change_new": {},
            "update_values": {},
            "hist_type": HistoryType.change,
            "comment": "",
            "user": user
        }
        custom_fields_by_names = {f["history_name"]: f for f in self.custom_fields}
        has_data = False
        for history_item in history['items']:
            if history_item['field'] == "Attachment":
                result['change_old']["attachments"] = []
                for att in obj.cummulative_attachments:
                    result['change_old']["attachments"].append({
                        "id": 0,
                        "filename": att
                    })

                if history_item['from'] is not None:
                    try:
                        idx = obj.cummulative_attachments.index(history_item['fromString'])
                        obj.cummulative_attachments.pop(idx)
                    except ValueError:
                        print("ERROR: Removing attachment that doesn't exist in the history ({})".format(history_item['fromString']))
                if history_item['to'] is not None:
                    obj.cummulative_attachments.append(history_item['toString'])

                result['change_new']["attachments"] = []
                for att in obj.cummulative_attachments:
                    result['change_new']["attachments"].append({
                        "id": 0,
                        "filename": att
                    })
                has_data = True
            elif history_item['field'] == "description":
                result['change_old']["description"] = history_item['fromString']
                result['change_new']["description"] = history_item['toString']
                result['change_old']["description_html"] = mdrender(obj.project, history_item['fromString'] or "")
                result['change_new']["description_html"] = mdrender(obj.project, history_item['toString'] or "")
                has_data = True
            elif history_item['field'] == "Epic Link":
                pass
            elif history_item['field'] == "Workflow":
                pass
            elif history_item['field'] == "Link":
                pass
            elif history_item['field'] == "labels":
                result['change_old']["tags"] = history_item['fromString'].split()
                result['change_new']["tags"] = history_item['toString'].split()
                has_data = True
            elif history_item['field'] == "Rank":
                pass
            elif history_item['field'] == "RemoteIssueLink":
                pass
            elif history_item['field'] == "Sprint":
                old_milestone = None
                if history_item['fromString']:
                    try:
                        old_milestone = obj.project.milestones.get(name=history_item['fromString']).id
                    except Milestone.DoesNotExist:
                        old_milestone = -1

                new_milestone = None
                if history_item['toString']:
                    try:
                        new_milestone = obj.project.milestones.get(name=history_item['toString']).id
                    except Milestone.DoesNotExist:
                        new_milestone = -2

                result['change_old']["milestone"] = old_milestone
                result['change_new']["milestone"] = new_milestone

                if old_milestone == -1 or new_milestone == -2:
                    result['update_values']["milestone"] = {}

                if old_milestone == -1:
                    result['update_values']["milestone"]["-1"] = history_item['fromString']
                if new_milestone == -2:
                    result['update_values']["milestone"]["-2"] = history_item['toString']
                has_data = True
            elif history_item['field'] == "status":
                if isinstance(obj, Task):
                    try:
                        old_status = obj.project.task_statuses.get(name=history_item['fromString']).id
                    except Exception:
                        old_status = -1
                    try:
                        new_status = obj.project.task_statuses.get(name=history_item['toString']).id
                    except Exception:
                        new_status = -2
                elif isinstance(obj, UserStory):
                    try:
                        old_status = obj.project.us_statuses.get(name=history_item['fromString']).id
                    except Exception:
                        old_status = -1
                    try:
                        new_status = obj.project.us_statuses.get(name=history_item['toString']).id
                    except Exception:
                        new_status = -2
                elif isinstance(obj, Issue):
                    try:
                        old_status = obj.project.issue_statuses.get(name=history_item['fromString']).id
                    except Exception:
                        old_status = -1
                    try:
                        new_status = obj.project.us_statuses.get(name=history_item['toString']).id
                    except Exception:
                        new_status = -2
                elif isinstance(obj, Epic):
                    try:
                        old_status = obj.project.epic_statuses.get(name=history_item['fromString']).id
                    except Exception:
                        old_status = -1
                    try:
                        new_status = obj.project.epic_statuses.get(name=history_item['toString']).id
                    except Exception:
                        new_status = -2

                if old_status == -1 or new_status == -2:
                    result['update_values']["status"] = {}

                if old_status == -1:
                    result['update_values']["status"]["-1"] = history_item['fromString']
                if new_status == -2:
                    result['update_values']["status"]["-2"] = history_item['toString']

                result['change_old']["status"] = old_status
                result['change_new']["status"] = new_status
                has_data = True
            elif history_item['field'] == "Story Points":
                old_points = None
                if history_item['fromString']:
                    estimation = float(history_item['fromString'])
                    (old_points, _) = Points.objects.get_or_create(
                        project=project,
                        value=estimation,
                        defaults={
                            "name": str(estimation),
                            "order": estimation,
                        }
                    )
                    old_points = old_points.id
                new_points = None
                if history_item['toString']:
                    estimation = float(history_item['toString'])
                    (new_points, _) = Points.objects.get_or_create(
                        project=project,
                        value=estimation,
                        defaults={
                            "name": str(estimation),
                            "order": estimation,
                        }
                    )
                    new_points = new_points.id
                result['change_old']["points"] = {project.roles.get(slug="main").id: old_points}
                result['change_new']["points"] = {project.roles.get(slug="main").id: new_points}
                has_data = True
            elif history_item['field'] == "summary":
                result['change_old']["subject"] = history_item['fromString']
                result['change_new']["subject"] = history_item['toString']
                has_data = True
            elif history_item['field'] == "Epic Color":
                if isinstance(obj, Epic):
                    result['change_old']["color"] = EPIC_COLORS.get(history_item['fromString'], None)
                    result['change_new']["color"] = EPIC_COLORS.get(history_item['toString'], None)
                    Epic.objects.filter(id=obj.id).update(
                        color=EPIC_COLORS.get(history_item['toString'], "#999999")
                    )
                    has_data = True
            elif history_item['field'] == "assignee":
                old_assigned_to = None
                if history_item['from'] is not None:
                    old_assigned_to = users_bindings.get(history_item['from'], -1)
                    if old_assigned_to != -1:
                        old_assigned_to = old_assigned_to.id

                new_assigned_to = None
                if history_item['to'] is not None:
                    new_assigned_to = users_bindings.get(history_item['to'], -2)
                    if new_assigned_to != -2:
                        new_assigned_to = new_assigned_to.id

                result['change_old']["assigned_to"] = old_assigned_to
                result['change_new']["assigned_to"] = new_assigned_to

                if old_assigned_to == -1 or new_assigned_to == -2:
                    result['update_values']["users"] = {}

                if old_assigned_to == -1:
                    result['update_values']["users"]["-1"] = history_item['fromString']
                if new_assigned_to == -2:
                    result['update_values']["users"]["-2"] = history_item['toString']
                has_data = True
            elif history_item['field'] in custom_fields_by_names:
                custom_field = custom_fields_by_names[history_item['field']]
                if isinstance(obj, Task):
                    field_obj = obj.project.taskcustomattributes.get(name=custom_field['tuesmon_field_name'])
                elif isinstance(obj, UserStory):
                    field_obj = obj.project.userstorycustomattributes.get(name=custom_field['tuesmon_field_name'])
                elif isinstance(obj, Issue):
                    field_obj = obj.project.issuecustomattributes.get(name=custom_field['tuesmon_field_name'])
                elif isinstance(obj, Epic):
                    field_obj = obj.project.epiccustomattributes.get(name=custom_field['tuesmon_field_name'])

                result['change_old']["custom_attributes"] = [{
                    "name": custom_field['tuesmon_field_name'],
                    "value": history_item['fromString'],
                    "id": field_obj.id
                }]
                result['change_new']["custom_attributes"] = [{
                    "name": custom_field['tuesmon_field_name'],
                    "value": history_item['toString'],
                    "id": field_obj.id
                }]
                has_data = True

        if not has_data:
            return None

        return result
예제 #11
0
 def get_description_html(self, obj):
     return mdrender(obj.project, obj.description)
예제 #12
0
 def get_blocked_note_html(self, obj):
     return mdrender(obj.project, obj.blocked_note)
예제 #13
0
def take_snapshot(obj: object, *, comment: str="", user=None,
                  delete: bool=False):
    """
    Given any model instance with registred content type,
    create new history entry of "change" type.

    This raises exception in case of object wasn't
    previously freezed.
    """

    key = make_key_from_model_object(obj)
    with advisory_lock("history-"+key):
        typename = get_typename_for_model_class(obj.__class__)

        new_fobj = freeze_model_instance(obj)
        old_fobj, need_real_snapshot = get_last_snapshot_for_key(key)

        # migrate diff to latest schema
        if old_fobj:
            old_fobj = migrate_to_last_version(typename, old_fobj)

        entry_model = apps.get_model("history", "HistoryEntry")
        user_id = None if user is None else user.id
        user_name = "" if user is None else user.get_full_name()

        # Determine history type
        if delete:
            entry_type = HistoryType.delete
            need_real_snapshot = True
        elif new_fobj and not old_fobj:
            entry_type = HistoryType.create
        elif new_fobj and old_fobj:
            entry_type = HistoryType.change
        else:
            raise RuntimeError("Unexpected condition")

        excluded_fields = get_excluded_fields(typename)

        fdiff = make_diff(old_fobj, new_fobj, excluded_fields)

        # If diff and comment are empty, do
        # not create empty history entry
        if (not fdiff.diff and
                not comment and old_fobj is not None and
                entry_type != HistoryType.delete):
            return None

        fvals = make_diff_values(typename, fdiff)

        if len(comment) > 0:
            is_hidden = False
        else:
            is_hidden = is_hidden_snapshot(fdiff)

        kwargs = {
            "user": {"pk": user_id, "name": user_name},
            "project_id": getattr(obj, 'project_id', getattr(obj, 'id', None)),
            "key": key,
            "type": entry_type,
            "snapshot": fdiff.snapshot if need_real_snapshot else None,
            "diff": fdiff.diff,
            "values": fvals,
            "comment": comment,
            "comment_html": mdrender(obj.project, comment),
            "is_hidden": is_hidden,
            "is_snapshot": need_real_snapshot,
        }

        return entry_model.objects.create(**kwargs)
예제 #14
0
 def get_html(self, obj):
     return mdrender(obj.project, obj.content)
예제 #15
0
 def field_from_native(self, data, files, field_name, into):
     super().field_from_native(data, files, field_name, into)
     into["comment_html"] = mdrender(self.context['project'],
                                     data.get("comment", ""))
예제 #16
0
    def _transform_action_data(self, us, action, statuses, options):
        users_bindings = options.get('users_bindings', {})
        due_date_field = us.project.userstorycustomattributes.first()

        ignored_actions = ["addAttachmentToCard", "addMemberToCard",
                           "deleteAttachmentFromCard", "deleteCard",
                           "removeMemberFromCard"]

        if action['type'] in ignored_actions:
            return None

        user = {"pk": None, "name": action.get('memberCreator', {}).get('fullName', None)}
        tuesmon_user = users_bindings.get(action.get('memberCreator', {}).get('id', None), None)
        if tuesmon_user:
            user = {"pk": tuesmon_user.id, "name": tuesmon_user.get_full_name()}

        result = {
            "change_old": {},
            "change_new": {},
            "hist_type": HistoryType.change,
            "comment": "",
            "user": user
        }

        if action['type'] == "commentCard":
            result['comment'] = str(action['data']['text'])
        elif action['type'] == "convertToCardFromCheckItem":
            UserStory.objects.filter(id=us.id, created_date__gt=action['date']).update(
                created_date=action['date'],
                owner=users_bindings.get(action["idMemberCreator"], self._user)
            )
            result['hist_type'] = HistoryType.create
        elif action['type'] == "copyCommentCard":
            UserStory.objects.filter(id=us.id, created_date__gt=action['date']).update(
                created_date=action['date'],
                owner=users_bindings.get(action["idMemberCreator"], self._user)
            )
            result['hist_type'] = HistoryType.create
        elif action['type'] == "createCard":
            UserStory.objects.filter(id=us.id, created_date__gt=action['date']).update(
                created_date=action['date'],
                owner=users_bindings.get(action["idMemberCreator"], self._user)
            )
            result['hist_type'] = HistoryType.create
        elif action['type'] == "updateCard":
            if 'desc' in action['data']['old']:
                result['change_old']["description"] = str(action['data']['old'].get('desc', ''))
                result['change_new']["description"] = str(action['data']['card'].get('desc', ''))
                result['change_old']["description_html"] = mdrender(us.project, str(action['data']['old'].get('desc', '')))
                result['change_new']["description_html"] = mdrender(us.project, str(action['data']['card'].get('desc', '')))
            if 'idList' in action['data']['old']:
                old_status_name = statuses[action['data']['old']['idList']]['name']
                result['change_old']["status"] = us.project.us_statuses.get(name=old_status_name).id
                new_status_name = statuses[action['data']['card']['idList']]['name']
                result['change_new']["status"] = us.project.us_statuses.get(name=new_status_name).id
            if 'name' in action['data']['old']:
                result['change_old']["subject"] = action['data']['old']['name']
                result['change_new']["subject"] = action['data']['card']['name']
            if 'due' in action['data']['old']:
                result['change_old']["custom_attributes"] = [{
                    "name": "Due",
                    "value": action['data']['old']['due'],
                    "id": due_date_field.id
                }]
                result['change_new']["custom_attributes"] = [{
                    "name": "Due",
                    "value": action['data']['card']['due'],
                    "id": due_date_field.id
                }]

            if result['change_old'] == {}:
                return None
        return result