From 50d1abf253bd872a7fae2f6568abcbabc1ef8cd6 Mon Sep 17 00:00:00 2001 From: Matthew Spence Date: Fri, 16 Aug 2024 10:03:40 -0500 Subject: [PATCH 01/14] initial work for migrating domainrequest model --- .../commands/populate_domain_request_dates.py | 21 ++++++++++++ src/registrar/models/domain_request.py | 33 ++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 src/registrar/management/commands/populate_domain_request_dates.py diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py new file mode 100644 index 000000000..7ea898c4c --- /dev/null +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -0,0 +1,21 @@ +import logging +from django.core.management import BaseCommand +from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors +from registrar.models import DomainRequest + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand, PopulateScriptTemplate): + help = "Loops through each valid domain request object and populates the last_status_update and first_submitted_date" + + def handle(self, **kwargs): + """Loops through each valid DomainRequest object and populates its last_status_update and first_submitted_date values""" + self.mass_update_records(DomainRequest, ["last_status_update", "last_submitted_date"]) + + def update_record(self, record: DomainRequest): + """Defines how we update the first_submitted_date and last_status_update fields""" + record.set_dates() + logger.info( + f"{TerminalColors.OKCYAN}Updating {record} => first submitted date: " f"{record.first_submitted_date}{TerminalColors.OKCYAN}, last status update:" f"{record.last_status_update}{TerminalColors.OKCYAN}" + ) diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 363de213b..d2c90243c 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -563,15 +563,32 @@ def get_action_needed_reason_label(cls, action_needed_reason: str): help_text="Acknowledged .gov acceptable use policy", ) - # submission date records when domain request is submitted - submission_date = models.DateField( + # initial submission date records when domain request was first submitted + first_submitted_date = models.DateField( null=True, blank=True, default=None, verbose_name="submitted at", - help_text="Date submitted", + help_text="Date initially submitted", ) + # last submission date records when domain request was last submitted + last_submitted_date = models.DateField( + null=True, + blank=True, + default=None, + verbose_name="submitted at", + help_text="Date last submitted", + ) + + # last status update records when domain request status was last updated by an admin or analyst + last_status_update = models.DateField( + null=True, + blank=True, + default=None, + verbose_name="last updated at", + help_text="Date of last status updated", + ) notes = models.TextField( null=True, blank=True, @@ -621,6 +638,9 @@ def save(self, *args, **kwargs): self.sync_organization_type() self.sync_yes_no_form_fields() + if self._cached_status != self.status: + self.last_status_update = timezone.now().date() + super().save(*args, **kwargs) # Handle the action needed email. @@ -796,9 +816,12 @@ def submit(self): DraftDomain = apps.get_model("registrar.DraftDomain") if not DraftDomain.string_could_be_domain(self.requested_domain.name): raise ValueError("Requested domain is not a valid domain name.") + + if not self.first_submitted_date: + self.first_submitted_date = timezone.now().date() - # Update submission_date to today - self.submission_date = timezone.now().date() + # Update last_submission_date to today + self.last_submitted_date = timezone.now().date() self.save() # Limit email notifications to transitions from Started and Withdrawn From f054cfa5cf3144fdaf8c73573177a2a78afca82c Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Fri, 16 Aug 2024 15:35:58 -0500 Subject: [PATCH 02/14] First complete pass --- src/registrar/admin.py | 6 ++- .../commands/populate_domain_request_dates.py | 29 +++++++++++-- .../commands/utility/terminal_helper.py | 2 +- ...0118_add_domainrequest_submission_dates.py | 43 +++++++++++++++++++ src/registrar/models/domain_request.py | 9 ++-- 5 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 src/registrar/migrations/0118_add_domainrequest_submission_dates.py diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 18c1052fc..3f3547341 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1649,7 +1649,9 @@ def queryset(self, request, queryset): # Columns list_display = [ "requested_domain", - "submission_date", + "first_submitted_date", + "last_submitted_date", + "last_status_update", "status", "generic_org_type", "federal_type", @@ -1852,7 +1854,7 @@ def get_fieldsets(self, request, obj=None): # Table ordering # NOTE: This impacts the select2 dropdowns (combobox) # Currentl, there's only one for requests on DomainInfo - ordering = ["-submission_date", "requested_domain__name"] + ordering = ["-last_submitted_date", "requested_domain__name"] change_form_template = "django/admin/domain_request_change_form.html" diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index 7ea898c4c..90fc06dcf 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -2,20 +2,41 @@ from django.core.management import BaseCommand from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors from registrar.models import DomainRequest +from auditlog.models import LogEntry +from django.core.exceptions import ObjectDoesNotExist logger = logging.getLogger(__name__) class Command(BaseCommand, PopulateScriptTemplate): - help = "Loops through each valid domain request object and populates the last_status_update and first_submitted_date" + help = "Loops through each domain request object and populates the last_status_update and first_submitted_date" def handle(self, **kwargs): - """Loops through each valid DomainRequest object and populates its last_status_update and first_submitted_date values""" - self.mass_update_records(DomainRequest, ["last_status_update", "last_submitted_date"]) + """Loops through each DomainRequest object and populates its last_status_update and first_submitted_date values""" + self.mass_update_records(DomainRequest, None, ["last_status_update", "first_submitted_date"]) def update_record(self, record: DomainRequest): """Defines how we update the first_submitted_date and last_status_update fields""" - record.set_dates() + try: + # Retrieve and order audit log entries by timestamp in descending order + audit_log_entries = LogEntry.objects.filter(object_pk=record.pk).order_by("-timestamp") + + # Loop through logs in descending order to find most recent status change + for log_entry in audit_log_entries: + if "status" in log_entry.changes: + record.last_status_update = log_entry.timestamp.date() + break + + # Loop through logs in ascending order to find first submission + for log_entry in audit_log_entries.reverse(): + if log_entry.changes_dict['status'](1) == 'Submitted': + record.first_submitted_date = log_entry.timestamp.date() + + except ObjectDoesNotExist as e: + logger.error(f"Object with object_pk {record.pk} does not exist: {e}") + except Exception as e: + logger.error(f"An error occurred during update_record: {e}") + logger.info( f"{TerminalColors.OKCYAN}Updating {record} => first submitted date: " f"{record.first_submitted_date}{TerminalColors.OKCYAN}, last status update:" f"{record.last_status_update}{TerminalColors.OKCYAN}" ) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 2c69e1080..b9e11be5d 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -86,7 +86,7 @@ def mass_update_records(self, object_class, filter_conditions, fields_to_update, You must define update_record before you can use this function. """ - records = object_class.objects.filter(**filter_conditions) + records = object_class.objects.filter(**filter_conditions) if filter_conditions else object_class.objects.all() readable_class_name = self.get_class_name(object_class) # Code execution will stop here if the user prompts "N" diff --git a/src/registrar/migrations/0118_add_domainrequest_submission_dates.py b/src/registrar/migrations/0118_add_domainrequest_submission_dates.py new file mode 100644 index 000000000..c971b2f9a --- /dev/null +++ b/src/registrar/migrations/0118_add_domainrequest_submission_dates.py @@ -0,0 +1,43 @@ +# Generated by Django 4.2.10 on 2024-08-16 15:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0117_alter_portfolioinvitation_portfolio_additional_permissions_and_more"), + ] + + operations = [ + migrations.RenameField( + model_name="domainrequest", + old_name="submission_date", + new_name="last_submitted_date", + ), + migrations.AlterField( + model_name="domainrequest", + name="last_submitted_date", + field=models.DateField( + blank=True, default=None, help_text="Date last submitted", null=True, verbose_name="last submitted on" + ), + ), + migrations.AddField( + model_name="domainrequest", + name="first_submitted_date", + field=models.DateField( + blank=True, default=None, help_text="Date initially submitted", null=True, verbose_name="first submitted on" + ), + ), + migrations.AddField( + model_name="domainrequest", + name="last_status_update", + field=models.DateField( + blank=True, + default=None, + help_text="Date of last status updated", + null=True, + verbose_name="last updated on", + ), + ), + ] diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index d2c90243c..e9711f0ae 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -568,7 +568,7 @@ def get_action_needed_reason_label(cls, action_needed_reason: str): null=True, blank=True, default=None, - verbose_name="submitted at", + verbose_name="first submitted on", help_text="Date initially submitted", ) @@ -577,7 +577,7 @@ def get_action_needed_reason_label(cls, action_needed_reason: str): null=True, blank=True, default=None, - verbose_name="submitted at", + verbose_name="last submitted on", help_text="Date last submitted", ) @@ -586,7 +586,7 @@ def get_action_needed_reason_label(cls, action_needed_reason: str): null=True, blank=True, default=None, - verbose_name="last updated at", + verbose_name="last updated on", help_text="Date of last status updated", ) notes = models.TextField( @@ -816,7 +816,8 @@ def submit(self): DraftDomain = apps.get_model("registrar.DraftDomain") if not DraftDomain.string_could_be_domain(self.requested_domain.name): raise ValueError("Requested domain is not a valid domain name.") - + + # if the domain has not been submitted before this must be the first time if not self.first_submitted_date: self.first_submitted_date = timezone.now().date() From df9f3dcce57032153aea29906249170c1c40d70a Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Tue, 20 Aug 2024 13:48:16 -0500 Subject: [PATCH 03/14] fix some tests --- src/registrar/fixtures_domain_requests.py | 2 +- src/registrar/models/domain_request.py | 2 +- .../already_has_domains.txt | 2 +- .../emails/action_needed_reasons/bad_name.txt | 2 +- .../eligibility_unclear.txt | 2 +- .../questionable_senior_official.txt | 2 +- .../emails/domain_request_withdrawn.txt | 2 +- .../emails/status_change_approved.txt | 2 +- .../emails/status_change_rejected.txt | 2 +- .../emails/submission_confirmation.txt | 2 +- .../includes/domain_requests_table.html | 2 +- src/registrar/tests/common.py | 6 +-- src/registrar/tests/test_admin_request.py | 26 +++++----- src/registrar/tests/test_reports.py | 2 +- .../tests/test_views_requests_json.py | 48 +++++++++---------- src/registrar/utility/csv_export.py | 6 +-- src/registrar/views/domain_requests_json.py | 2 +- src/registrar/views/report_views.py | 6 +-- 18 files changed, 60 insertions(+), 58 deletions(-) diff --git a/src/registrar/fixtures_domain_requests.py b/src/registrar/fixtures_domain_requests.py index 50f611474..a5ec3fc74 100644 --- a/src/registrar/fixtures_domain_requests.py +++ b/src/registrar/fixtures_domain_requests.py @@ -95,7 +95,7 @@ def _set_non_foreign_key_fields(cls, da: DomainRequest, app: dict): # TODO for a future ticket: Allow for more than just "federal" here da.generic_org_type = app["generic_org_type"] if "generic_org_type" in app else "federal" - da.submission_date = fake.date() + da.last_submitted_date = fake.date() da.federal_type = ( app["federal_type"] if "federal_type" in app diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index e9711f0ae..77ebf88f8 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -821,7 +821,7 @@ def submit(self): if not self.first_submitted_date: self.first_submitted_date = timezone.now().date() - # Update last_submission_date to today + # Update last_submitted_date to today self.last_submitted_date = timezone.now().date() self.save() diff --git a/src/registrar/templates/emails/action_needed_reasons/already_has_domains.txt b/src/registrar/templates/emails/action_needed_reasons/already_has_domains.txt index b1b3b0a1c..2e3012c91 100644 --- a/src/registrar/templates/emails/action_needed_reasons/already_has_domains.txt +++ b/src/registrar/templates/emails/action_needed_reasons/already_has_domains.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. We've identified an action that you’ll need to complete before we continue reviewing your .gov domain request. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Action needed ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/action_needed_reasons/bad_name.txt b/src/registrar/templates/emails/action_needed_reasons/bad_name.txt index 7d088aa4e..9481a1e63 100644 --- a/src/registrar/templates/emails/action_needed_reasons/bad_name.txt +++ b/src/registrar/templates/emails/action_needed_reasons/bad_name.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. We've identified an action that you’ll need to complete before we continue reviewing your .gov domain request. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Action needed ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/action_needed_reasons/eligibility_unclear.txt b/src/registrar/templates/emails/action_needed_reasons/eligibility_unclear.txt index d3a986183..705805998 100644 --- a/src/registrar/templates/emails/action_needed_reasons/eligibility_unclear.txt +++ b/src/registrar/templates/emails/action_needed_reasons/eligibility_unclear.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. We've identified an action that you’ll need to complete before we continue reviewing your .gov domain request. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Action needed ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt b/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt index e20e4cb60..5967d7089 100644 --- a/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt +++ b/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. We've identified an action that you’ll need to complete before we continue reviewing your .gov domain request. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Action needed ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/domain_request_withdrawn.txt b/src/registrar/templates/emails/domain_request_withdrawn.txt index 6efa92d64..0db00feea 100644 --- a/src/registrar/templates/emails/domain_request_withdrawn.txt +++ b/src/registrar/templates/emails/domain_request_withdrawn.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. Your .gov domain request has been withdrawn and will not be reviewed by our team. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Withdrawn ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/status_change_approved.txt b/src/registrar/templates/emails/status_change_approved.txt index 70f813599..66f8f8b6c 100644 --- a/src/registrar/templates/emails/status_change_approved.txt +++ b/src/registrar/templates/emails/status_change_approved.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. Congratulations! Your .gov domain request has been approved. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Approved You can manage your approved domain on the .gov registrar . diff --git a/src/registrar/templates/emails/status_change_rejected.txt b/src/registrar/templates/emails/status_change_rejected.txt index 2fcbb1d83..4e5250162 100644 --- a/src/registrar/templates/emails/status_change_rejected.txt +++ b/src/registrar/templates/emails/status_change_rejected.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. Your .gov domain request has been rejected. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Rejected ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/submission_confirmation.txt b/src/registrar/templates/emails/submission_confirmation.txt index 740e6f393..c8ff4c7eb 100644 --- a/src/registrar/templates/emails/submission_confirmation.txt +++ b/src/registrar/templates/emails/submission_confirmation.txt @@ -4,7 +4,7 @@ Hi, {{ recipient.first_name }}. We received your .gov domain request. DOMAIN REQUESTED: {{ domain_request.requested_domain.name }} -REQUEST RECEIVED ON: {{ domain_request.submission_date|date }} +REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Submitted ---------------------------------------------------------------- diff --git a/src/registrar/templates/includes/domain_requests_table.html b/src/registrar/templates/includes/domain_requests_table.html index 487c1cee5..bd909350c 100644 --- a/src/registrar/templates/includes/domain_requests_table.html +++ b/src/registrar/templates/includes/domain_requests_table.html @@ -45,7 +45,7 @@

