예제 #1
0
파일: models.py 프로젝트: krukas/Trionyx
class AuditLogEntry(models.BaseModel):
    """Auditlog model"""

    ACTION_ADDED = 10
    ACTION_CHANGED = 20
    ACTION_DELETED = 30

    action_choices = [
        (ACTION_ADDED, _('Added')),
        (ACTION_CHANGED, _('Changed')),
        (ACTION_DELETED, _('Deleted')),
    ]

    content_type = models.ForeignKey('contenttypes.ContentType',
                                     models.CASCADE,
                                     related_name='+')
    object_id = models.BigIntegerField(blank=True, null=True)
    content_object = fields.GenericForeignKey('content_type', 'object_id')
    object_verbose_name = models.TextField(default='', blank=True)

    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             models.SET_NULL,
                             blank=True,
                             null=True,
                             related_name='+')
    action = models.IntegerField(choices=action_choices)
    changes = models.JSONField()

    class Meta:
        """Model meta description"""

        indexes = [
            models.Index(fields=['content_type', 'object_id']),
        ]
예제 #2
0
파일: models.py 프로젝트: krukas/Trionyx
class LogEntry(models.Model):
    """Log entry event"""

    log = models.ForeignKey(Log, models.CASCADE, related_name='entries')
    log_time = models.DateTimeField(_('Log time'))

    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             models.SET_NULL,
                             null=True,
                             blank=True,
                             verbose_name=_('User'))
    path = models.TextField(_('Path'), default='')
    user_agent = models.TextField(_('User agent'), default='')

    class Meta:
        """Model meta description"""

        verbose_name = _('Log entry')
        verbose_name_plural = _('Log entries')
예제 #3
0
class Comment(models.BaseModel):
    item = models.ForeignKey(Item,
                             related_name='comments',
                             on_delete=models.CASCADE)
    comment = models.TextField(default='')

    def generate_verbose_name(self):
        comment = strip_tags(self.comment)
        if len(comment) > 20:
            return f"{comment:.20}..."
        return comment
예제 #4
0
class WorkLog(models.BaseModel):
    item = models.ForeignKey(Item,
                             related_name='worklogs',
                             on_delete=models.CASCADE)

    date = models.DateField()
    worked = models.FloatField()
    billed = models.FloatField(
        null=True,
        blank=True,
        help_text=
        'On empty this wil be auto filled based on estimate and remaining billed hours, when there is no estimate billed will always be same as worked'
    )

    description = models.TextField(default='', null=True, blank=True)

    def save(self, *args, **kwargs):
        result = self.item.worklogs.exclude(id=self.id).aggregate(
            total_worked=models.Sum('worked'),
            total_billed=models.Sum('billed'),
        )
        self.item.total_worked = (float(result['total_worked'])
                                  if result['total_worked'] else 0.0) + float(
                                      self.worked)
        total_billed = float(
            result['total_billed']) if result['total_billed'] else 0.0

        if self.item.non_billable:
            self.billed = 0
            self.item.total_billed = 0
        elif not self.billed and not self.item.estimate:
            self.billed = self.worked
        elif not self.billed:
            available = float(self.item.estimate or 0.0) - total_billed
            if available > 0:
                self.billed = available if self.worked > available else float(
                    self.worked)
            else:
                self.billed = 0

        self.item.total_billed = total_billed + float(self.billed)

        if not self.description:
            self.description = f'Working on item {self.item.code}'

        self.item.save()
        super().save(*args, **kwargs)

    def generate_verbose_name(self):
        description = strip_tags(self.description)
        if len(description) > 20:
            return f"{description:.20}..."
        return description
예제 #5
0
파일: models.py 프로젝트: krukas/Trionyx
class UserAttribute(models.Model):
    """User attribute to store system values for user"""

    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             models.CASCADE,
                             related_name='attributes')
    code = models.CharField(max_length=128, null=False)
    value = models.JSONField()

    objects = UserAttributeManager()

    class Meta:
        """Model meta description"""

        unique_together = ('user', 'code')

    def __str__(self):
        """User Attribute representation"""
        return self.code
