class Ticket(db.BaseDocument): title = db.StringField(max_length=200, required=True) description = db.StringField() labels = db.ListField(db.StringField()) number = db.IntField() project = db.ReferenceField('Project', reverse_delete_rule=db.CASCADE) order = db.IntField() points = db.IntField() type = db.StringField(max_length=1, choices=TICKET_TYPE) files = db.ListField(db.ReferenceField('Attachment')) assigned_to = db.ListField(db.ReferenceField('ProjectMember')) closed = db.BooleanField(default=False) related_tickets = db.ListField(db.ReferenceField('TicketDependency')) @classmethod def get_last_ticket(cls, project_pk): return cls.objects(project=project_pk).order_by('-number').first() @classmethod def get_tickets_backlog(cls, project_pk, not_tickets): tickets = Ticket.objects( Q(project=project_pk) & Q(id__nin=not_tickets) & (Q(closed=False) | Q(closed__exists=False))).order_by('order') return tickets @classmethod def get_next_order_index(cls, project_pk): return cls.objects(project=project_pk).count() @classmethod def remove_attachment(cls, tkt_id, att): cls.objects(pk=tkt_id).update_one(pull__files=att) @classmethod def remove_member(cls, tkt_id, project_member_id): cls.objects(pk=tkt_id).update_one(pull__assigned_to=project_member_id) @classmethod def remove_related_ticket(cls, tkt_id, related_tkt_id): cls.objects(pk=tkt_id).update_one(pull__related_tickets=related_tkt_id) @classmethod def search(cls, query, projects): return cls.objects((Q(title__icontains=query) | Q(description__icontains=query)) & Q(project__in=projects)) @classmethod def get_closed_tickets(cls, project_pk): return cls.objects(project=project_pk, closed=True) @classmethod def close_tickets(cls, tickets_ids): cls.objects(pk__in=tickets_ids).update(set__closed=True)
class Comment(db.BaseDocument): comment = db.StringField() who = db.ReferenceField('User', reverse_delete_rule=db.NULLIFY) ticket = db.ReferenceField('Ticket', reverse_delete_rule=db.CASCADE) @classmethod def get_by_ticket(cls, ticket_pk): return cls.objects(ticket=ticket_pk).order_by('-created_on')
class Sprint(db.BaseDocument): name = db.StringField(max_length=100, required=True) start_date = db.DateTimeField() end_date = db.DateTimeField() project = db.ReferenceField(Project, reverse_delete_rule=db.CASCADE) order = db.IntField(min_value=0) started = db.BooleanField(default=False) finalized = db.BooleanField(default=False) total_points_when_started = db.IntField() @classmethod def get_by_project(cls, project_pk): try: instances = cls.objects(project=project_pk).order_by('order') except cls.DoesNotExtis: instances = [] return instances @classmethod def get_by_project_not_finalized(cls, project_pk): try: instances = cls.objects(project=project_pk, finalized=False).order_by('order') except cls.DoesNotExtis: instances = [] return instances @classmethod def get_active_sprint(cls, project_pk): try: instance = cls.objects.get(project=project_pk, started=True, finalized=False) except cls.DoesNotExtis: instance = None return instance @classmethod def get_archived_sprints(cls, project_pk): return cls.objects(project=project_pk, finalized=True).order_by('order')
class User(db.BaseDocument): email = db.StringField(required=True) password = db.StringField(required=False) first_name = db.StringField(max_length=50) last_name = db.StringField(max_length=50) activation_token = db.StringField() active = db.BooleanField(default=True) picture = db.StringField() is_admin = db.BooleanField(default=False) meta = {'indexes': [{'fields': ['email'], 'sparse': True, 'unique': True}]} excluded_fields = ['activation_token', 'password'] @property def full_name(self): return '%s, %s' % (self.first_name, self.last_name) def set_password(self, password): utils.validate_password(password) self.password = generate_password_hash(password, 'sha1') def verify_password(self, pwd): return pwd is not None and check_password_hash(self.password, pwd) def validate(self, clean=True): err_dict = {} try: if self.password: utils.validate_password(self.password) except mongo_errors.ValidationError as ex: err_dict.update(ex.to_dict()) try: super(User, self).validate(clean=clean) except mongo_errors.ValidationError as ex: err_dict.update(ex.to_dict()) if err_dict: raise mongo_errors.ValidationError(errors=err_dict) @classmethod def is_duplicated_email(cls, email): """ Validates if the email is already in use. :param email: The email to validate. :return: Dict with {field: error}. """ if cls.objects(email=email): return True return False @classmethod def get_by_email(cls, email): """ Returns User by email if it exists. :param email: The email to filter by. :return: A user instance or None if the email is not in use. """ try: instance = cls.objects.filter(active=True, email=email).first() except (mongo_errors.ValidationError, cls.DoesNotExist): instance = None return instance @classmethod def get_by_activation_token(cls, token_activation): """ Returns User by activation token if it exists. :param token_activation: The email to filter by. :return: A user instance or None if the token_activation is not in use. """ try: instance = cls.objects(activation_token=token_activation).first() except (mongo_errors.ValidationError, cls.DoesNotExist): instance = None return instance @classmethod def get_or_create(cls, **kwargs): """ Gets or creates a User, using the provided data. An email must always be provided. :param kwargs: keyword arguments to be passed to the model constructor. :return: A tuple with the user instance and a boolean indicating whether the user was created or not. """ created = False email = kwargs.get('email') if not email: raise ValueError('No email provided') instance = cls.get_by_email(email=email) if not instance: created = True instance = cls(**kwargs) return instance, created def save(self, force_insert=False, **kwargs): doc = self.to_mongo() created = ('_id' not in doc or self._created or force_insert) if created: # Set a password on creation (even if one wasn't provided) password = self.password or utils.generate_random_password() self.set_password(password) return super(User, self).save(force_insert=force_insert, **kwargs) @classmethod def search(cls, query): return cls.objects( Q(email__istartswith=query) | Q(first_name__istartswith=query) | Q(last_name__istartswith=query))
class TicketDependency(db.BaseDocument): ticket = db.ReferenceField('Ticket') type = db.StringField(choices=DEPENDENCY_TYPE, max_length=2)
class Attachment(db.BaseDocument): name = db.StringField() size = db.IntField() type = db.StringField() data = db.StringField()
class UserActivity(db.BaseDocument): project = db.ReferenceField(Project) verb = db.StringField() author = db.ReferenceField(User) data = db.DictField() to = db.ReferenceField(User)