Exemplo n.º 1
0
def sprint_pre_delete(sender, document, *args, **kwargs):
    """
    Delete related documents when a User is deleted
    :param sender: Class
    :param document: Sprint Object
    :return: Nothing
    """
    # delete tickets assigned to sprint
    SprintTicketOrder.objects(sprint=document).delete()
    # delete ticket transition
    TicketColumnTransition.objects(sprint=document).delete()
Exemplo n.º 2
0
def ticket_pre_delete(sender, document, *args, **kwargs):
    """
    Delete related documents when a Ticket is deleted
    :param sender: Class
    :param document: Ticket Object
    :return: Nothing
    """
    # delete attachements
    for att in document.files:
        att.delete()
    # delete ticket column transition
    TicketColumnTransition.objects(ticket=document).delete()
    # delete sprint ticket order
    SprintTicketOrder.objects(ticket=document).delete()
    # delete comments
    Comment.objects(ticket=document).delete()
Exemplo n.º 3
0
    def post(self, project_pk, sprint_pk):
        """
        Update Order of tickets in sprint

        :param project_pk: Project ID
        :param sprint_pk: Sprint ID
        :return: 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)

        sprint = get_sprint_request(sprint_pk)

        SprintTicketOrder.order_items(data, sprint)
        # save activity
        save_notification(project_pk=project_pk,
                          verb='sprint_ticket_order',
                          data=data)

        return data, 200
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
def ticket_serializer(tkt):
    """
    Serialize a Ticket
    :param tkt: Ticket Instance
    :return: Ticket Composite
    """
    data = tkt.to_dict()
    data['badges'] = {
        'comments': Comment.get_by_ticket(tkt).count(),
        'files': len(tkt.files)
    }
    files = []
    for f in tkt.files:
        if f.__class__.__name__ != 'DBRef':
            files.append(f)
    data['files'] = files

    tt = TicketCT.get_latest_transition(tkt)
    if tt is not None:
        data['in_column'] = tt.column.title

    sp = SprintTicketOrder.get_active_ticket(tkt)
    if sp is not None:
        if sp.sprint.__class__.__name__ != 'DBRef':
            data['sprint'] = sp.sprint

    assignments = []
    for ass in tkt.assigned_to:
        if ass.__class__.__name__ != 'DBRef':
            val = ass.to_dict()
            val['member'] = ass.member.to_dict()
            assignments.append(val)
    data['assigned_to'] = assignments

    related = []
    for r in tkt.related_tickets:
        if r.__class__.__name__ != 'DBRef':
            rtkt = r.to_dict()
            rtkt['ticket'] = r.ticket
            related.append(rtkt)
    data['related_tickets'] = related
    return data
Exemplo n.º 6
0
    def get(self, project_pk, sprint_id):
        """
        Get tickets fro a Sprint
        :param project_pk: Project ID
        :param sprint_id: Sprint ID
        :return: List of Tickets
        """

        prj = get_project_request(project_pk)
        sprint = get_sprint_request(sprint_id)

        # first get all the columns
        ticket_list = []
        columns = Column.get_by_project(prj)

        if columns:
            ticket_transitions = TicketCT.get_transitions_in_cols(columns)
            tickets_in_cols = []
            for tt in ticket_transitions:
                tickets_in_cols.append(tt.ticket)

            # exclude from sprint
            tickets = SprintTicketOrder.list_spo(sprint, tickets_in_cols)

            for t in tickets:
                if t.ticket.__class__.__name__ != 'DBRef':
                    tkt = t.ticket_repr
                    tkt['order'] = t.order
                    tkt['badges'] = {
                        'comments': Comment.get_by_ticket(t.ticket).count(),
                        'files': len(t.ticket.files)
                    }
                    assignments = []
                    for ass in t.ticket.assigned_to:
                        if ass.__class__.__name__ != 'DBRef':
                            val = ass.to_dict()
                            val['member'] = ass.member.to_dict()
                            assignments.append(val)
                    tkt['assigned_to'] = assignments
                    ticket_list.append(tkt)
        return ticket_list, 200
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
def sprint_serializer(obj):
    """
    Serialize Sprint
    :param obj: Sprint Instance
    :return: Sprint Composite
    """
    data = obj.to_dict()
    tickets = list(SprintTicketOrder.get_active_sprint(obj.id))
    ticket_list = []
    for t in tickets:
        tkt = t.ticket.to_dict()
        tkt['order'] = t.order
        tkt['badges'] = {
            'comments': Comment.get_by_ticket(t.ticket).count(),
            'files': len(t.ticket.files)
        }
        assignments = []
        for ass in t.ticket.assigned_to:
            if ass.__class__.__name__ != 'DBRef':
                assignments.append(ass)
        tkt['assigned_to'] = assignments
        ticket_list.append(tkt)
    data["tickets"] = ticket_list
    return data
Exemplo n.º 9
0
    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
Exemplo n.º 10
0
    def get(self, project_pk, sprint_id):
        prj = get_project_request(project_pk)
        sprint = get_sprint_request(sprint_id)
        if sprint:
            # include the weekends??
            weekends = bool(request.args.get('weekends', False))
            # get tickets of the sprint
            tickets_in_sprint = SprintTicketOrder.objects(
                sprint=sprint,
                active=(sprint.started and not sprint.finalized))
            # get the points planned when it started
            planned_sprint_points = sprint.total_points_when_started

            # get the sum of points of the entire sprint
            y1_total_sprint_points = 0
            if tickets_in_sprint:
                for sto in tickets_in_sprint:
                    y1_total_sprint_points += sto.ticket_repr.get('points')

            if planned_sprint_points > y1_total_sprint_points:
                planned_sprint_points = y1_total_sprint_points

            # get done column
            col = Column.objects.get(project=sprint.project, done_column=True)

            # define the lists
            days = []
            points_remaining = []
            ideal_planned = [y1_total_sprint_points]
            ideal_real = [y1_total_sprint_points]

            # add the first date, the sprint started date
            days.append(sprint.start_date)
            # get the duration in days of the sprint
            duration = (sprint.end_date - sprint.start_date).days

            # get the dates of the sprint
            counter = 1
            while counter <= duration:
                d = sprint.start_date + timedelta(days=counter)
                if d.date() <= sprint.end_date.date():
                    if weekends:
                        if d.weekday() != 5 and d.weekday() != 6:
                            continue
                    days.append(d)
                counter += 1

            # calculate deltas for ideal lines
            delta_planned = float(y1_total_sprint_points) / float(duration)
            delta_real = float(y1_total_sprint_points) / float(duration)
            # define initial counters
            planned_counter = y1_total_sprint_points
            real_counter = y1_total_sprint_points
            starting_points = planned_sprint_points

            # get team velocity
            x2_limit_date = datetime.now()
            if datetime.now() > sprint.end_date:
                x2_limit_date = sprint.end_date
            total_burned_points = 0
            tickets_done = TicketCT.objects(column=col,
                                            sprint=sprint,
                                            when__gte=sprint.start_date,
                                            when__lte=x2_limit_date,
                                            latest_state=True)

            for td in tickets_done:
                spo_done = SprintTicketOrder.objects.get(ticket=td.ticket,
                                                         sprint=sprint)
                total_burned_points += spo_done.ticket_repr.get('points')
            # adding 1 day to include limits
            used_days = (x2_limit_date.date() - sprint.start_date.date()).days
            if used_days < 1:
                velocity = float(y1_total_sprint_points) / float(duration)
            else:
                y2_remaining_points = y1_total_sprint_points - total_burned_points
                velocity = float(y1_total_sprint_points -
                                 y2_remaining_points) / float(used_days)
            delta_real = velocity or delta_real

            eta = y1_total_sprint_points / delta_real

            # list of formatted dates
            formatted_days = []
            # tickets added o burned
            tickets = []
            # iterate each day to see the burned or added points
            for day in days:
                planned_counter -= delta_planned
                real_counter -= delta_real
                if planned_counter > 0:
                    ideal_planned.append(round(planned_counter, 2))
                else:
                    ideal_planned.append(0)
                if real_counter > 0:
                    ideal_real.append(round(real_counter, 2))
                else:
                    ideal_real.append(0)
                # format the dates
                formatted_days.append(datetime.strftime(day, '%d %a, %b %Y'))
                start_date = day if sprint.start_date < day else sprint.start_date
                end_date = (start_date +
                            timedelta(hours=23, minutes=59)).date()

                if start_date.date() <= x2_limit_date.date():

                    points_burned_for_date = 0
                    points_added = 0
                    # get tickets after started sprint
                    filters = dict(sprint=sprint,
                                   when__gt=start_date,
                                   when__lt=end_date)
                    if start_date > sprint.start_date:
                        filters.update(dict(when__gt=start_date.date()))

                    spt_list = SprintTicketOrder.objects(**filters)
                    for spt in spt_list:
                        tickets.append(u'Added: %s-%s (%s)' %
                                       (spt.ticket.project.prefix,
                                        spt.ticket.number, spt.ticket.points))
                        points_added += spt.ticket_repr.get('points')

                    # get tickets in done column
                    tct_list = TicketCT.objects(column=col,
                                                sprint=sprint,
                                                when__gte=start_date.date(),
                                                when__lt=end_date,
                                                latest_state=True)

                    for tct in tct_list:
                        spo = SprintTicketOrder.objects.get(ticket=tct.ticket,
                                                            sprint=sprint)
                        tickets.append(
                            u'Burned: %s-%s (%s)' %
                            (tct.ticket.project.prefix, tct.ticket.number,
                             spo.ticket_repr.get('points')))
                        points_burned_for_date += spo.ticket_repr.get('points')

                    if points_burned_for_date > 0:
                        starting_points -= points_burned_for_date
                    if points_added > 0:
                        starting_points += points_added

                    points_remaining.append(starting_points)

            data = {
                'points_remaining': points_remaining,
                'tickets': tickets,
                'dates': formatted_days,
                'ideal_planned': ideal_planned,
                'ideal_real': ideal_real,
                'eta': sprint.start_date + timedelta(days=eta),
                'velocity': delta_real,
                'planned_points': sprint.total_points_when_started,
                'all_tickets':
                json.loads(sprint.get_tickets_with_latest_status())
            }
            return data, 200
        return {'error': 'Bad Request'}, 400
Exemplo n.º 11
0
    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
Exemplo n.º 12
0
    def post(self, project_pk):
        """
        Move Ticket to Column or Backlog
        :param project_pk: Project ID
        :return: Ticket Transition
        """
        project = get_project_request(project_pk)

        data = request.get_json(silent=True)
        if not data:
            raise api_errors.InvalidAPIUsage(api_errors.INVALID_JSON_BODY_MSG)

        tkt = get_ticket_request(data.get('ticket'))

        result = {}
        if data.get('backlog'):
            latest_tran = TicketCT.get_latest_transition(tkt)
            if latest_tran:
                latest_tran.latest_state = False
                latest_tran.save()

            SprintTicketOrder.order_items(data.get('order'),
                                          data.get('backlog'))
            # save activity
            save_notification(project_pk=project_pk,
                              verb='ticket_transition',
                              data=data)

            result = latest_tran.to_json(), 200

        else:
            # Search already state
            col = Column.get_by_id(data.get('column'))

            if not col:
                raise api_errors.MissingResource(api_errors.INVALID_COLUMN_MSG)

            user = User.get_by_id(current_user.id)
            if not user:
                raise api_errors.MissingResource(
                    api_errors.INVALID_USER_ID_MSG)

            transition = TicketCT()
            transition.ticket = tkt
            transition.column = col
            transition.order = TicketCT.get_next_order_index(col)

            if project.project_type == 'S':
                sprint = get_sprint_request(data.get('sprint'))
                latest_tran = TicketCT.get_latest_transition(tkt, sprint)

                transition.sprint = sprint
            else:
                latest_tran = TicketCT.get_latest_transition(tkt)

            if latest_tran:
                latest_tran.latest_state = False
                latest_tran.save()

            transition.latest_state = True
            transition.who = user
            transition.save()

            if project.project_type == 'S':
                sprint = get_sprint_request(data.get('sprint'))
                TicketCT.order_items(data.get('order'),
                                     sprint=sprint,
                                     column=col)
            else:
                TicketCT.order_items(data.get('order'), column=col)

            # save activity
            save_notification(project_pk=project_pk,
                              verb='ticket_transition',
                              data=transition.to_dict())

            result = transition.to_json(), 201
        return result
Exemplo n.º 13
0
    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