Example #1
0
    def form_valid(self, document_form, revision_form):
        """Saves both the document and it's revision."""
        doc, metadata, revision = save_document_forms(
            document_form,
            revision_form,
            self.category,
            created_by=self.request.user)

        message_text = '''You created the document
                       <a href="%(url)s">%(key)s (%(title)s)</a>'''
        message_data = {
            'url': doc.get_absolute_url(),
            'key': doc.document_key,
            'title': doc.title
        }
        notify(self.request.user, _(message_text) % message_data)

        activity_log.send(
            verb='created',
            target=None,
            action_object=doc,
            sender=None,
            actor=self.request.user)

        return HttpResponseRedirect(self.get_success_url())
Example #2
0
def process_export(export_id, user_pk=None):
    from exports.models import Export
    export = Export.objects.select_related().get(id=export_id)

    # Cleanup oldest export when there are too many
    owner = export.owner
    oldest_exports_qs = Export.objects \
        .filter(owner=owner) \
        .order_by('-created_on') \
        .all()
    oldest_exports = list(oldest_exports_qs)
    if len(oldest_exports) > settings.EXPORTS_TO_KEEP:
        oldest_export_index = settings.EXPORTS_TO_KEEP - 1
        oldest_export = oldest_exports[oldest_export_index]
        Export.objects \
            .filter(owner=owner) \
            .filter(created_on__lt=oldest_export.created_on) \
            .delete()
    user = User.objects.get(pk=user_pk)
    export.status = 'processing'
    export.save()
    export.write_file()
    export.status = 'done'
    export.save()
    activity_log.send(verb=Activity.VERB_CREATED,
                      action_object_str=export.get_pretty_filename(),
                      sender=None,
                      actor=user)
Example #3
0
    def delete(self, request, *args, **kwargs):
        all_revisions = list(self.object.get_all_revisions())

        if len(all_revisions) <= 1:
            return HttpResponseForbidden('Cannot delete a single latest revision')

        latest_revision = all_revisions[0]
        previous_revision = all_revisions[1]
        latest_revision_str = str(latest_revision)

        self.object.latest_revision = previous_revision
        self.object.save()

        self.object.document.current_revision = previous_revision.revision
        self.object.document.current_revision_date = previous_revision.revision_date
        self.object.document.updated_on = timezone.now()
        self.object.document.save()
        latest_revision.delete()
        activity_log.send(verb=Activity.VERB_DELETED,
                          action_object_str=latest_revision_str,
                          target=self.object.document,
                          sender=self.__class__,
                          actor=self.request.user)

        success_url = self.get_success_url()
        return HttpResponseRedirect(success_url)
Example #4
0
 def form_valid(self, document_form, revision_form):
     response = super(DocumentEdit, self).form_valid(document_form,
                                                     revision_form)
     activity_log.send(verb=Activity.VERB_EDITED,
                       action_object=self.revision,
                       target=self.object.document,
                       sender=None,
                       actor=self.request.user)
     return response
Example #5
0
def batch_cancel_reviews(user_id, category_id, contenttype_id, document_ids):
    contenttype = ContentType.objects.get_for_id(contenttype_id)
    document_class = contenttype.model_class()

    docs = document_class.objects \
        .select_related() \
        .filter(document__category_id=category_id) \
        .filter(document_id__in=document_ids)

    ok = []
    nok = []
    counter = float(1)
    nb_reviews = docs.count()

    for doc in docs:

        # Update progress counter
        progress = counter / nb_reviews * 100
        current_task.update_state(
            state='PROGRESS',
            meta={'progress': progress})

        try:
            if not doc.latest_revision.is_under_review():
                raise RuntimeError()

            doc.latest_revision.cancel_review()
            user = User.objects.get(pk=user_id)
            activity_log.send(verb=Activity.VERB_CANCELLED_REVIEW,
                              target=doc.latest_revision,
                              sender=batch_cancel_reviews,
                              actor=user)
            ok.append(doc)
        except:  # noqa
            nok.append(doc)

        counter += 1

    if len(ok) > 0:
        ok_message = ugettext('You canceled the review for the following documents:')
        ok_list = '</li><li>'.join('<a href="%s">%s</a>' % (doc.get_absolute_url(), doc) for doc in ok)
        notify(user_id, '{} <ul><li>{}</li></ul>'.format(
            ok_message,
            ok_list
        ))

    if len(nok) > 0:
        nok_message = ugettext("We failed to cancel the review for the following documents:")
        nok_list = '</li><li>'.join('<a href="%s">%s</a>' % (doc.get_absolute_url(), doc) for doc in nok)
        notify(user_id, '{} <ul><li>{}</li></ul>'.format(
            nok_message,
            nok_list
        ))

    return 'done'
