<commit_before>from datetime import timedelta

from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _

from adhocracy4.modules import models as modules_models

from . import content
from .validators import validate_content


class PhasesQuerySet(models.QuerySet):

    def active_phases(self):
        """Return active phases."""
        now = timezone.now()
        return self.filter(start_date__lte=now, end_date__gt=now)

    def finished_phases(self):
        """Return past phases."""
        return self.filter(end_date__lte=timezone.now())

    def past_phases(self):
        """Return past phases ordered by start date."""
        return self\
            .filter(end_date__lte=timezone.now())\
            .order_by('start_date')

    def future_phases(self):
        """
        Return future phases ordered by start date.

        Note: Phases without a start date are assumed to start in the future.
        """
        return self\
            .filter(models.Q(start_date__gt=timezone.now())
                    | models.Q(start_date=None))\
            .order_by('start_date')

    def past_and_active_phases(self):
        """Return past and active phases ordered by start date."""
        return self\
            .filter(start_date__lte=timezone.now())\
            .order_by('start_date')

    def finish_next(self, hours=24):
        """
        All phases that are active and finish within the given time.
        """
        now = timezone.now()
        last_end_date = (now + timedelta(hours=hours))
        return self.active_phases().filter(end_date__lte=last_end_date)

    def start_last(self, hours=1):
        """
        All phases that have started within the given time.
        """
        now = timezone.now()
        first_start_date = (now - timedelta(hours=hours))
        return self.filter(start_date__gt=first_start_date, start_date__lt=now)


class Phase(models.Model):
    name = models.CharField(max_length=80, verbose_name=_('Name'))
    description = models.TextField(max_length=300,
                                   verbose_name=_('Description'))
    type = models.CharField(max_length=128, validators=[validate_content])
    module = models.ForeignKey(modules_models.Module, on_delete=models.CASCADE)
    start_date = models.DateTimeField(blank=True, null=True,
                                      verbose_name=_('Start date'))
    end_date = models.DateTimeField(blank=True, null=True,
                                    verbose_name=_('End date'))
    weight = models.IntegerField(default=0)

    objects = PhasesQuerySet.as_manager()

    class Meta:
        ordering = ['weight']

    def __str__(self):
        return '{} ({})'.format(self.name, self.type)

    def content(self):
        return content[self.type]

    def clean(self):
        if self.end_date and self.start_date:
            if self.end_date < self.start_date:
                raise ValidationError({
                    'end_date': _('End date can not be smaller'
                                  'than the start date.')
                })
        super().clean()

    @cached_property
    def view(self):
        return content[self.type].view

    @cached_property
    def is_over(self):
        '''Test if phase is over.'''
        return self.end_date <= timezone.now()

    def has_feature(self, feature, model):
        return content[self.type].has_feature(feature, model)

    def is_first_of_project(self):
        """Test if this is the first phase of the project."""
        return self == self.module.project.phases.first()
<commit_msg>phases: make sure end_date exists when checking if phase is over<commit_after>from datetime import timedelta

from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _

from adhocracy4.modules import models as modules_models

from . import content
from .validators import validate_content


class PhasesQuerySet(models.QuerySet):

    def active_phases(self):
        """Return active phases."""
        now = timezone.now()
        return self.filter(start_date__lte=now, end_date__gt=now)

    def finished_phases(self):
        """Return past phases."""
        return self.filter(end_date__lte=timezone.now())

    def past_phases(self):
        """Return past phases ordered by start date."""
        return self\
            .filter(end_date__lte=timezone.now())\
            .order_by('start_date')

    def future_phases(self):
        """
        Return future phases ordered by start date.

        Note: Phases without a start date are assumed to start in the future.
        """
        return self\
            .filter(models.Q(start_date__gt=timezone.now())
                    | models.Q(start_date=None))\
            .order_by('start_date')

    def past_and_active_phases(self):
        """Return past and active phases ordered by start date."""
        return self\
            .filter(start_date__lte=timezone.now())\
            .order_by('start_date')

    def finish_next(self, hours=24):
        """
        All phases that are active and finish within the given time.
        """
        now = timezone.now()
        last_end_date = (now + timedelta(hours=hours))
        return self.active_phases().filter(end_date__lte=last_end_date)

    def start_last(self, hours=1):
        """
        All phases that have started within the given time.
        """
        now = timezone.now()
        first_start_date = (now - timedelta(hours=hours))
        return self.filter(start_date__gt=first_start_date, start_date__lt=now)


class Phase(models.Model):
    name = models.CharField(max_length=80, verbose_name=_('Name'))
    description = models.TextField(max_length=300,
                                   verbose_name=_('Description'))
    type = models.CharField(max_length=128, validators=[validate_content])
    module = models.ForeignKey(modules_models.Module, on_delete=models.CASCADE)
    start_date = models.DateTimeField(blank=True, null=True,
                                      verbose_name=_('Start date'))
    end_date = models.DateTimeField(blank=True, null=True,
                                    verbose_name=_('End date'))
    weight = models.IntegerField(default=0)

    objects = PhasesQuerySet.as_manager()

    class Meta:
        ordering = ['weight']

    def __str__(self):
        return '{} ({})'.format(self.name, self.type)

    def content(self):
        return content[self.type]

    def clean(self):
        if self.end_date and self.start_date:
            if self.end_date < self.start_date:
                raise ValidationError({
                    'end_date': _('End date can not be smaller'
                                  'than the start date.')
                })
        super().clean()

    @cached_property
    def view(self):
        return content[self.type].view

    @cached_property
    def is_over(self):
        '''Test if phase is over.'''
        if self.end_date:
            return self.end_date <= timezone.now()
        return False

    def has_feature(self, feature, model):
        return content[self.type].has_feature(feature, model)

    def is_first_of_project(self):
        """Test if this is the first phase of the project."""
        return self == self.module.project.phases.first()

{'fraction_non_alphanumeric': 0.10237154150197629, 'fraction_numerical': 0.0034255599472990776, 'mean_word_length': 2.4441923774954626, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1975521', 'n_tokens_mistral': 2180, 'n_tokens_neox': 2097, 'n_words': 524}