diff --git a/lemarche/siaes/models.py b/lemarche/siaes/models.py index 0f1602b03..ece4dd621 100644 --- a/lemarche/siaes/models.py +++ b/lemarche/siaes/models.py @@ -873,7 +873,7 @@ def __init__(self, *args, **kwargs): """ https://stackoverflow.com/a/23363123 """ - super(Siae, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) for field_name in self.TRACK_UPDATE_FIELDS: setattr(self, f"__previous_{field_name}", getattr(self, field_name)) diff --git a/lemarche/tenders/models.py b/lemarche/tenders/models.py index fe79af028..67dd3ae1a 100644 --- a/lemarche/tenders/models.py +++ b/lemarche/tenders/models.py @@ -292,6 +292,11 @@ class Tender(models.Model): FIELDS_STATS = FIELDS_STATS_COUNT + FIELDS_STATS_TIMESTAMPS + ["marche_benefits"] READONLY_FIELDS = FIELDS_SURVEY_TRANSACTIONED + FIELDS_STATS + TRACK_UPDATE_FIELDS = [ + # set last_updated fields + "siae_transactioned", + ] + # used in templates STATUS_DRAFT = tender_constants.STATUS_DRAFT STATUS_PUBLISHED = tender_constants.STATUS_PUBLISHED @@ -596,6 +601,14 @@ class Meta: def __str__(self): return self.title + def __init__(self, *args, **kwargs): + """ + https://stackoverflow.com/a/23363123 + """ + super().__init__(*args, **kwargs) + for field_name in self.TRACK_UPDATE_FIELDS: + setattr(self, f"__previous_{field_name}", getattr(self, field_name)) + def set_slug(self, with_uuid=False): """ The slug field should be unique. @@ -605,6 +618,19 @@ def set_slug(self, with_uuid=False): if with_uuid: self.slug += f"-{str(uuid4())[:4]}" + def set_last_updated_fields(self): + """ + We track changes on some fields, in order to update their 'last_updated' counterpart. + Where are the '__previous' fields set? In the __init__ method + """ + for field_name in self.TRACK_UPDATE_FIELDS: + previous_field_name = f"__previous_{field_name}" + if getattr(self, field_name) and getattr(self, field_name) != getattr(self, previous_field_name): + try: + setattr(self, f"{field_name}_last_updated", timezone.now()) + except AttributeError: # TRACK_UPDATE_FIELDS without last_updated fields + pass + def set_siae_found_list(self): """ Where the Tender-Siae matching magic happens! @@ -648,10 +674,12 @@ def set_siae_found_list(self): def save(self, *args, **kwargs): """ + - update the "last_updated" fields - update the object stats - update the object content_fill_dates - generate the slug field """ + self.set_last_updated_fields() try: self.set_slug() with transaction.atomic(): diff --git a/lemarche/www/tenders/tests.py b/lemarche/www/tenders/tests.py index 3d4bfef09..5c82dc407 100644 --- a/lemarche/www/tenders/tests.py +++ b/lemarche/www/tenders/tests.py @@ -1770,12 +1770,16 @@ def test_only_tender_author_with_sesame_token_can_call_tender_survey_transaction # full form displayed (but should never happen) def test_update_tender_stats_on_tender_survey_transactioned_answer_true(self): + self.assertIsNone(Tender.objects.get(id=self.tender.id).survey_transactioned_answer) + self.assertIsNone(Tender.objects.get(id=self.tender.id).siae_transactioned) + self.assertIsNone(Tender.objects.get(id=self.tender.id).siae_transactioned_last_updated) # load with answer 'True': partial form url = self.url + self.user_buyer_1_sesame_query_string + "&answer=True" response = self.client.get(url, follow=True) self.assertEqual(response.status_code, 200) self.assertTrue(Tender.objects.get(id=self.tender.id).survey_transactioned_answer) self.assertTrue(Tender.objects.get(id=self.tender.id).siae_transactioned) + self.assertIsNotNone(Tender.objects.get(id=self.tender.id).siae_transactioned_last_updated) # fill in form response = self.client.post( url, data={"survey_transactioned_amount": 1000, "survey_transactioned_feedback": "Feedback"}, follow=True diff --git a/lemarche/www/tenders/utils.py b/lemarche/www/tenders/utils.py index 0c01b2823..22652eb18 100644 --- a/lemarche/www/tenders/utils.py +++ b/lemarche/www/tenders/utils.py @@ -104,8 +104,13 @@ def get_or_create_user(request_user, tender_dict: dict, source=user_constants.SO def duplicate(tender: Tender, fields_to_remove=FIELDS_TO_REMOVE) -> Tender: - fields_to_remove_full = ["_state", "_django_version", "id", "slug"] + fields_to_remove - fields_to_keep = [field for field in tender.__dict__.keys() if field not in fields_to_remove_full] + fields_to_remove_full = ["id", "slug"] + fields_to_remove + # other rule: fields starting with "_" : "_state", "_django_version", "__previous_siae_transactioned", + fields_to_keep = [ + field_name + for field_name in tender.__dict__.keys() + if (field_name not in fields_to_remove_full and not field_name.startswith("_")) + ] # sectors # managed post-create new_tender_dict = dict() diff --git a/lemarche/www/tenders/views.py b/lemarche/www/tenders/views.py index f45765aed..cf0868f77 100644 --- a/lemarche/www/tenders/views.py +++ b/lemarche/www/tenders/views.py @@ -616,13 +616,11 @@ def get(self, request, *args, **kwargs): if survey_transactioned_answer in ["True", "False"]: # transform survey_transactioned_answer into bool survey_transactioned_answer = survey_transactioned_answer == "True" - # update survey_transactioned_answer - Tender.objects.filter(id=self.object.id).update( - survey_transactioned_answer=survey_transactioned_answer, - survey_transactioned_answer_date=timezone.now(), - siae_transactioned=survey_transactioned_answer, - updated_at=timezone.now(), - ) + # update tender + self.object.survey_transactioned_answer = survey_transactioned_answer + self.object.survey_transactioned_answer_date = timezone.now() + self.object.siae_transactioned = survey_transactioned_answer + self.object.save() else: pass # TODO or not? "answer" should always be passed @@ -683,12 +681,10 @@ def get(self, request, *args, **kwargs): if survey_transactioned_answer in ["True", "False"]: # transform survey_transactioned_answer into bool survey_transactioned_answer = survey_transactioned_answer == "True" - # update survey_transactioned_answer - TenderSiae.objects.filter(id=self.object.id).update( - survey_transactioned_answer=survey_transactioned_answer, - survey_transactioned_answer_date=timezone.now(), - updated_at=timezone.now(), - ) + # update tender + self.object.survey_transactioned_answer = survey_transactioned_answer + self.object.survey_transactioned_answer_date = timezone.now() + self.object.save() else: pass # TODO or not? "answer" should always be passed