Example #6
0
    def delete(self, request, *args, **kwargs):
        """Delete the document and associated data.

        We need to delete the top level document object. Thus, metadata and
        revisions will also be deleted.

        """
        document = self.object.document
        document_str = str(document)
        success_url = self.get_success_url()
        document.delete()
        activity_log.send(verb=Activity.VERB_DELETED,
                          target=None,
                          action_object_str=document_str,
                          sender=None,
                          actor=self.request.user)

        return HttpResponseRedirect(success_url)
Example #7
0
def do_create_transmittal(
        user_id, from_category_id, to_category_id, document_ids,
        contract_number, purpose_of_issue, recipients_ids):

    # Display a small amount of progression
    # so the user won't get impatient
    current_task.update_state(
        state='PROGRESS',
        meta={'progress': 10})

    from_category = Category.objects.get(pk=from_category_id)
    to_category = Category.objects.get(pk=to_category_id)
    recipients = Entity.objects.filter(pk__in=recipients_ids)
    documents = Document.objects \
        .select_related() \
        .filter(id__in=document_ids)
    revisions = []
    for doc in documents:
        revisions.append(doc.get_latest_revision())
    try:
        for recipient in recipients:
            doc, _, _ = create_transmittal(
                from_category,
                to_category,
                revisions,
                contract_number,
                recipient,
                purpose_of_issue=purpose_of_issue)
            msg = '''You successfully created transmittal
                     <a href="{}">{}</a>'''.format(doc.get_absolute_url(), doc)
            notify(user_id, msg)

            user = User.objects.get(pk=user_id)
            activity_log.send(verb=Activity.VERB_CREATED,
                              action_object=doc,
                              sender=None,
                              actor=user)

    except TransmittalError as e:
        msg = '''We failed to create a transmittal for the
                 following reason: "{}".'''.format(e)
        notify(user_id, msg)

    return 'done'
Example #8
0
    def post(self, request, *args, **kwargs):
        self.metadata = self.get_object()
        revision = self.metadata.latest_revision
        document = self.metadata.document

        if revision.can_be_reviewed:
            revision.start_review()

            # If a non empty message body was submitted...
            body = self.request.POST.get('body', None)
            if body:
                Note.objects.create(
                    author=self.request.user,
                    document=document,
                    revision=revision.revision,
                    body=body)

            message_text = '''You started the review on revision %(rev)s of
                           the document <a href="%(url)s">%(key)s (%(title)s)</a>'''
            message_data = {
                'rev': revision.name,
                'url': document.get_absolute_url(),
                'key': document.document_key,
                'title': document.title
            }
            notify(request.user, _(message_text) % message_data)
            activity_log.send(verb=Activity.VERB_STARTED_REVIEW,
                              action_object=revision,
                              sender=None,
                              actor=self.request.user)
        else:
            message_text = '''The review on revision %(rev)s of the document
                           <a href="%(url)s">%(key)s (%(title)s)</a>
                           cannot be started'''
            message_data = {
                'rev': revision.name,
                'url': document.get_absolute_url(),
                'key': document.document_key,
                'title': document.title
            }
            notify(request.user, _(message_text) % message_data)

        return HttpResponseRedirect(self.get_redirect_url())
Example #9
0
    def post_review(self, form):
        """Update the Review object with posted data.

        Also, updates the document if any review step is finished.

        """
        comments_file = form.cleaned_data.get('comments', None)
        return_code = form.cleaned_data.get('return_code', None)

        # Update the review
        self.object.post_review(comments_file, return_code=return_code)
        if return_code:
            self.revision.return_code = return_code

        verb = None
        # If every reviewer has posted comments, close the reviewers step
        if self.object.role == 'reviewer':
            qs = Review.objects \
                .filter(document=self.document) \
                .filter(revision=self.revision.revision) \
                .filter(role='reviewer') \
                .exclude(closed_on=None)
            if qs.count() == self.revision.reviewers.count():
                self.revision.end_reviewers_step(save=False)
                verb = Activity.VERB_CLOSED_REVIEWER_STEP

        # If leader, end leader step
        elif self.object.role == 'leader':
            self.revision.end_leader_step(save=False)
            verb = Activity.VERB_CLOSED_LEADER_STEP

        # If approver, end approver step
        elif self.object.role == 'approver':
            self.revision.end_review(save=False)
            verb = Activity.VERB_CLOSED_APPROVER_STEP

        self.revision.save(update_document=True)

        if verb:
            activity_log.send(verb=verb,
                              target=self.revision,
                              sender=do_batch_import,
                              actor=self.request.user)