Domain requests

Domain name - Date submitted + Date submitted Status Action diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index a4c3e2ef4..85d1c56e7 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -775,13 +775,13 @@ def sharedSetUp(cls): cls.domain_request_3.alternative_domains.add(website, website_2) cls.domain_request_3.current_websites.add(website_3, website_4) cls.domain_request_3.cisa_representative_email = "test@igorville.com" - cls.domain_request_3.submission_date = get_time_aware_date(datetime(2024, 4, 2)) + cls.domain_request_3.last_submitted_date = get_time_aware_date(datetime(2024, 4, 2)) cls.domain_request_3.save() - cls.domain_request_4.submission_date = get_time_aware_date(datetime(2024, 4, 2)) + cls.domain_request_4.last_submitted_date = get_time_aware_date(datetime(2024, 4, 2)) cls.domain_request_4.save() - cls.domain_request_6.submission_date = get_time_aware_date(datetime(2024, 4, 2)) + cls.domain_request_6.last_submitted_date = get_time_aware_date(datetime(2024, 4, 2)) cls.domain_request_6.save() @classmethod diff --git a/src/registrar/tests/test_admin_request.py b/src/registrar/tests/test_admin_request.py index c4fc8bcee..d81f73156 100644 --- a/src/registrar/tests/test_admin_request.py +++ b/src/registrar/tests/test_admin_request.py @@ -422,7 +422,7 @@ def test_submitter_sortable(self): # Assert that our sort works correctly self.test_helper.assert_table_sorted( - "11", + "13", ( "submitter__first_name", "submitter__last_name", @@ -431,7 +431,7 @@ def test_submitter_sortable(self): # Assert that sorting in reverse works correctly self.test_helper.assert_table_sorted( - "-11", + "-13", ( "-submitter__first_name", "-submitter__last_name", @@ -454,7 +454,7 @@ def test_investigator_sortable(self): # Assert that our sort works correctly self.test_helper.assert_table_sorted( - "12", + "14", ( "investigator__first_name", "investigator__last_name", @@ -463,7 +463,7 @@ def test_investigator_sortable(self): # Assert that sorting in reverse works correctly self.test_helper.assert_table_sorted( - "-12", + "-14", ( "-investigator__first_name", "-investigator__last_name", @@ -476,7 +476,7 @@ def test_investigator_sortable(self): @less_console_noise_decorator def test_default_sorting_in_domain_requests_list(self): """ - Make sure the default sortin in on the domain requests list page is reverse submission_date + Make sure the default sortin in on the domain requests list page is reverse last_submitted_date then alphabetical requested_domain """ @@ -486,12 +486,12 @@ def test_default_sorting_in_domain_requests_list(self): for name in ["ccc.gov", "bbb.gov", "eee.gov", "aaa.gov", "zzz.gov", "ddd.gov"] ] - domain_requests[0].submission_date = timezone.make_aware(datetime(2024, 10, 16)) - domain_requests[1].submission_date = timezone.make_aware(datetime(2001, 10, 16)) - domain_requests[2].submission_date = timezone.make_aware(datetime(1980, 10, 16)) - domain_requests[3].submission_date = timezone.make_aware(datetime(1998, 10, 16)) - domain_requests[4].submission_date = timezone.make_aware(datetime(2013, 10, 16)) - domain_requests[5].submission_date = timezone.make_aware(datetime(1980, 10, 16)) + domain_requests[0].last_submitted_date = timezone.make_aware(datetime(2024, 10, 16)) + domain_requests[1].last_submitted_date = timezone.make_aware(datetime(2001, 10, 16)) + domain_requests[2].last_submitted_date = timezone.make_aware(datetime(1980, 10, 16)) + domain_requests[3].last_submitted_date = timezone.make_aware(datetime(1998, 10, 16)) + domain_requests[4].last_submitted_date = timezone.make_aware(datetime(2013, 10, 16)) + domain_requests[5].last_submitted_date = timezone.make_aware(datetime(1980, 10, 16)) # Save the modified domain requests to update their attributes in the database for domain_request in domain_requests: @@ -1556,7 +1556,9 @@ def test_readonly_when_restricted_creator(self): "cisa_representative_last_name", "has_cisa_representative", "is_policy_acknowledged", - "submission_date", + "first_submitted_date", + "last_submitted_date", + "last_status_update", "notes", "alternative_domains", ] diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 52aaa8c38..4b58d6d22 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -762,7 +762,7 @@ def test_get_sliced_requests(self): with less_console_noise(): filter_condition = { "status": DomainRequest.DomainRequestStatus.SUBMITTED, - "submission_date__lte": self.end_date, + "last_submitted_date__lte": self.end_date, } submitted_requests_sliced_at_end_date = DomainRequestExport.get_sliced_requests(filter_condition) expected_content = [3, 2, 0, 0, 0, 0, 1, 0, 0, 1] diff --git a/src/registrar/tests/test_views_requests_json.py b/src/registrar/tests/test_views_requests_json.py index 7bdc922cf..c140b7e44 100644 --- a/src/registrar/tests/test_views_requests_json.py +++ b/src/registrar/tests/test_views_requests_json.py @@ -25,91 +25,91 @@ def setUpClass(cls): DomainRequest.objects.create( creator=cls.user, requested_domain=lamb_chops, - submission_date="2024-01-01", + last_submitted_date="2024-01-01", status=DomainRequest.DomainRequestStatus.STARTED, created_at="2024-01-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=short_ribs, - submission_date="2024-02-01", + last_submitted_date="2024-02-01", status=DomainRequest.DomainRequestStatus.WITHDRAWN, created_at="2024-02-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=beef_chuck, - submission_date="2024-03-01", + last_submitted_date="2024-03-01", status=DomainRequest.DomainRequestStatus.REJECTED, created_at="2024-03-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=stew_beef, - submission_date="2024-04-01", + last_submitted_date="2024-04-01", status=DomainRequest.DomainRequestStatus.STARTED, created_at="2024-04-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-05-01", + last_submitted_date="2024-05-01", status=DomainRequest.DomainRequestStatus.STARTED, created_at="2024-05-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-06-01", + last_submitted_date="2024-06-01", status=DomainRequest.DomainRequestStatus.WITHDRAWN, created_at="2024-06-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-07-01", + last_submitted_date="2024-07-01", status=DomainRequest.DomainRequestStatus.REJECTED, created_at="2024-07-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-08-01", + last_submitted_date="2024-08-01", status=DomainRequest.DomainRequestStatus.STARTED, created_at="2024-08-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-09-01", + last_submitted_date="2024-09-01", status=DomainRequest.DomainRequestStatus.STARTED, created_at="2024-09-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-10-01", + last_submitted_date="2024-10-01", status=DomainRequest.DomainRequestStatus.WITHDRAWN, created_at="2024-10-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-11-01", + last_submitted_date="2024-11-01", status=DomainRequest.DomainRequestStatus.REJECTED, created_at="2024-11-01", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-11-02", + last_submitted_date="2024-11-02", status=DomainRequest.DomainRequestStatus.WITHDRAWN, created_at="2024-11-02", ), DomainRequest.objects.create( creator=cls.user, requested_domain=None, - submission_date="2024-12-01", + last_submitted_date="2024-12-01", status=DomainRequest.DomainRequestStatus.APPROVED, created_at="2024-12-01", ), @@ -138,7 +138,7 @@ def test_get_domain_requests_json_authenticated(self): # Extract fields from response requested_domains = [request["requested_domain"] for request in data["domain_requests"]] - submission_dates = [request["submission_date"] for request in data["domain_requests"]] + last_submitted_dates = [request["last_submitted_date"] for request in data["domain_requests"]] statuses = [request["status"] for request in data["domain_requests"]] created_ats = [request["created_at"] for request in data["domain_requests"]] ids = [request["id"] for request in data["domain_requests"]] @@ -154,7 +154,7 @@ def test_get_domain_requests_json_authenticated(self): self.domain_requests[i].requested_domain.name if self.domain_requests[i].requested_domain else None, requested_domains[i], ) - self.assertEqual(self.domain_requests[i].submission_date, submission_dates[i]) + self.assertEqual(self.domain_requests[i].last_submitted_date, last_submitted_dates[i]) self.assertEqual(self.domain_requests[i].get_status_display(), statuses[i]) self.assertEqual( parse_datetime(self.domain_requests[i].created_at.isoformat()), parse_datetime(created_ats[i]) @@ -287,26 +287,26 @@ def test_pagination(self): def test_sorting(self): """test that sorting works properly on the result set""" - response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "desc"}) + response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"}) self.assertEqual(response.status_code, 200) data = response.json - # Check if sorted by submission_date in descending order - submission_dates = [req["submission_date"] for req in data["domain_requests"]] - self.assertEqual(submission_dates, sorted(submission_dates, reverse=True)) + # Check if sorted by last_submitted_date in descending order + last_submitted_dates = [req["last_submitted_date"] for req in data["domain_requests"]] + self.assertEqual(last_submitted_dates, sorted(last_submitted_dates, reverse=True)) - response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "asc"}) + response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "asc"}) self.assertEqual(response.status_code, 200) data = response.json - # Check if sorted by submission_date in ascending order - submission_dates = [req["submission_date"] for req in data["domain_requests"]] - self.assertEqual(submission_dates, sorted(submission_dates)) + # Check if sorted by last_submitted_date in ascending order + last_submitted_dates = [req["last_submitted_date"] for req in data["domain_requests"]] + self.assertEqual(last_submitted_dates, sorted(last_submitted_dates)) def test_filter_approved_excluded(self): """test that approved requests are excluded from result set.""" # sort in reverse chronological order of submission date, since most recent request is approved - response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "desc"}) + response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"}) self.assertEqual(response.status_code, 200) data = response.json diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index db961a36d..6f8510418 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -1235,7 +1235,7 @@ def parse_row(cls, columns, model): "State/territory": model.get("state_territory"), "Request purpose": model.get("purpose"), "CISA regional representative": model.get("cisa_representative_email"), - "Submitted at": model.get("submission_date"), + "Submitted at": model.get("last_submitted_date"), } row = [FIELDS.get(column, "") for column in columns] @@ -1279,8 +1279,8 @@ def get_filter_conditions(cls, start_date=None, end_date=None): end_date_formatted = format_end_date(end_date) return Q( status=DomainRequest.DomainRequestStatus.SUBMITTED, - submission_date__lte=end_date_formatted, - submission_date__gte=start_date_formatted, + last_submitted_date__lte=end_date_formatted, + last_submitted_date__gte=start_date_formatted, ) @classmethod diff --git a/src/registrar/views/domain_requests_json.py b/src/registrar/views/domain_requests_json.py index 2e58c8e48..6b0b346f8 100644 --- a/src/registrar/views/domain_requests_json.py +++ b/src/registrar/views/domain_requests_json.py @@ -46,7 +46,7 @@ def get_domain_requests_json(request): domain_requests_data = [ { "requested_domain": domain_request.requested_domain.name if domain_request.requested_domain else None, - "submission_date": domain_request.submission_date, + "last_submitted_date": domain_request.last_submitted_date, "status": domain_request.get_status_display(), "created_at": format(domain_request.created_at, "c"), # Serialize to ISO 8601 "id": domain_request.id, diff --git a/src/registrar/views/report_views.py b/src/registrar/views/report_views.py index 428298b52..abdbd37c9 100644 --- a/src/registrar/views/report_views.py +++ b/src/registrar/views/report_views.py @@ -26,7 +26,7 @@ def get(self, request): created_at__gt=thirty_days_ago, status=models.DomainRequest.DomainRequestStatus.APPROVED ) avg_approval_time = last_30_days_approved_applications.annotate( - approval_time=F("approved_domain__created_at") - F("submission_date") + approval_time=F("approved_domain__created_at") - F("last_submitted_date") ).aggregate(Avg("approval_time"))["approval_time__avg"] # Format the timedelta to display only days if avg_approval_time is not None: @@ -104,11 +104,11 @@ def get(self, request): filter_submitted_requests_start_date = { "status": models.DomainRequest.DomainRequestStatus.SUBMITTED, - "submission_date__lte": start_date_formatted, + "last_submitted_date__lte": start_date_formatted, } filter_submitted_requests_end_date = { "status": models.DomainRequest.DomainRequestStatus.SUBMITTED, - "submission_date__lte": end_date_formatted, + "last_submitted_date__lte": end_date_formatted, } submitted_requests_sliced_at_start_date = csv_export.DomainRequestExport.get_sliced_requests( filter_submitted_requests_start_date From 08c42a6e8d48bb117e494e4ae82095c403106521 Mon Sep 17 00:00:00 2001 From: Matthew Spence Date: Wed, 21 Aug 2024 12:28:41 -0500 Subject: [PATCH 04/14] merge main and resolve migration conflicts --- ...sion_dates.py => 0119_add_domainrequest_submission_dates.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/registrar/migrations/{0118_add_domainrequest_submission_dates.py => 0119_add_domainrequest_submission_dates.py} (92%) diff --git a/src/registrar/migrations/0118_add_domainrequest_submission_dates.py b/src/registrar/migrations/0119_add_domainrequest_submission_dates.py similarity index 92% rename from src/registrar/migrations/0118_add_domainrequest_submission_dates.py rename to src/registrar/migrations/0119_add_domainrequest_submission_dates.py index c971b2f9a..0b94e1257 100644 --- a/src/registrar/migrations/0118_add_domainrequest_submission_dates.py +++ b/src/registrar/migrations/0119_add_domainrequest_submission_dates.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ("registrar", "0117_alter_portfolioinvitation_portfolio_additional_permissions_and_more"), + ("registrar", "0118_alter_portfolio_options_alter_portfolio_creator_and_more"), ] operations = [ From 62ac4757ec1b0f41b05d87d4d5cd2e3baa503f40 Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Thu, 22 Aug 2024 16:25:24 -0500 Subject: [PATCH 05/14] review changes --- src/registrar/assets/js/get-gov.js | 4 +- .../commands/populate_domain_request_dates.py | 40 +++++++++---------- ...0119_add_domainrequest_submission_dates.py | 2 +- src/registrar/models/domain_request.py | 8 ++-- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index f3b41eb51..70659b009 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -1599,7 +1599,7 @@ document.addEventListener('DOMContentLoaded', function() { const domainName = request.requested_domain ? request.requested_domain : `New domain request
(${utcDateString(request.created_at)})`; const actionUrl = request.action_url; const actionLabel = request.action_label; - const submissionDate = request.submission_date ? new Date(request.submission_date).toLocaleDateString('en-US', options) : `Not submitted`; + const submissionDate = request.last_submitted_date ? new Date(request.last_submitted_date).toLocaleDateString('en-US', options) : `Not submitted`; // Even if the request is not deletable, we may need this empty string for the td if the deletable column is displayed let modalTrigger = ''; @@ -1699,7 +1699,7 @@ document.addEventListener('DOMContentLoaded', function() { ${domainName} - + ${submissionDate} diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index 90fc06dcf..19d8e4f00 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -17,26 +17,26 @@ def handle(self, **kwargs): def update_record(self, record: DomainRequest): """Defines how we update the first_submitted_date and last_status_update fields""" - try: - # Retrieve and order audit log entries by timestamp in descending order - audit_log_entries = LogEntry.objects.filter(object_pk=record.pk).order_by("-timestamp") - - # Loop through logs in descending order to find most recent status change - for log_entry in audit_log_entries: - if "status" in log_entry.changes: - record.last_status_update = log_entry.timestamp.date() - break - - # Loop through logs in ascending order to find first submission - for log_entry in audit_log_entries.reverse(): - if log_entry.changes_dict['status'](1) == 'Submitted': - record.first_submitted_date = log_entry.timestamp.date() - - except ObjectDoesNotExist as e: - logger.error(f"Object with object_pk {record.pk} does not exist: {e}") - except Exception as e: - logger.error(f"An error occurred during update_record: {e}") + + # Retrieve and order audit log entries by timestamp in descending order + audit_log_entries = LogEntry.objects.filter(object_pk=record.pk).order_by("-timestamp") + + # Loop through logs in descending order to find most recent status change + for log_entry in audit_log_entries: + if 'status' in LogEntry.changes_dict: + record.last_status_update = log_entry.timestamp.date() + break + + # Loop through logs in ascending order to find first submission + for log_entry in audit_log_entries.reverse(): + if log_entry.changes_dict['status'](1) == 'Submitted': + record.first_submitted_date = log_entry.timestamp.date() + break logger.info( - f"{TerminalColors.OKCYAN}Updating {record} => first submitted date: " f"{record.first_submitted_date}{TerminalColors.OKCYAN}, last status update:" f"{record.last_status_update}{TerminalColors.OKCYAN}" + f"{TerminalColors.OKCYAN}Updating {record} => first submitted date: " f"{record.first_submitted_date}{TerminalColors.OKCYAN}, last status update:" f"{record.last_status_update}{TerminalColors.ENDC}" ) + + def should_skip_record(self, record) -> bool: + # make sure the record had some kind of history + return LogEntry.objects.filter(object_pk=record.pk).exists() diff --git a/src/registrar/migrations/0119_add_domainrequest_submission_dates.py b/src/registrar/migrations/0119_add_domainrequest_submission_dates.py index 0b94e1257..ea209626e 100644 --- a/src/registrar/migrations/0119_add_domainrequest_submission_dates.py +++ b/src/registrar/migrations/0119_add_domainrequest_submission_dates.py @@ -35,7 +35,7 @@ class Migration(migrations.Migration): field=models.DateField( blank=True, default=None, - help_text="Date of last status updated", + help_text="Date of the last status update", null=True, verbose_name="last updated on", ), diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 74d275d95..09f200793 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -563,7 +563,7 @@ def get_action_needed_reason_label(cls, action_needed_reason: str): help_text="Acknowledged .gov acceptable use policy", ) - # initial submission date records when domain request was first submitted + # Records when the domain request was first submitted first_submitted_date = models.DateField( null=True, blank=True, @@ -572,7 +572,7 @@ def get_action_needed_reason_label(cls, action_needed_reason: str): help_text="Date initially submitted", ) - # last submission date records when domain request was last submitted + # Records when domain request was last submitted last_submitted_date = models.DateField( null=True, blank=True, @@ -581,13 +581,13 @@ def get_action_needed_reason_label(cls, action_needed_reason: str): help_text="Date last submitted", ) - # last status update records when domain request status was last updated by an admin or analyst + # Records when domain request status was last updated by an admin or analyst last_status_update = models.DateField( null=True, blank=True, default=None, verbose_name="last updated on", - help_text="Date of last status updated", + help_text="Date of the last status update", ) notes = models.TextField( null=True, From 497837b09882731c121adbba88110d6155253e66 Mon Sep 17 00:00:00 2001 From: Matt-Spence Date: Mon, 26 Aug 2024 11:18:43 -0500 Subject: [PATCH 06/14] Update src/registrar/management/commands/populate_domain_request_dates.py Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com> --- .../management/commands/populate_domain_request_dates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index 19d8e4f00..fe471fc9d 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -34,7 +34,7 @@ def update_record(self, record: DomainRequest): break logger.info( - f"{TerminalColors.OKCYAN}Updating {record} => first submitted date: " f"{record.first_submitted_date}{TerminalColors.OKCYAN}, last status update:" f"{record.last_status_update}{TerminalColors.ENDC}" + f"{TerminalColors.OKCYAN}Updating {record} => first submitted date: " f"{record.first_submitted_date}, last status update:" f"{record.last_status_update}{TerminalColors.ENDC}" ) def should_skip_record(self, record) -> bool: From f7b2e38bba21c91cc46b39da26464800cdf72ac9 Mon Sep 17 00:00:00 2001 From: Matthew Spence Date: Mon, 26 Aug 2024 11:54:08 -0500 Subject: [PATCH 07/14] linter fixes --- .../commands/populate_domain_request_dates.py | 11 +++++++---- src/registrar/models/domain_request.py | 2 +- src/registrar/tests/test_views_requests_json.py | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index fe471fc9d..e20e35909 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -3,7 +3,6 @@ from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors from registrar.models import DomainRequest from auditlog.models import LogEntry -from django.core.exceptions import ObjectDoesNotExist logger = logging.getLogger(__name__) @@ -12,12 +11,13 @@ class Command(BaseCommand, PopulateScriptTemplate): help = "Loops through each domain request object and populates the last_status_update and first_submitted_date" def handle(self, **kwargs): - """Loops through each DomainRequest object and populates its last_status_update and first_submitted_date values""" + """Loops through each DomainRequest object and populates + its last_status_update and first_submitted_date values""" self.mass_update_records(DomainRequest, None, ["last_status_update", "first_submitted_date"]) def update_record(self, record: DomainRequest): """Defines how we update the first_submitted_date and last_status_update fields""" - + # Retrieve and order audit log entries by timestamp in descending order audit_log_entries = LogEntry.objects.filter(object_pk=record.pk).order_by("-timestamp") @@ -34,7 +34,10 @@ def update_record(self, record: DomainRequest): break logger.info( - f"{TerminalColors.OKCYAN}Updating {record} => first submitted date: " f"{record.first_submitted_date}, last status update:" f"{record.last_status_update}{TerminalColors.ENDC}" + f"""{TerminalColors.OKCYAN}Updating {record} => + first submitted date: {record.first_submitted_date}, + last status update: {record.last_status_update}{TerminalColors.ENDC} + """ ) def should_skip_record(self, record) -> bool: diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 09f200793..7ee80e43a 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -823,7 +823,7 @@ def submit(self): if not DraftDomain.string_could_be_domain(self.requested_domain.name): raise ValueError("Requested domain is not a valid domain name.") - # if the domain has not been submitted before this must be the first time + # if the domain has not been submitted before this must be the first time if not self.first_submitted_date: self.first_submitted_date = timezone.now().date() diff --git a/src/registrar/tests/test_views_requests_json.py b/src/registrar/tests/test_views_requests_json.py index c140b7e44..fd44bdc31 100644 --- a/src/registrar/tests/test_views_requests_json.py +++ b/src/registrar/tests/test_views_requests_json.py @@ -287,7 +287,8 @@ def test_pagination(self): def test_sorting(self): """test that sorting works properly on the result set""" - response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"}) + response = self.app.get(reverse("get_domain_requests_json"), + {"sort_by": "last_submitted_date", "order": "desc"}) self.assertEqual(response.status_code, 200) data = response.json @@ -306,7 +307,8 @@ def test_sorting(self): def test_filter_approved_excluded(self): """test that approved requests are excluded from result set.""" # sort in reverse chronological order of submission date, since most recent request is approved - response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"}) + response = self.app.get(reverse("get_domain_requests_json"), + {"sort_by": "last_submitted_date", "order": "desc"}) self.assertEqual(response.status_code, 200) data = response.json From 207a3ac3aa2801e1d188f28349d1ea71a95b328a Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Tue, 27 Aug 2024 13:31:03 -0500 Subject: [PATCH 08/14] fix populate script --- .../management/commands/populate_domain_request_dates.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index e20e35909..f7ea5ce27 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -20,16 +20,16 @@ def update_record(self, record: DomainRequest): # Retrieve and order audit log entries by timestamp in descending order audit_log_entries = LogEntry.objects.filter(object_pk=record.pk).order_by("-timestamp") - # Loop through logs in descending order to find most recent status change for log_entry in audit_log_entries: - if 'status' in LogEntry.changes_dict: + if 'status' in log_entry.changes_dict: record.last_status_update = log_entry.timestamp.date() break # Loop through logs in ascending order to find first submission for log_entry in audit_log_entries.reverse(): - if log_entry.changes_dict['status'](1) == 'Submitted': + status = log_entry.changes_dict.get('status') + if status and status[1] == 'Submitted': record.first_submitted_date = log_entry.timestamp.date() break @@ -42,4 +42,4 @@ def update_record(self, record: DomainRequest): def should_skip_record(self, record) -> bool: # make sure the record had some kind of history - return LogEntry.objects.filter(object_pk=record.pk).exists() + return not LogEntry.objects.filter(object_pk=record.pk).exists() \ No newline at end of file From 9e592cbd753052c6f5df68d5120bf732f6c43e25 Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Tue, 27 Aug 2024 15:11:43 -0500 Subject: [PATCH 09/14] fix dumb typo --- .../management/commands/populate_domain_request_dates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index f7ea5ce27..cef140cdd 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -29,7 +29,7 @@ def update_record(self, record: DomainRequest): # Loop through logs in ascending order to find first submission for log_entry in audit_log_entries.reverse(): status = log_entry.changes_dict.get('status') - if status and status[1] == 'Submitted': + if status and status[1] == 'submitted': record.first_submitted_date = log_entry.timestamp.date() break From 9c069a8e64e48b237caec9d41423aec264f66fb6 Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Tue, 27 Aug 2024 15:12:57 -0500 Subject: [PATCH 10/14] add instructions --- docs/operations/data_migration.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/operations/data_migration.md b/docs/operations/data_migration.md index cd748b22d..ed2f643d5 100644 --- a/docs/operations/data_migration.md +++ b/docs/operations/data_migration.md @@ -816,3 +816,30 @@ Example: `cf ssh getgov-za` | | Parameter | Description | |:-:|:-------------------------- |:-----------------------------------------------------------------------------------| | 1 | **federal_cio_csv_path** | Specifies where the federal CIO csv is | + +## Populate Domain Request Dates +This section outlines how to run the populate_domain_request_dates script + +### Running on sandboxes + +#### Step 1: Login to CloudFoundry +```cf login -a api.fr.cloud.gov --sso``` + +#### Step 2: SSH into your environment +```cf ssh getgov-{space}``` + +Example: `cf ssh getgov-za` + +#### Step 3: Create a shell instance +```/tmp/lifecycle/shell``` + +#### Step 4: Running the script +```./manage.py populate_domain_request_dates --debug``` + +### Running locally +```docker-compose exec app ./manage.py populate_domain_request_dates --debug``` + +##### Optional parameters +| | Parameter | Description | +|:-:|:-------------------------- |:----------------------------------------------------------------------------| +| 1 | **debug** | Increases logging detail. Defaults to False. | From 09ba7450b39d00e6cec1a3e91085ede27715db09 Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Tue, 27 Aug 2024 15:19:36 -0500 Subject: [PATCH 11/14] linter fixes --- .../management/commands/populate_domain_request_dates.py | 8 ++++---- src/registrar/tests/test_views_requests_json.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index cef140cdd..3bbe6b306 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -11,7 +11,7 @@ class Command(BaseCommand, PopulateScriptTemplate): help = "Loops through each domain request object and populates the last_status_update and first_submitted_date" def handle(self, **kwargs): - """Loops through each DomainRequest object and populates + """Loops through each DomainRequest object and populates its last_status_update and first_submitted_date values""" self.mass_update_records(DomainRequest, None, ["last_status_update", "first_submitted_date"]) @@ -34,12 +34,12 @@ def update_record(self, record: DomainRequest): break logger.info( - f"""{TerminalColors.OKCYAN}Updating {record} => - first submitted date: {record.first_submitted_date}, + f"""{TerminalColors.OKCYAN}Updating {record} => + first submitted date: {record.first_submitted_date}, last status update: {record.last_status_update}{TerminalColors.ENDC} """ ) def should_skip_record(self, record) -> bool: # make sure the record had some kind of history - return not LogEntry.objects.filter(object_pk=record.pk).exists() \ No newline at end of file + return not LogEntry.objects.filter(object_pk=record.pk).exists() diff --git a/src/registrar/tests/test_views_requests_json.py b/src/registrar/tests/test_views_requests_json.py index fd44bdc31..07a65b586 100644 --- a/src/registrar/tests/test_views_requests_json.py +++ b/src/registrar/tests/test_views_requests_json.py @@ -287,7 +287,7 @@ def test_pagination(self): def test_sorting(self): """test that sorting works properly on the result set""" - response = self.app.get(reverse("get_domain_requests_json"), + response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"}) self.assertEqual(response.status_code, 200) data = response.json @@ -307,7 +307,7 @@ def test_sorting(self): def test_filter_approved_excluded(self): """test that approved requests are excluded from result set.""" # sort in reverse chronological order of submission date, since most recent request is approved - response = self.app.get(reverse("get_domain_requests_json"), + response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"}) self.assertEqual(response.status_code, 200) data = response.json From 635e6e67351594a9242d6d704a94bbc63a91918d Mon Sep 17 00:00:00 2001 From: matthewswspence Date: Wed, 28 Aug 2024 09:32:12 -0500 Subject: [PATCH 12/14] linter fixes --- .../commands/populate_domain_request_dates.py | 6 +++--- .../0119_add_domainrequest_submission_dates.py | 6 +++++- src/registrar/tests/test_views_requests_json.py | 10 ++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/registrar/management/commands/populate_domain_request_dates.py b/src/registrar/management/commands/populate_domain_request_dates.py index 3bbe6b306..d975a035d 100644 --- a/src/registrar/management/commands/populate_domain_request_dates.py +++ b/src/registrar/management/commands/populate_domain_request_dates.py @@ -22,14 +22,14 @@ def update_record(self, record: DomainRequest): audit_log_entries = LogEntry.objects.filter(object_pk=record.pk).order_by("-timestamp") # Loop through logs in descending order to find most recent status change for log_entry in audit_log_entries: - if 'status' in log_entry.changes_dict: + if "status" in log_entry.changes_dict: record.last_status_update = log_entry.timestamp.date() break # Loop through logs in ascending order to find first submission for log_entry in audit_log_entries.reverse(): - status = log_entry.changes_dict.get('status') - if status and status[1] == 'submitted': + status = log_entry.changes_dict.get("status") + if status and status[1] == "submitted": record.first_submitted_date = log_entry.timestamp.date() break diff --git a/src/registrar/migrations/0119_add_domainrequest_submission_dates.py b/src/registrar/migrations/0119_add_domainrequest_submission_dates.py index ea209626e..35897e302 100644 --- a/src/registrar/migrations/0119_add_domainrequest_submission_dates.py +++ b/src/registrar/migrations/0119_add_domainrequest_submission_dates.py @@ -26,7 +26,11 @@ class Migration(migrations.Migration): model_name="domainrequest", name="first_submitted_date", field=models.DateField( - blank=True, default=None, help_text="Date initially submitted", null=True, verbose_name="first submitted on" + blank=True, + default=None, + help_text="Date initially submitted", + null=True, + verbose_name="first submitted on", ), ), migrations.AddField( diff --git a/src/registrar/tests/test_views_requests_json.py b/src/registrar/tests/test_views_requests_json.py index 07a65b586..20a4069f7 100644 --- a/src/registrar/tests/test_views_requests_json.py +++ b/src/registrar/tests/test_views_requests_json.py @@ -287,8 +287,9 @@ def test_pagination(self): def test_sorting(self): """test that sorting works properly on the result set""" - response = self.app.get(reverse("get_domain_requests_json"), - {"sort_by": "last_submitted_date", "order": "desc"}) + response = self.app.get( + reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"} + ) self.assertEqual(response.status_code, 200) data = response.json @@ -307,8 +308,9 @@ def test_sorting(self): def test_filter_approved_excluded(self): """test that approved requests are excluded from result set.""" # sort in reverse chronological order of submission date, since most recent request is approved - response = self.app.get(reverse("get_domain_requests_json"), - {"sort_by": "last_submitted_date", "order": "desc"}) + response = self.app.get( + reverse("get_domain_requests_json"), {"sort_by": "last_submitted_date", "order": "desc"} + ) self.assertEqual(response.status_code, 200) data = response.json From ba1553a936aedf26c3d95177e1dddf73ad77a729 Mon Sep 17 00:00:00 2001 From: Matthew Spence Date: Thu, 29 Aug 2024 11:49:45 -0500 Subject: [PATCH 13/14] add new dates to csv export --- src/registrar/utility/csv_export.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index 6f8510418..7ca3b7e97 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -1235,7 +1235,9 @@ def parse_row(cls, columns, model): "State/territory": model.get("state_territory"), "Request purpose": model.get("purpose"), "CISA regional representative": model.get("cisa_representative_email"), - "Submitted at": model.get("last_submitted_date"), + "Last submitted date": model.get("last_submitted_date"), + "First submitted date": model.get("first_submitted_date"), + "Last status update": model.get("last_status_update"), } row = [FIELDS.get(column, "") for column in columns] @@ -1304,7 +1306,9 @@ def get_columns(cls): """ return [ "Domain request", - "Submitted at", + "Last submitted date", + "First submitted date", + "Last status update", "Status", "Domain type", "Federal type", From fc17854529ecdebd544ba717a67bb979948f069f Mon Sep 17 00:00:00 2001 From: Matthew Spence Date: Thu, 29 Aug 2024 11:52:04 -0500 Subject: [PATCH 14/14] correct migration doc --- docs/operations/data_migration.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/docs/operations/data_migration.md b/docs/operations/data_migration.md index ed2f643d5..5914eb179 100644 --- a/docs/operations/data_migration.md +++ b/docs/operations/data_migration.md @@ -834,12 +834,7 @@ Example: `cf ssh getgov-za` ```/tmp/lifecycle/shell``` #### Step 4: Running the script -```./manage.py populate_domain_request_dates --debug``` +```./manage.py populate_domain_request_dates``` ### Running locally -```docker-compose exec app ./manage.py populate_domain_request_dates --debug``` - -##### Optional parameters -| | Parameter | Description | -|:-:|:-------------------------- |:----------------------------------------------------------------------------| -| 1 | **debug** | Increases logging detail. Defaults to False. | +```docker-compose exec app ./manage.py populate_domain_request_dates```