예제 #6
0
class Item(models.BaseModel):
    TYPE_FEATURE = 10
    TYPE_ENHANCEMENT = 20
    TYPE_TASK = 30
    TYPE_BUG = 40
    TYPE_QUESTION = 50

    TYPE_CHOICES = (
        (TYPE_FEATURE, _('Feature')),
        (TYPE_ENHANCEMENT, _('Enhancement')),
        (TYPE_TASK, _('Task')),
        (TYPE_BUG, _('Bug')),
        (TYPE_QUESTION, _('Question')),
    )

    PRIORITY_HIGHEST = 50
    PRIORITY_HIGH = 40
    PRIORITY_MEDIUM = 30
    PRIORITY_LOW = 20
    PRIORITY_LOWEST = 10

    PRIORITY_CHOICES = (
        (PRIORITY_HIGHEST, _('Highest')),
        (PRIORITY_HIGH, _('High')),
        (PRIORITY_MEDIUM, _('Medium')),
        (PRIORITY_LOW, _('Low')),
        (PRIORITY_LOWEST, _('Lowest')),
    )

    project = models.ForeignKey(Project,
                                related_name='items',
                                on_delete=models.CASCADE)
    item_type = models.IntegerField(choices=TYPE_CHOICES,
                                    default=TYPE_FEATURE,
                                    blank=True)
    priority = models.IntegerField(choices=PRIORITY_CHOICES,
                                   default=PRIORITY_MEDIUM,
                                   blank=True)

    code = models.CharField(max_length=32)
    name = models.CharField(max_length=256)
    description = models.TextField(default='', null=True, blank=True)
    completed_on = models.DateField(default=None, null=True, blank=True)

    estimate = models.FloatField(null=True, blank=True)
    non_billable = models.BooleanField(default=False, blank=True)

    # Item stats
    total_worked = models.FloatField(default=0.0, blank=True)
    total_billed = models.FloatField(default=0.0, blank=True)

    @property
    def estimate_used(self):
        return 0.0

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

        if not self.code and self.project:
            with CacheLock('set-item-code', self.project_id):
                self.project.refresh_from_db()  # get latest value
                self.project.item_increment_id += 1
                self.code = f"{self.project.code}-{self.project.item_increment_id}"
                self.save(update_fields=['code'])
                self.project.save(update_fields=['item_increment_id'])

    @classmethod
    def get_type_icon(cls, item_type):
        icon_mapping = {
            cls.TYPE_FEATURE: 'fa fa-star',
            cls.TYPE_ENHANCEMENT: 'fa fa-bolt',
            cls.TYPE_TASK: 'fa fa-check',
            cls.TYPE_BUG: 'fa fa-bug',
            cls.TYPE_QUESTION: 'fa fa-question-circle',
        }

        badge_mapping = {
            cls.TYPE_FEATURE: 'badge-feature',
            cls.TYPE_ENHANCEMENT: 'badge-enhancement',
            cls.TYPE_TASK: 'badge-task',
            cls.TYPE_BUG: 'badge-bug',
            cls.TYPE_QUESTION: 'badge-question',
        }

        return f"<span class='badge badge-icon {badge_mapping[item_type]}'><i class='{icon_mapping[item_type]}'></i></span>"

    @classmethod
    def get_priority_icon(cls, priority):
        icon = 'fa fa-long-arrow-up'
        if priority <= cls.PRIORITY_LOW:
            icon = 'fa fa-long-arrow-down'

        class_mapping = {
            cls.PRIORITY_HIGHEST: 'priority-icon-highest',
            cls.PRIORITY_HIGH: 'priority-icon-high',
            cls.PRIORITY_MEDIUM: 'priority-icon-medium',
            cls.PRIORITY_LOW: 'priority-icon-low',
            cls.PRIORITY_LOWEST: 'priority-icon-lowest',
        }

        return f"<i class='{class_mapping[priority]} {icon}'></i>"
