Exemple #1
0
 def test_choices_from_list(self):
     choices = get_choices_from_list(self.values_list.index)
     self.assertCountEqual(choices, [
         ('test1', 'test1 - Test 1'),
         ('test2', 'test2 - Test 2'),
         ('test3', 'test3 - Test 3'),
     ])
Exemple #2
0
def update_schedule_section(document, metadata, revision, rewrite_schedule=True, **kwargs):
    """Update the "actual" dates of the schedule section.

    The "<status> Actual Date" fields must be updated automatically depending
    on the different revisions' statuses and created date.

    See #172

    """
    if not rewrite_schedule:
        return

    if not isinstance(metadata, ScheduleMixin):
        return

    list_index = revision._meta.get_field('status').list_index
    statuses = get_choices_from_list(list_index)
    for status, _ in statuses:
        field = 'status_{}_actual_date'.format(status).lower()
        setattr(metadata, field, None)

    revisions = document.get_all_revisions()
    for rev in revisions.reverse():
        if rev.status:
            field = 'status_{}_actual_date'.format(rev.status).lower()
            if not getattr(metadata, field, None):
                setattr(metadata, field, rev.created_on)

    metadata.save()
Exemple #3
0
def update_schedule_section(document,
                            metadata,
                            revision,
                            rewrite_schedule=True,
                            **kwargs):
    """Update the "actual" dates of the schedule section.

    The "<status> Actual Date" fields must be updated automatically depending
    on the different revisions' statuses and created date.

    See #172

    """
    if not rewrite_schedule:
        return

    if not isinstance(metadata, ScheduleMixin):
        return

    list_index = revision._meta.get_field('status').list_index
    statuses = get_choices_from_list(list_index)
    for status, _ in statuses:
        field = 'status_{}_actual_date'.format(status).lower()
        setattr(metadata, field, None)

    revisions = document.get_all_revisions()
    for rev in revisions.reverse():
        if rev.status:
            field = 'status_{}_actual_date'.format(rev.status).lower()
            if not getattr(metadata, field, None):
                setattr(metadata, field, rev.created_on)

    metadata.save()
Exemple #4
0
 def test_choices_from_list(self):
     choices = get_choices_from_list(self.values_list.index)
     self.assertItemsEqual(choices, [
         (u'test1', u'test1 - Test 1'),
         (u'test2', u'test2 - Test 2'),
         (u'test3', u'test3 - Test 3'),
     ])
Exemple #5
0
 def test_choices_from_list(self):
     choices = get_choices_from_list(self.values_list.index)
     self.assertItemsEqual(choices, [
         (u'test1', u'test1 - Test 1'),
         (u'test2', u'test2 - Test 2'),
         (u'test3', u'test3 - Test 3'),
     ])
Exemple #6
0
 def test_choices_from_list(self):
     choices = get_choices_from_list(self.values_list.index)
     self.assertCountEqual(choices, [
         ('test1', 'test1 - Test 1'),
         ('test2', 'test2 - Test 2'),
         ('test3', 'test3 - Test 3'),
     ])
Exemple #7
0
    def fetch_documents_behind_schedule(self, category):
        """Fetch documents behind schedule.

        Documents behind schedule are documents that were meant to reach a
        certain status at a certain date (forecast), and that date has
        already passed whereas the document still has not reached that status.

        It technical terms, it means:

          - in it's "schedule" table, the document has one line with a
            `forecast` value that is < today and an `actual` value that is null.
          - that lines concerns a status that is higher than the current status
            in the document workflow.
        """

        # Get the list of existing statuses for this category's document class
        Metadata = category.document_class()
        Revision = Metadata.get_revision_class()
        list_index = Revision._meta.get_field('status').list_index
        statuses = [
            status.lower() for status, _ in get_choices_from_list(list_index)
        ]
        today = timezone.now().date()

        # Create a first coarse filter to get all documents that MAY be
        # behind schedule.
        # Get all documents with any X status with a past forecast date AND
        # not actual date.
        #
        # However, that does not mean that this document is behind schedule,
        # because statuses can be skipped. E.g a document with a forecast date
        # for status A that goes directly to the next status B will have an
        # empty value for the `status_A_actual_date` but is still not
        # behind schedule.
        #
        # The reason we don't filter everything in a single query is because it
        # would make the said query ridiculously complex.
        conditions = []
        for status in statuses:
            forecast_field = 'status_{}_forecast_date'.format(status)
            actual_field = 'status_{}_actual_date'.format(status)

            # Let's check that the actual schedule fields corresponding to
            # this status exists
            try:
                Metadata._meta.get_field(forecast_field)
                Metadata._meta.get_field(actual_field)
            except FieldDoesNotExist:
                continue

            forecast_condition = '{}__lt'.format(forecast_field)
            actual_condition = '{}__isnull'.format(actual_field)
            conditions.append(
                Q(**{forecast_condition: today}) & Q(**{actual_condition: True}))

        coarse_filter = reduce(operator.or_, conditions)
        documents = Metadata.objects \
            .filter(document__category=category) \
            .filter(coarse_filter) \
            .select_related('document', 'latest_revision')

        def is_behind_schedule(document):
            """Tells if a single document is actually behind schedule.

            Check for a "behind schedule" condition, but only for statuses
            that the document has not reached yet.
            """

            current_status = document.status.lower()
            # statuses are sorted by chronological order
            current_status_index = statuses.index(current_status)
            for status_index, status in enumerate(statuses):
                if status_index <= current_status_index:
                    # The document has already passed this status, ignore
                    # this schedule line.
                    continue

                forecast_field = 'status_{}_forecast_date'.format(status)
                actual_field = 'status_{}_actual_date'.format(status)

                if getattr(document, forecast_field, None) is None:
                    continue

                forecast_date = getattr(document, forecast_field)
                actual_date = getattr(document, actual_field)
                if forecast_date < today and actual_date is None:
                    return True

            return False

        # Here, we filter the queryset to remove false positives.
        behind_schedule_documents = filter(is_behind_schedule, documents)
        return list(behind_schedule_documents)