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)
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)
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
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
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
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
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
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()
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})
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
def get_description_html(self, obj): return mdrender(obj.project, obj.description)
def get_blocked_note_html(self, obj): return mdrender(obj.project, obj.blocked_note)
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)
def get_html(self, obj): return mdrender(obj.project, obj.content)
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", ""))
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