From 83d7101cf7aa90380f46105b1b054e180c9f60d0 Mon Sep 17 00:00:00 2001 From: Matthew McKenzie Date: Fri, 29 Nov 2024 22:03:13 +0000 Subject: [PATCH] Make it a Celery task --- settings/common.py | 3 ++ workbaskets/tasks.py | 70 +++++++++++++++++++++++++++++++++++++++++ workbaskets/views/ui.py | 60 +++-------------------------------- 3 files changed, 78 insertions(+), 55 deletions(-) diff --git a/settings/common.py b/settings/common.py index 51ef14097..01fbd39a8 100644 --- a/settings/common.py +++ b/settings/common.py @@ -647,6 +647,9 @@ "workbaskets.tasks.check_workbasket": { "queue": "rule-check", }, + "workbaskets.tasks.call_end_measures": { + "queue": "standard", + }, "workbaskets.tasks.transition": { "queue": "standard", }, diff --git a/workbaskets/tasks.py b/workbaskets/tasks.py index 9482aee00..8a0dad962 100644 --- a/workbaskets/tasks.py +++ b/workbaskets/tasks.py @@ -1,12 +1,21 @@ +from datetime import date + from celery import group from celery import shared_task from celery.utils.log import get_task_logger +from django.db.models import F from django.db.transaction import atomic from checks.tasks import check_transaction from checks.tasks import check_transaction_sync +from commodities.models.orm import GoodsNomenclature from common.celery import app +from common.models.transactions import Transaction +from common.util import TaricDateRange +from common.validators import UpdateType +from measures.models.tracked_models import Measure from workbaskets.models import WorkBasket +from workbaskets.validators import WorkflowStatus # Celery logger adds the task id and status and outputs via the worker. logger = get_task_logger(__name__) @@ -64,3 +73,64 @@ def call_check_workbasket_sync(self, workbasket_id: int): workbasket: WorkBasket = WorkBasket.objects.get(pk=workbasket_id) workbasket.delete_checks() check_workbasket_sync(workbasket) + + +def promote_measure_to_top(promoted_measure, workbasket_transactions): + """Set the transaction order of `promoted_measure` to be first in the + workbasket, demoting the transactions that came before it.""" + + top_transaction = workbasket_transactions.first() + + if ( + not promoted_measure + or not top_transaction + or promoted_measure == top_transaction + ): + return + + current_position = promoted_measure.order + top_position = top_transaction.order + workbasket_transactions.filter(order__lt=current_position).update( + order=F("order") + 1, + ) + promoted_measure.order = top_position + promoted_measure.save(update_fields=["order"]) + + +def end_measures(measures, workbasket): + """Iterate through measures on commodities, end-date those which have + already began and delete those which have not yet started.""" + for measure in measures: + workbasket_transactions = Transaction.objects.filter( + workbasket=workbasket, + workbasket__status=WorkflowStatus.EDITING, + ).order_by("order") + + commodity = GoodsNomenclature.objects.all().get( + pk=measure.goods_nomenclature_id, + transaction__workbasket=workbasket, + ) + if measure.valid_between.lower > commodity.valid_between.upper: + continue + if measure.valid_between.lower > date.today(): + new_measure_version = measure.new_version( + workbasket=workbasket, + update_type=UpdateType.DELETE, + ) + else: + new_measure_version = measure.new_version( + workbasket=workbasket, + update_type=UpdateType.UPDATE, + valid_between=TaricDateRange( + measure.valid_between.lower, + commodity.valid_between.upper, + ), + ) + promote_measure_to_top(new_measure_version.transaction, workbasket_transactions) + + +@app.task +def call_end_measures(measure_pks, workbasket_pk): + workbasket = WorkBasket.objects.all().get(pk=workbasket_pk) + measures = Measure.objects.all().filter(pk__in=measure_pks) + end_measures(measures, workbasket) diff --git a/workbaskets/views/ui.py b/workbaskets/views/ui.py index 70e61ab99..40808cc0b 100644 --- a/workbaskets/views/ui.py +++ b/workbaskets/views/ui.py @@ -44,9 +44,7 @@ from common.inspect_tap_tasks import TAPTasks from common.models import Transaction from common.models.transactions import TransactionPartition -from common.util import TaricDateRange from common.util import format_date_string -from common.validators import UpdateType from common.views import SortingMixin from common.views import WithPaginationListMixin from common.views import WithPaginationListView @@ -75,6 +73,7 @@ from workbaskets.models import WorkBasket from workbaskets.session_store import SessionStore from workbaskets.tasks import call_check_workbasket_sync +from workbaskets.tasks import call_end_measures from workbaskets.validators import WorkflowStatus from workbaskets.views.decorators import require_current_workbasket from workbaskets.views.mixins import WithCurrentWorkBasket @@ -1794,7 +1793,7 @@ class AutoEndDateMeasures(SortingMixin, WithPaginationListMixin, ListView): "goods_nomenclature": "goods_nomenclature__item_id", } - @cached_property + @property def workbasket(self): return WorkBasket.objects.get(pk=self.kwargs["wb_pk"]) @@ -1811,7 +1810,7 @@ def commodities(self): .values_list("pk") ) - @cached_property + @property def measures(self): # Should I only be filtering for measures without an end date? return Measure.objects.current().filter( @@ -1845,7 +1844,6 @@ def get_context_data(self, **kwargs): def post(self, request, *args, **kwargs): if request.POST.get("action", None) == "auto-end-date-measures": self.end_measures() - return redirect( "workbaskets:workbasket-ui-auto-end-date-measures-confirm", self.workbasket.pk, @@ -1853,56 +1851,8 @@ def post(self, request, *args, **kwargs): @atomic def end_measures(self): - """Iterate through measures on commodities, end-date those which have - already began and delete those which have not yet started.""" - # TODO: How do you check that the commodity code has actually been updated and it isn't a create or the same date as before - for ( - measure - ) in self.measures: # Does this need to be here? is this a likely occurence? - commodity = GoodsNomenclature.objects.all().get( - pk=measure.goods_nomenclature_id, - transaction__workbasket=self.workbasket, - ) - if measure.valid_between.lower > commodity.valid_between.upper: - continue - if measure.valid_between.lower > date.today(): - new_measure_version = measure.new_version( - workbasket=self.workbasket, - update_type=UpdateType.DELETE, - ) - else: - new_measure_version = measure.new_version( - workbasket=self.workbasket, - update_type=UpdateType.UPDATE, - valid_between=TaricDateRange( - measure.valid_between.lower, - commodity.valid_between.upper, - ), - ) - self.promote_measure_to_top(new_measure_version.transaction) - - def promote_measure_to_top(self, promoted_measure): - """Set the transaction order of `promoted_measure` to be first in the - workbasket, demoting the transactions that came before it.""" - # Is it better to bulk move all measure transactions at the end? - - top_transaction = self.workbasket_transactions().first() - - if ( - not promoted_measure - or not top_transaction - or promoted_measure == top_transaction - ): - return - - current_position = promoted_measure.order - top_position = top_transaction.order - self.workbasket_transactions().filter(order__lt=current_position).update( - order=F("order") + 1, - ) - - promoted_measure.order = top_position - promoted_measure.save(update_fields=["order"]) + measure_pks = [measure.pk for measure in self.measures] + call_end_measures.apply_async((measure_pks, self.workbasket.pk)) class AutoEndDateMeasuresConfirm(DetailView):