예제 #7
0
class Project(models.BaseModel):
    STATUS_DRAFT = 10
    STATUS_ACTIVE = 20
    STATUS_ON_HOLD = 30
    STATUS_COMPLETED = 40
    STATUS_CANCELED = 99

    STATUS_CHOICES = [
        (STATUS_DRAFT, _('Draft')),
        (STATUS_ACTIVE, _('Active')),
        (STATUS_ON_HOLD, _('On hold')),
        (STATUS_COMPLETED, _('Completed')),
        (STATUS_CANCELED, _('Canceled')),
    ]

    TYPE_FIXED = 10
    TYPE_HOURLY_BASED = 20

    TYPE_CHOICES = [
        (TYPE_FIXED, _('Fixed')),
        (TYPE_HOURLY_BASED, _('Hourly based')),
    ]

    # Connect Project to other Model
    for_object_type = models.ForeignKey(
        'contenttypes.ContentType',
        models.SET_NULL,
        blank=True,
        null=True,
        verbose_name=_('Object type'),
    )
    for_object_id = models.BigIntegerField(_('Object ID'),
                                           blank=True,
                                           null=True)
    for_object = GenericForeignKey('for_object_type', 'for_object_id')

    name = models.CharField(max_length=256)
    code = models.CharField(max_length=10, unique=True)
    status = models.IntegerField(choices=STATUS_CHOICES, default=STATUS_DRAFT)
    project_type = models.IntegerField(choices=TYPE_CHOICES,
                                       default=TYPE_FIXED)
    description = models.TextField(default='', null=True, blank=True)

    deadline = models.DateField(default=None, null=True, blank=True)
    started_on = models.DateField(default=None, null=True, blank=True)
    completed_on = models.DateField(default=None, null=True, blank=True)

    fixed_price = models.PriceField(null=True, blank=True)
    project_hourly_rate = models.PriceField(null=True, blank=True)

    item_increment_id = models.IntegerField(default=0)

    # Project stats
    open_items = models.IntegerField(default=0)
    completed_items = models.IntegerField(default=0)

    total_items_estimate = models.FloatField(default=0.0)
    total_worked = models.FloatField(default=0.0)
    total_billed = models.FloatField(default=0.0)

    @property
    def hourly_rate(self):
        return float(self.project_hourly_rate if self.
                     project_hourly_rate else app_settings.HOURLY_RATE)

    @property
    def estimate_used(self):
        return 0.0

    def save(self, *args, **kwargs):
        self.code = str(self.code).upper()
        super().save(*args, **kwargs)
예제 #8
0
파일: models.py 프로젝트: krukas/Trionyx
class Task(models.BaseModel):
    """Task model"""

    SCHEDULED = 10
    QUEUE = 20
    LOCKED = 30
    RUNNING = 40
    COMPLETED = 50
    FAILED = 99

    STATUS_CHOICES = (
        (SCHEDULED, _('Scheduled')),
        (QUEUE, _('Queue')),
        (LOCKED, _('Locked')),
        (RUNNING, _('Running')),
        (COMPLETED, _('Completed')),
        (FAILED, _('Failed')),
    )

    celery_task_id = models.CharField(max_length=64, unique=True)
    status = models.IntegerField(_('Status'),
                                 choices=STATUS_CHOICES,
                                 default=QUEUE)
    identifier = models.CharField(_('Identifier'),
                                  max_length=255,
                                  default='not_set')
    description = models.TextField(_('Description'), default='')

    progress = models.PositiveIntegerField(_('Progress'), default=0)
    progress_output = models.JSONField(_('Progress output'), default=list)

    scheduled_at = models.DateTimeField(_('Scheduled at'),
                                        blank=True,
                                        null=True)
    started_at = models.DateTimeField(_('started at'), blank=True, null=True)
    execution_time = models.IntegerField(_('Execution time'), default=0)
    result = models.TextField(_('Result'), blank=True, default='')

    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             models.SET_NULL,
                             blank=True,
                             null=True,
                             verbose_name=_('Started by'))
    object_type = models.ForeignKey('contenttypes.ContentType',
                                    models.SET_NULL,
                                    blank=True,
                                    null=True,
                                    related_name='+',
                                    verbose_name=_('Object type'))
    object_id = models.BigIntegerField(_('Object ID'), blank=True, null=True)
    object = fields.GenericForeignKey('object_type', 'object_id')
    object_verbose_name = models.TextField(_('Object name'),
                                           blank=True,
                                           default='')

    class Meta:
        """Model meta description"""

        verbose_name = _('Task')
        verbose_name_plural = _('Tasks')

    def cancel_celery_task(self, kill=False):
        """
        Make sure we cancel the task (if in queue/scheduled).
        :param: kill Also kill the task if it's running, defaults to False.
        """
        celery_control = Control(current_app)
        celery_control.revoke(task_id=self.celery_task_id, terminate=kill)