Example #10
0
    def form_valid(self, document_form, revision_form):
        """Saves both the document and it's revision."""
        document, self.object, self.revision = save_document_forms(
            document_form, revision_form, self.category)

        message_text = '''You created revision %(rev)s for document
                       <a href="%(url)s">%(key)s (%(title)s)</a>'''
        message_data = {
            'rev': self.revision.name,
            'url': self.object.get_absolute_url(),
            'key': self.object.document_key,
            'title': self.object.title
        }
        notify(self.request.user, _(message_text) % message_data)

        activity_log.send(verb=Activity.VERB_CREATED,
                          target=self.revision,
                          sender=None,
                          actor=self.request.user)

        return HttpResponseRedirect(self.get_success_url())
Example #11
0
    def post(self, request, *args, **kwargs):
        self.metadata = self.get_object()
        revision = self.metadata.latest_revision
        document = self.metadata.document

        if revision.is_under_review():
            revision.cancel_review()
            message_text = '''You cancelled the review on revision %(rev)s of
                           the document <a href="%(url)s">%(key)s (%(title)s)</a>'''
            message_data = {
                'rev': revision.name,
                'url': document.get_absolute_url(),
                'key': document.document_key,
                'title': document.title
            }
            notify(request.user, _(message_text) % message_data)
            activity_log.send(verb=Activity.VERB_CANCELLED_REVIEW,
                              target=revision,
                              sender=None,
                              actor=self.request.user)
        return HttpResponseRedirect(self.get_redirect_url())
Example #12
0
def do_batch_import(user_id, category_id, contenttype_id, document_ids,
                    remark=None):

    # Fetch document list
    contenttype = ContentType.objects.get_for_id(contenttype_id)
    document_class = contenttype.model_class()
    docs = document_class.objects \
        .select_related() \
        .filter(document__category_id=category_id) \
        .filter(document_id__in=document_ids)

    ok = []
    nok = []
    count = 0
    total_docs = docs.count()

    # We compute the progress as the proportion of documents for which the
    # review has started.
    # However, after all documents are treated, there is still the
    # index + refresh step which takes quite some time. So we artificially
    # increase the document count as a hackish way to never display a 100%
    # progress bar as long as the task is not truly finished.
    total_docs += 30

    pre_batch_review.send(sender=do_batch_import)

    # Try to start the review for every listed document
    for doc in docs:
        try:
            if not doc.latest_revision.can_be_reviewed:
                raise RuntimeError()

            doc.latest_revision.start_review()
            user = User.objects.get(pk=user_id)
            activity_log.send(verb=Activity.VERB_STARTED_REVIEW,
                              target=doc.latest_revision,
                              sender=do_batch_import,
                              actor=user)
            # In case of batch review start with a remark,
            # the same remark is added for every review.
            # Note: using "bulk_create" to create all the discussion
            # at the end of the task would be much more efficient.
            # However, by doing so, individual `post_save` signals would not
            # be fired. Since the request is expected to take some time anyway,
            # we will let this as is for now.
            if remark:
                Note.objects.create(
                    author_id=user_id,
                    document_id=doc.document.id,
                    revision=doc.latest_revision.revision,
                    body=remark)

            batch_item_indexed.send(
                sender=do_batch_import,
                document_type=doc.document.document_type(),
                document_id=doc.id,
                json=doc.latest_revision.to_json())
            ok.append(doc)
        except:  # noqa
            nok.append(doc)

        # Update the task progression bar
        count += 1
        progress = float(count) / total_docs * 100
        current_task.update_state(
            state='PROGRESS',
            meta={'progress': progress})

    post_batch_review.send(sender=do_batch_import, user_id=user_id)

    # Send success and failure notifications
    if len(ok) > 0:
        ok_message = ugettext('The review started for the following documents:')
        ok_list = '</li><li>'.join('<a href="%s">%s</a>' % (doc.get_absolute_url(), doc) for doc in ok)
        notify(user_id, '{} <ul><li>{}</li></ul>'.format(
            ok_message,
            ok_list
        ))

    if len(nok) > 0:
        nok_message = ugettext("We failed to start the review for the following documents:")
        nok_list = '</li><li>'.join('<a href="%s">%s</a>' % (doc.get_absolute_url(), doc) for doc in nok)
        notify(user_id, '{} <ul><li>{}</li></ul>'.format(
            nok_message,
            nok_list
        ))

    return 'done'
