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())
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)
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)
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)
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
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'
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'
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)
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'
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)
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())
def do_create_transmittal( user_id, from_category_id, to_category_id, document_ids, contract_number, 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) 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.pk) 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'
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())
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())
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())
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) 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())
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)
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)
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'
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'
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'
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'