def post(self, project_pk): """ Move Ticket :param project_pk: Project ID :return: """ get_project_request(project_pk) data = request.get_json(silent=True) if not data: raise api_errors.InvalidAPIUsage(api_errors.INVALID_JSON_BODY_MSG) source = data.get('source') destiny = data.get('dest') if not source or not destiny: raise api_errors.InvalidAPIUsage(payload={ 'source': api_errors.REQUIRED_MSG, 'dest': api_errors.REQUIRED_MSG }) remove_from_sprint = False sprint_id = None ticket_id = None if source.get('project_id'): sprint_id = destiny.get('sprint_id') ticket_id = source.get('ticket_id') elif source.get('sprint_id') and destiny.get('sprint_id'): sprint_id = destiny.get('sprint_id') ticket_id = source.get('ticket_id') elif source.get('sprint_id') and destiny.get('project_id'): sprint_id = source.get('sprint_id') ticket_id = source.get('ticket_id') remove_from_sprint = True sprint = get_sprint_request(sprint_id) ticket = get_ticket_request(ticket_id) if remove_from_sprint: SprintTicketOrder.inactivate_spo(sprint, ticket) Ticket.order_items(destiny.get('order')) else: SprintTicketOrder.inactivate_spo(sprint, ticket) tkt_ord_sprint = SprintTicketOrder() tkt_ord_sprint.sprint = sprint tkt_ord_sprint.ticket = ticket tkt_ord_sprint.ticket_repr = ticket.to_dict() tkt_ord_sprint.save() SprintTicketOrder.order_items(destiny.get('order'), sprint) # save activity save_notification(project_pk=project_pk, verb='ticket_movement', data=data) return data, 200
def project_member_pre_delete(sender, document, *args, **kwargs): """ Delete related documents when a Project Member is deleted :param sender: Class :param document: Project Member Object :return: Nothing """ Ticket.objects( assigned_to__contains=document ).update(pull__assigned_to=document)
def project_pre_delete(sender, document, *args, **kwargs): """ Delete related documents when a User is deleted :param sender: Class :param document: Project Object :return: Nothing """ # delete sprints Sprint.objects(project=document).delete() # delete members ProjectMember.objects(project=document).delete() # delete tickets Ticket.objects(project=document).delete() # delete columns Column.objects(project=document).delete()
def post(self, project_pk, tkt_id): """ Clone Ticket - Make a copy of a ticket :param project_pk: :param tkt_id: :return: """ prj = get_project_request(project_pk) tkt = get_ticket_request(tkt_id) new_tkt = tkt.clone() last_tkt = Ticket.get_last_ticket(prj) new_tkt.number = last_tkt.number + 1 if last_tkt else 1 new_tkt.order = Ticket.get_next_order_index(prj) new_tkt.save() return new_tkt, 200
def delete(self, project_pk, tkt_id, rtkt_id): """ Delete Related Ticket :param project_pk: Project ID :param tkt_id: Ticket ID :param rtkt_id: Related Ticket ID :return: """ get_project_request(project_pk) get_ticket_request(tkt_id) rtkt = TicketDependency.get_by_id(rtkt_id) if not rtkt: raise api_errors.MissingResource(api_errors.INVALID_TICKET_MSG) Ticket.remove_related_ticket(tkt_id, rtkt) return {}, 204
def post(self, project_pk): """ Update Order """ get_project_request(project_pk) data = request.get_json(silent=True) if not data: raise api_errors.InvalidAPIUsage(api_errors.INVALID_JSON_BODY_MSG) Ticket.order_items(data) # save activity save_notification(project_pk=project_pk, verb='backlog_order', data=data) return data, 200
def get(self, project_pk): """ Get Closed tickets :param project_pk: Project ID :return: List of tickets closed """ prj = get_project_request(project_pk) return Ticket.get_closed_tickets(prj), 200
def put(self, project_pk, tkt_id): """ Update Ticket :param project_pk: Project ID :param tkt_id: Ticket ID :return: updated Ticket Resource """ get_project_request(project_pk) tkt = get_ticket_request(tkt_id) data = request.get_json(silent=True) if not data: raise api_errors.InvalidAPIUsage(api_errors.INVALID_JSON_BODY_MSG) tkt.description = data.get('description', tkt.description) tkt.points = data.get('points', tkt.points) tkt.title = data.get('title', tkt.title) tkt.labels = data.get('labels', tkt.labels) tkt.type = data.get('type', tkt.type) tkt.closed = data.get('closed', tkt.closed) if 'related_tickets_data' in data: for tkt_rel_data in data.get('related_tickets_data'): tkt_rel = Ticket.get_by_id(tkt_rel_data.get('value')) if tkt_rel: rt = TicketDependency() rt.ticket = tkt_rel rt.type = tkt_rel_data.get('type', 'R') rt.save() tkt.related_tickets.append(rt) tkt.save() if data.get('sprint'): sprint = Sprint.get_by_id(data.get('sprint')['pk']) if sprint: spo = SprintTicketOrder.get_active_sprint_ticket(sprint, tkt) if not spo: # remove old data if this already exists spo = SprintTicketOrder(sprint=sprint, ticket=tkt) spo.ticket_repr = tkt.to_dict() spo.order = SprintTicketOrder.get_next_order_index( sprint.id) spo.save() else: spo = SprintTicketOrder.get_active_ticket(tkt) if spo: spo.ticket_repr = tkt.to_dict() spo.save() # save activity save_notification(project_pk=project_pk, verb='update_ticket', data=tkt.to_dict()) return tkt, 200
def get_ticket_request(ticket_id): """ Get Ticket from the url :param ticket_id: Ticket ID :return: Ticket Object """ tkt = Ticket.get_by_id(ticket_id) if tkt is None: raise api_errors.MissingResource(api_errors.INVALID_TICKET_MSG) return tkt
def get(self, query): """ Search Tickets that contains the query :param query: text to search :return: List of matched tickets """ projects = [] projects_query = ProjectMember.get_by_member(current_user.id) for p in projects_query: projects.append(str(p.project.pk)) return Ticket.search(query, projects), 200
def delete(self, project_pk, tkt_id, att_id): """ Delete an attachment :param project_pk: Project ID :param tkt_id: Ticket ID :param att_id: Attachment ID :return: Nothing """ get_project_request(project_pk) att = Attachment.get_by_id(att_id) tkt = get_ticket_request(tkt_id) Ticket.remove_attachment(tkt.id, att) # save activity save_notification(project_pk=project_pk, verb='delete_attachment', data=att.to_dict()) att.delete() return {}, 204
def delete(self, project_pk, tkt_id, pm_id): """ Remove member from ticket :param project_pk: Project ID :param tkt_id: Ticket ID :param member_id: ProjectMember ID :return: Nothing """ get_project_request(project_pk) tkt = get_ticket_request(tkt_id) pm = ProjectMember.get_by_id(pm_id) if not pm: raise api_errors.MissingResource( api_errors.INVALID_PROJECT_MEMBER_MSG) Ticket.remove_member(tkt, pm) # save activity save_notification(project_pk=project_pk, verb='delete_assignment', data=tkt.to_dict()) return {}, 204
def get(self, project_pk, query): """ Search Tickets by project :param project_pk: Project ID :param query: text to search :return: List of matched tickets """ prj = get_project_request(project_pk) tickets = set(Ticket.search(query, [str(prj.pk)])) results = [] for tkt in tickets: val = dict(text='%s-%s: %s' % (tkt.project.prefix, tkt.number, tkt.title), value=str(tkt.id)) results.append(val) return results, 200
def get(self, project_pk): """ Get Tickets for a Project :param project_pk: Project ID :return: List of tickets """ prj = get_project_request(project_pk) tickets = [] sprints = Sprint.get_by_project(prj) if prj.project_type == u'S': for s in sprints: spos = SprintTicketOrder.get_active_sprint(s) for spo in spos: tickets.append(spo.ticket.id) return Ticket.get_tickets_backlog(project_pk, tickets), 200
def get(self, project_pk): """ Get Tickets for backlog board :param project_pk: Project ID :return: List of tickets """ tickets = [] col_ids = [] prj = get_project_request(project_pk) column_list = Column.get_by_project(prj) for c in column_list: col_ids.append(str(c.pk)) tct_list = TicketCT.get_transitions_in_cols(col_ids) for t in tct_list: tickets.append(str(t.ticket.pk)) results = Ticket.get_tickets_backlog(prj, not_tickets=tickets) return results, 200
def post(self, project_pk): """ Create Ticket """ data = request.get_json(silent=True) if not data: raise api_errors.InvalidAPIUsage(api_errors.INVALID_JSON_BODY_MSG) project = get_project_request(project_pk) tkt = Ticket() last_tkt = Ticket.get_last_ticket(project_pk) tkt.number = last_tkt.number + 1 if last_tkt else 1 tkt.order = Ticket.get_next_order_index(project_pk) tkt.project = project tkt.description = data.get('description') tkt.points = data.get('points', 0) tkt.title = data.get('title') tkt.labels = data.get('labels') tkt.type = data.get('type', 'U') tkt.save() if data.get('sprint'): # Assign to a sprint sprint = Sprint.get_by_id(pk=data.get('sprint')['pk']) if sprint: spo = SprintTicketOrder(sprint=sprint, ticket=tkt) spo.ticket_repr = tkt.to_dict() spo.order = SprintTicketOrder.get_next_order_index(sprint.id) spo.save() # save activity save_notification(project_pk=project_pk, verb='new_ticket', data=tkt.to_dict()) return tkt, 201
def put(self, project_pk, sp_id): """ Update a Sprint :param project_pk: :param sp_id: :return: """ get_project_request(project_pk) data = request.get_json(silent=True) if not data: raise api_errors.InvalidAPIUsage(api_errors.INVALID_JSON_BODY_MSG) sp = get_sprint_request(sp_id) sp.name = data.get('name', sp.name) if data.get('for_starting'): # sum all the ticket for the initial planning value sto = SprintTicketOrder.get_active_sprint(sp) total_planned_points = 0 if sto: # get the sum of points for s in sto: total_planned_points += s.ticket.points sp.total_points_when_started = total_planned_points sp.start_date = parser.parse(data.get('start_date')) sp.end_date = parser.parse(data.get('end_date')) sp.started = True elif data.get('for_finalized'): # Mark as finalized sp.finalized = True # Get tickets in Done column finished_tickets = [] tickets_to_close_id = [] tt = TicketCT.get_transitions_for_sprint(sp) for t in tt: if t.column.done_column: finished_tickets.append(t.ticket) tickets_to_close_id.append(str(t.ticket.pk)) # Deactivate Tickets SprintTicketOrder.inactivate_list_spo(sp, finished_tickets) # Close Tickets Ticket.close_tickets(tickets_to_close_id) elif data.get('for_editing'): sp.start_date = parser.parse(data.get('start_date')) sp.end_date = parser.parse(data.get('end_date')) sp.save() # save activity save_notification(project_pk=project_pk, verb='update_sprint', data=sp.to_dict()) return sp, 200
class ProjectImport(AuthResource): """ Import Data Project """ def post(self, project_pk): """ Import cards and columns :param project_pk: Project ID :return: List of Tickets and Columns imported """ project = get_project_request(project_pk) try: body = json.loads(request.form.get('data')) except ValueError, e: raise api_errors.InvalidAPIUsage( api_errors.INVALID_JSON_BODY_MSG ) imported_file = request.files.get('file') if not imported_file: raise api_errors.InvalidAPIUsage( api_errors.REQUIRED_MSG ) data = json.loads(imported_file.stream.read().decode('utf-8'), encoding='UTF-8') import_args = body.get('data') tickets = [] last_ticket = Ticket.get_last_ticket(project_pk) starting_number = last_ticket.number if last_ticket else 1 amazon_url = 'https://trello-attachments.s3.amazonaws.com/' if import_args.get('include_cards'): for card in data.get('cards'): t = Ticket() t.title = card.get('name') t.description = card.get('desc') t.labels = [l.get('name') for l in card.get('labels')] t.closed = card.get('closed') for att in card.get('attachments'): location = att.get('url') if amazon_url in location: file_location = urllib2.urlopen(location) file_data = file_location.read() if file_data: att_file = Attachment() att_file.name = att.get('name') att_file.size = att.get('bytes') att_file.type = att.get('mimeType') att_file.data = base64.b64encode(file_data) att_file.save() t.files.append(att_file) t.project = project t.number = starting_number t.order = starting_number - 1 # by default user story t.type = 'U' t.points = 0 tickets.append(t) starting_number += 1 columns = [] if import_args.get('include_cols'): for col in data.get('lists'): if not col.get('closed'): new_col = Column() new_col.title = col.get('name') new_col.project = project columns.append(new_col) if tickets: Ticket.objects.insert(tickets, load_bulk=False) if columns: Column.objects.insert(columns, load_bulk=False) # save activity save_notification(project_pk=project_pk, verb='import', data=data) return [tickets, columns], 200