Example #13
0
def batch_close_reviews(user_id, review_ids):
    """Close several reviews at once.

    Only reviewers can do this.

    """
    logger.info('Closing several reviews at once: {}'.format(
        ', '.join(review_ids)))

    ok = []
    nok = []
    nb_reviews = len(review_ids)
    reviews = Review.objects \
        .filter(reviewer_id=user_id) \
        .filter(role=Review.ROLES.reviewer) \
        .filter(status=Review.STATUSES.progress) \
        .filter(id__in=review_ids) \
        .select_related()

    counter = float(1)
    comments = None
    for review in reviews:

        # Update progress counter
        progress = counter / nb_reviews * 100
        current_task.update_state(
            state='PROGRESS',
            meta={'progress': progress})

        try:
            with transaction.atomic():
                # Post an empty review
                logger.info('Closing review {}'.format(review.id))
                review.post_review(comments, save=True)

                # Check if the review step has ended
                # TODO This is highly inefficient. Maybe one day
                # find another way to do it?
                document = review.document
                latest_revision = document.get_latest_revision()
                waiting_reviews = Review.objects \
                    .filter(document=document) \
                    .filter(revision=review.revision) \
                    .filter(role='reviewer') \
                    .exclude(closed_on=None)
                if waiting_reviews.count() == latest_revision.reviewers.count():
                    logger.info('Closing reviewers step')
                    latest_revision.end_reviewers_step(save=False)
                    user = User.objects.get(pk=user_id)
                    activity_log.send(verb=Activity.VERB_CLOSED_REVIEWER_STEP,
                                      target=latest_revision,
                                      sender=do_batch_import,
                                      actor=user)
            ok.append(review.document)
        except:  # noqa
            nok.append(review.document)
            pass

        counter += 1

    if len(ok) > 0:
        ok_message = ugettext('You closed the review for the following documents:')
        ok_list = '</li><li>'.join('<a href="%s">%s</a>' % (doc.get_absolute_url(), doc) for doc in ok)
        notify(user_id, '{} <ul><li>{}</li></ul>'.format(
            ok_message,
            ok_list
        ))

    if len(nok) > 0:
        nok_message = ugettext("We failed to close the review for the following documents:")
        nok_list = '</li><li>'.join('<a href="%s">%s</a>' % (doc.get_absolute_url(), doc) for doc in nok)
        notify(user_id, '{} <ul><li>{}</li></ul>'.format(
            nok_message,
            nok_list
        ))

    return 'done'
Example #14
0
    def form_valid(self, form):
        """Process the submitted file and form.

        Multiple cases:
          - A reviewer/leader/approver is submitting a review, withe a comment file or not
          - The leader is closing the reviewer step
          - The approver is closing the reviewer step
          - The approver is closing the leader step
          - The approver is sending the review back to leader step

        """
        user = self.request.user
        self.check_permission(user, self.revision, self.object)

        # A review was posted, with or without file
        if 'review' in self.request.POST:

            # Can the user really comment?
            can_comment = self.object.status not in ('pending',)
            if not can_comment:
                return HttpResponseForbidden()

            activity_log.send(
                verb=Activity.VERB_REVIEWED,
                target=self.revision,
                sender=None,
                actor=self.request.user)

            self.post_review(form)

            comments_file = form.cleaned_data.get('comments', None)
            if comments_file:
                message_text = '''You reviewed the document
                               <a href="%(url)s">%(key)s (%(title)s)</a>
                               in revision %(rev)s with comments.'''
            else:
                message_text = '''You reviewed the document
                               <a href="%(url)s">%(key)s (%(title)s)</a>
                               in revision %(rev)s without comments.'''

            message_data = {
                'rev': self.revision.name,
                'url': self.document.get_absolute_url(),
                'key': self.document.document_key,
                'title': self.document.title
            }
            notify(user, _(message_text) % message_data)

        verb = None
        if 'close_reviewers_step' in self.request.POST and user in (
                self.revision.leader, self.revision.approver):
            self.revision.end_reviewers_step()
            verb = Activity.VERB_CLOSED_REVIEWER_STEP

        if 'close_leader_step' in self.request.POST and user == self.revision.approver:
            self.revision.end_leader_step()
            verb = Activity.VERB_CLOSED_LEADER_STEP

        if 'back_to_leader_step' in self.request.POST and user == self.revision.approver:
            self.revision.send_back_to_leader_step()
            body = self.request.POST.get('body', None)
            verb = Activity.VERB_SENT_BACK_TO_LEADER_STEP

            if body:
                Note.objects.create(
                    author=user,
                    document=self.document,
                    revision=self.revision,
                    body=body)

        if verb:
            activity_log.send(verb=verb,
                              target=self.revision,
                              sender=None,
                              actor=self.request.user)

        url = self.get_success_url()
        return HttpResponseRedirect(url)