From 3bf4807d8c4389f2773e6b364bfdce47d7c8a097 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 12 Mar 2025 14:50:14 -0600 Subject: [PATCH 01/36] Add script to update public contacts --- .../update_default_public_contacts.py | 36 +++++++++++++++++++ .../commands/utility/terminal_helper.py | 9 ++--- src/registrar/models/domain.py | 19 +++++----- 3 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 src/registrar/management/commands/update_default_public_contacts.py diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py new file mode 100644 index 000000000..e036a460d --- /dev/null +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -0,0 +1,36 @@ +import logging +from django.core.management import BaseCommand +from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalHelper +from registrar.models import PublicContact, Domain +from django.db.models import Q + +from registrar.utility.enums import DefaultEmail + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand, PopulateScriptTemplate): + help = "Loops through each default PublicContact and updates some values on each" + + def handle(self, **kwargs): + """Loops through each valid User object and updates its verification_type value""" + old_emails = [email for email in DefaultEmail if email != DefaultEmail.PUBLIC_CONTACT_DEFAULT] + filter_condition = {"email__in": old_emails} + fields_to_update = [ + "name", + "street1", + "pc", + "email" + ] + self.mass_update_records(PublicContact, filter_condition, fields_to_update) + + def update_record(self, record: PublicContact): + """Defines how we update the verification_type field""" + record.name = "CSD/CB – Attn: .gov TLD" + record.street1 = "1110 N. Glebe Rd" + record.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT + TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updating default values for '{record}'.") + TerminalHelper.colorful_logger("INFO", "MAGENTA", f"Attempting EPP update.") + # Since this function raises an error, this update will revert on both the model and here + Domain._set_singleton_contact(record, expectedType=record.contact_type) + TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updated record in EPP.") diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 87d9f12e5..6ecf1f82f 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -119,10 +119,11 @@ class PopulateScriptTemplate(ABC): readable_class_name = self.get_class_name(object_class) # for use in the execution prompt. - proposed_changes = f"""==Proposed Changes== - Number of {readable_class_name} objects to change: {len(records)} - These fields will be updated on each record: {fields_to_update} - """ + proposed_changes = ( + "==Proposed Changes==\n" + f"Number of {readable_class_name} objects to change: {len(records)}\n" + f"These fields will be updated on each record: {fields_to_update}" + ) if verbose: proposed_changes = f"""{proposed_changes} diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 410d763ba..b21576c83 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -870,7 +870,8 @@ class Domain(TimeStampedModel, DomainHelper): logger.error("Error changing to new registrant error code is %s, error is %s" % (e.code, e)) # TODO-error handling better here? - def _set_singleton_contact(self, contact: PublicContact, expectedType: str): # noqa + @classmethod + def _set_singleton_contact(cls, contact: PublicContact, expectedType: str): # noqa """Sets the contacts by adding them to the registry as new contacts, updates the contact if it is already in epp, deletes any additional contacts of the matching type for this domain @@ -889,12 +890,12 @@ class Domain(TimeStampedModel, DomainHelper): # domain and type but a different id # like in highlander where there can only be one duplicate_contacts = PublicContact.objects.exclude(registry_id=contact.registry_id).filter( - domain=self, contact_type=contact.contact_type + domain=cls, contact_type=contact.contact_type ) # if no record exists with this contact type # make contact in registry, duplicate and errors handled there - errorCode = self._make_contact_in_registry(contact) + errorCode = cls._make_contact_in_registry(contact) # contact is already added to the domain, but something may have changed on it alreadyExistsInRegistry = errorCode == ErrorCode.OBJECT_EXISTS @@ -915,11 +916,11 @@ class Domain(TimeStampedModel, DomainHelper): if isRegistrant: # send update domain only for registant contacts existing_contact.delete() - self._add_registrant_to_existing_domain(contact) + cls._add_registrant_to_existing_domain(contact) else: # remove the old contact and add a new one try: - self._update_domain_with_contact(contact=existing_contact, rem=True) + cls._update_domain_with_contact(contact=existing_contact, rem=True) existing_contact.delete() except Exception as err: logger.error("Raising error after removing and adding a new contact") @@ -928,13 +929,13 @@ class Domain(TimeStampedModel, DomainHelper): # update domain with contact or update the contact itself if not isEmptySecurity: if not alreadyExistsInRegistry and not isRegistrant: - self._update_domain_with_contact(contact=contact, rem=False) + cls._update_domain_with_contact(contact=contact, rem=False) # if already exists just update elif alreadyExistsInRegistry: current_contact = PublicContact.objects.filter(registry_id=contact.registry_id).get() if current_contact.email != contact.email: - self._update_epp_contact(contact=contact) + cls._update_epp_contact(contact=contact) else: logger.info("removing security contact and setting default again") @@ -944,10 +945,10 @@ class Domain(TimeStampedModel, DomainHelper): # don't let user delete the default without adding a new email if current_contact.email != PublicContact.get_default_security().email: # remove the contact - self._update_domain_with_contact(contact=current_contact, rem=True) + cls._update_domain_with_contact(contact=current_contact, rem=True) current_contact.delete() # add new contact - security_contact = self.get_default_security_contact() + security_contact = cls.get_default_security_contact() security_contact.save() @security_contact.setter # type: ignore From a60703204652df046329d513387f65464e431bb4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 14 Mar 2025 08:53:52 -0600 Subject: [PATCH 02/36] Update update_default_public_contacts.py --- .../management/commands/update_default_public_contacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index e036a460d..1a6f4c217 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -30,7 +30,7 @@ class Command(BaseCommand, PopulateScriptTemplate): record.street1 = "1110 N. Glebe Rd" record.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updating default values for '{record}'.") - TerminalHelper.colorful_logger("INFO", "MAGENTA", f"Attempting EPP update.") + TerminalHelper.colorful_logger("INFO", "MAGENTA", f"Attempting to update record in EPP...") # Since this function raises an error, this update will revert on both the model and here Domain._set_singleton_contact(record, expectedType=record.contact_type) TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updated record in EPP.") From 1fa8b19bbc9384f13713a6db7c55b90d0f60ccd2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 14 Mar 2025 08:56:34 -0600 Subject: [PATCH 03/36] Update update_default_public_contacts.py --- .../management/commands/update_default_public_contacts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 1a6f4c217..a276ba854 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -28,6 +28,7 @@ class Command(BaseCommand, PopulateScriptTemplate): """Defines how we update the verification_type field""" record.name = "CSD/CB – Attn: .gov TLD" record.street1 = "1110 N. Glebe Rd" + record.pc = "22201" record.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updating default values for '{record}'.") TerminalHelper.colorful_logger("INFO", "MAGENTA", f"Attempting to update record in EPP...") From 0fa9ad1546152c09b94bdc1f5c084b9fa1f97d25 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:25:54 -0600 Subject: [PATCH 04/36] Check for default --- .../update_default_public_contacts.py | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index a276ba854..70ea836c5 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -4,6 +4,7 @@ from registrar.management.commands.utility.terminal_helper import PopulateScript from registrar.models import PublicContact, Domain from django.db.models import Q +from registrar.models.utility.generic_helper import normalize_string from registrar.utility.enums import DefaultEmail logger = logging.getLogger(__name__) @@ -14,14 +15,24 @@ class Command(BaseCommand, PopulateScriptTemplate): def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" + + # We should only update DEFAULT records. This means that if all values are not default, + # we should skip as this could lead to data corruption. + # Since we check for all fields, we don't account for casing differences. + self.old_and_new_default_contact_values = { + "name": { + "csd/cb – attn: .gov tld", + "csd/cb – attn: cameron dixon", + "program manager", + "registry customer service", + }, + "street1": {"1110 n. glebe rd", "cisa – ngr stop 0645", "4200 wilson blvd."}, + "pc": {"22201", "20598-0645"}, + "email": {email for email in DefaultEmail}, + } old_emails = [email for email in DefaultEmail if email != DefaultEmail.PUBLIC_CONTACT_DEFAULT] filter_condition = {"email__in": old_emails} - fields_to_update = [ - "name", - "street1", - "pc", - "email" - ] + fields_to_update = ["name", "street1", "pc", "email"] self.mass_update_records(PublicContact, filter_condition, fields_to_update) def update_record(self, record: PublicContact): @@ -35,3 +46,17 @@ class Command(BaseCommand, PopulateScriptTemplate): # Since this function raises an error, this update will revert on both the model and here Domain._set_singleton_contact(record, expectedType=record.contact_type) TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updated record in EPP.") + + def should_skip_record(self, record) -> bool: # noqa + """Skips updating a public contact if it contains different default info.""" + for key, expected_values in self.old_and_new_default_contact_values.items(): + record_field = normalize_string(getattr(record, key)) + if record_field not in expected_values: + message = ( + f"Skipping '{record}' to avoid data corruption. " + f"The field '{key}' does not match the default.\n" + f"Details: DB value - {record_field}, expected value(s) - {expected_values}" + ) + TerminalHelper.colorful_logger("WARNING", "YELLOW", message) + return False + return True From ec97f28442ed19d73678324535c1c6ea54b4c5da Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:35:23 -0600 Subject: [PATCH 05/36] use save instead of set --- .../commands/update_default_public_contacts.py | 7 ++----- .../management/commands/utility/terminal_helper.py | 9 +++++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 70ea836c5..e2878e249 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -32,8 +32,7 @@ class Command(BaseCommand, PopulateScriptTemplate): } old_emails = [email for email in DefaultEmail if email != DefaultEmail.PUBLIC_CONTACT_DEFAULT] filter_condition = {"email__in": old_emails} - fields_to_update = ["name", "street1", "pc", "email"] - self.mass_update_records(PublicContact, filter_condition, fields_to_update) + self.mass_update_records(PublicContact, filter_condition, [], skip_bulk_update=True) def update_record(self, record: PublicContact): """Defines how we update the verification_type field""" @@ -42,9 +41,7 @@ class Command(BaseCommand, PopulateScriptTemplate): record.pc = "22201" record.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updating default values for '{record}'.") - TerminalHelper.colorful_logger("INFO", "MAGENTA", f"Attempting to update record in EPP...") - # Since this function raises an error, this update will revert on both the model and here - Domain._set_singleton_contact(record, expectedType=record.contact_type) + record.save() TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updated record in EPP.") def should_skip_record(self, record) -> bool: # noqa diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 6ecf1f82f..89ab83b21 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -86,7 +86,7 @@ class PopulateScriptTemplate(ABC): """ raise NotImplementedError - def mass_update_records(self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False): + def mass_update_records(self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False, skip_bulk_update=False): """Loops through each valid "object_class" object - specified by filter_conditions - and updates fields defined by fields_to_update using update_record. @@ -105,6 +105,10 @@ class PopulateScriptTemplate(ABC): verbose: Whether to print a detailed run summary *before* run confirmation. Default: False. + + skip_bulk_update: Whether to avoid doing a bulk update or not. + This setting assumes that you are doing a save in the update_record class. + IMPORANT: this setting invalidates 'fields_to_update'. Raises: NotImplementedError: If you do not define update_record before using this function. @@ -155,7 +159,8 @@ class PopulateScriptTemplate(ABC): logger.error(fail_message) # Do a bulk update on the desired field - ScriptDataHelper.bulk_update_fields(object_class, to_update, fields_to_update) + if not skip_bulk_update: + ScriptDataHelper.bulk_update_fields(object_class, to_update, fields_to_update) # Log what happened TerminalHelper.log_script_run_summary( From 0abec1900ff06b517d5d6cc8c9e87fec3fdc033e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:42:57 -0600 Subject: [PATCH 06/36] Script cleanup and lint --- .../update_default_public_contacts.py | 24 ++++++++++++++++--- .../commands/utility/terminal_helper.py | 13 ++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index e2878e249..cab208d69 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -1,4 +1,5 @@ import logging +import argparse from django.core.management import BaseCommand from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalHelper from registrar.models import PublicContact, Domain @@ -13,8 +14,25 @@ logger = logging.getLogger(__name__) class Command(BaseCommand, PopulateScriptTemplate): help = "Loops through each default PublicContact and updates some values on each" + def add_arguments(self, parser): + """Adds command line arguments""" + parser.add_argument( + "--overwrite_updated_contacts", + action=argparse.BooleanOptionalAction, + help=( + "Loops over PublicContacts with the email 'help@get.gov' when enabled." + "Use this setting if the record was updated in the DB but not correctly in EPP." + ), + ) + def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" + overwrite_updated_contacts = kwargs.get("overwrite_updated_contacts") + default_emails = {email for email in DefaultEmail} + + # Don't update records we've already updated + if not overwrite_updated_contacts: + default_emails.remove(DefaultEmail.PUBLIC_CONTACT_DEFAULT) # We should only update DEFAULT records. This means that if all values are not default, # we should skip as this could lead to data corruption. @@ -28,10 +46,10 @@ class Command(BaseCommand, PopulateScriptTemplate): }, "street1": {"1110 n. glebe rd", "cisa – ngr stop 0645", "4200 wilson blvd."}, "pc": {"22201", "20598-0645"}, - "email": {email for email in DefaultEmail}, + "email": default_emails, } - old_emails = [email for email in DefaultEmail if email != DefaultEmail.PUBLIC_CONTACT_DEFAULT] - filter_condition = {"email__in": old_emails} + + filter_condition = {"email__in": default_emails} self.mass_update_records(PublicContact, filter_condition, [], skip_bulk_update=True) def update_record(self, record: PublicContact): diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 89ab83b21..fec21fd4d 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -86,7 +86,9 @@ class PopulateScriptTemplate(ABC): """ raise NotImplementedError - def mass_update_records(self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False, skip_bulk_update=False): + def mass_update_records( + self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False, skip_bulk_update=False + ): """Loops through each valid "object_class" object - specified by filter_conditions - and updates fields defined by fields_to_update using update_record. @@ -105,10 +107,11 @@ class PopulateScriptTemplate(ABC): verbose: Whether to print a detailed run summary *before* run confirmation. Default: False. - - skip_bulk_update: Whether to avoid doing a bulk update or not. - This setting assumes that you are doing a save in the update_record class. - IMPORANT: this setting invalidates 'fields_to_update'. + + skip_bulk_update: Whether to avoid doing a bulk update or not. + This setting assumes that you are doing a save in the update_record class. + IMPORANT: this setting invalidates 'fields_to_update'. + Default: False Raises: NotImplementedError: If you do not define update_record before using this function. From 1377663bf96e5b5c90d1a1757189edfc91f6341f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 14 Mar 2025 11:55:21 -0600 Subject: [PATCH 07/36] unit tests --- .../update_default_public_contacts.py | 4 +- src/registrar/models/domain.py | 21 +- src/registrar/tests/common.py | 18 +- .../tests/test_management_scripts.py | 195 ++++++++++++++++++ 4 files changed, 223 insertions(+), 15 deletions(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index cab208d69..b59ab35a5 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -73,5 +73,5 @@ class Command(BaseCommand, PopulateScriptTemplate): f"Details: DB value - {record_field}, expected value(s) - {expected_values}" ) TerminalHelper.colorful_logger("WARNING", "YELLOW", message) - return False - return True + return True + return False diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index b21576c83..24eca1988 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -870,8 +870,7 @@ class Domain(TimeStampedModel, DomainHelper): logger.error("Error changing to new registrant error code is %s, error is %s" % (e.code, e)) # TODO-error handling better here? - @classmethod - def _set_singleton_contact(cls, contact: PublicContact, expectedType: str): # noqa + def _set_singleton_contact(self, contact: PublicContact, expectedType: str): # noqa """Sets the contacts by adding them to the registry as new contacts, updates the contact if it is already in epp, deletes any additional contacts of the matching type for this domain @@ -880,6 +879,7 @@ class Domain(TimeStampedModel, DomainHelper): which inturn call this function) Will throw error if contact type is not the same as expectType Raises ValueError if expected type doesn't match the contact type""" + if expectedType != contact.contact_type: raise ValueError("Cannot set a contact with a different contact type, expected type was %s" % expectedType) @@ -890,12 +890,11 @@ class Domain(TimeStampedModel, DomainHelper): # domain and type but a different id # like in highlander where there can only be one duplicate_contacts = PublicContact.objects.exclude(registry_id=contact.registry_id).filter( - domain=cls, contact_type=contact.contact_type + domain=self, contact_type=contact.contact_type ) - # if no record exists with this contact type # make contact in registry, duplicate and errors handled there - errorCode = cls._make_contact_in_registry(contact) + errorCode = self._make_contact_in_registry(contact) # contact is already added to the domain, but something may have changed on it alreadyExistsInRegistry = errorCode == ErrorCode.OBJECT_EXISTS @@ -916,11 +915,11 @@ class Domain(TimeStampedModel, DomainHelper): if isRegistrant: # send update domain only for registant contacts existing_contact.delete() - cls._add_registrant_to_existing_domain(contact) + self._add_registrant_to_existing_domain(contact) else: # remove the old contact and add a new one try: - cls._update_domain_with_contact(contact=existing_contact, rem=True) + self._update_domain_with_contact(contact=existing_contact, rem=True) existing_contact.delete() except Exception as err: logger.error("Raising error after removing and adding a new contact") @@ -929,13 +928,13 @@ class Domain(TimeStampedModel, DomainHelper): # update domain with contact or update the contact itself if not isEmptySecurity: if not alreadyExistsInRegistry and not isRegistrant: - cls._update_domain_with_contact(contact=contact, rem=False) + self._update_domain_with_contact(contact=contact, rem=False) # if already exists just update elif alreadyExistsInRegistry: current_contact = PublicContact.objects.filter(registry_id=contact.registry_id).get() if current_contact.email != contact.email: - cls._update_epp_contact(contact=contact) + self._update_epp_contact(contact=contact) else: logger.info("removing security contact and setting default again") @@ -945,10 +944,10 @@ class Domain(TimeStampedModel, DomainHelper): # don't let user delete the default without adding a new email if current_contact.email != PublicContact.get_default_security().email: # remove the contact - cls._update_domain_with_contact(contact=current_contact, rem=True) + self._update_domain_with_contact(contact=current_contact, rem=True) current_contact.delete() # add new contact - security_contact = cls.get_default_security_contact() + security_contact = self.get_default_security_contact() security_contact.save() @security_contact.setter # type: ignore diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 101be51ef..daa231e9d 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -1910,7 +1910,14 @@ class MockEppLib(TestCase): return MagicMock(res_data=[mocked_result]) def mockCreateContactCommands(self, _request, cleaned): - if getattr(_request, "id", None) == "fail" and self.mockedSendFunction.call_count == 3: + ids_to_throw_already_exists = [ + "failAdmin", + "failTech", + "failSec", + "failReg", + "fail" + ] + if getattr(_request, "id", None) in ids_to_throw_already_exists and self.mockedSendFunction.call_count == 3: # use this for when a contact is being updated # sets the second send() to fail raise RegistryError(code=ErrorCode.OBJECT_EXISTS) @@ -1925,7 +1932,14 @@ class MockEppLib(TestCase): return MagicMock(res_data=[self.mockDataInfoHosts]) def mockDeleteContactCommands(self, _request, cleaned): - if getattr(_request, "id", None) == "fail": + ids_to_throw_already_exists = [ + "failAdmin", + "failTech", + "failSec", + "failReg", + "fail" + ] + if getattr(_request, "id", None) in ids_to_throw_already_exists: raise RegistryError(code=ErrorCode.OBJECT_EXISTS) else: return MagicMock( diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index 110feea85..2f6bf95c3 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -32,6 +32,7 @@ from registrar.models import ( Portfolio, Suborganization, ) +from registrar.utility.enums import DefaultEmail import tablib from unittest.mock import patch, call, MagicMock, mock_open from epplibwrapper import commands, common @@ -2506,3 +2507,197 @@ class TestRemovePortfolios(TestCase): # Check that the portfolio was deleted self.assertFalse(Portfolio.objects.filter(organization_name="Test with suborg").exists()) + + +class TestUpdateDefaultPublicContacts(MockEppLib): + """Tests for the update_default_public_contacts management command.""" + + @less_console_noise_decorator + def setUp(self): + """Setup test data with PublicContact records.""" + super().setUp() + self.domain_request = completed_domain_request( + name="testdomain.gov", + status=DomainRequest.DomainRequestStatus.IN_REVIEW, + ) + self.domain_request.approve() + self.domain = self.domain_request.approved_domain + + # 1. PublicContact with all old default values + self.old_default_contact = PublicContact.get_default_administrative() + self.old_default_contact.registry_id = "failAdmin" + self.old_default_contact.name = "CSD/CB – ATTN: Cameron Dixon" + self.old_default_contact.street1 = "CISA – NGR STOP 0645" + self.old_default_contact.pc = "20598-0645" + self.old_default_contact.email = DefaultEmail.OLD_PUBLIC_CONTACT_DEFAULT + self.old_default_contact.domain = self.domain + self.old_default_contact.save() + + # 2. PublicContact with current default email but old values for other fields + self.mixed_default_contact = PublicContact.get_default_technical() + self.mixed_default_contact.registry_id = "failTech" + self.mixed_default_contact.name = "registry customer service" + self.mixed_default_contact.street1 = "4200 Wilson Blvd." + self.mixed_default_contact.pc = "22201" + self.mixed_default_contact.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT + self.mixed_default_contact.domain = self.domain + self.mixed_default_contact.save(skip_epp_save=True) + + # 3. PublicContact with non-default values + self.non_default_contact = PublicContact.get_default_security() + self.non_default_contact.registry_id = "failSec" + self.non_default_contact.name = "Hotdogs" + self.non_default_contact.street1 = "123 hotdog town" + self.non_default_contact.pc = "22111" + self.non_default_contact.email = "thehotdogman@igorville.gov" + self.non_default_contact.domain = self.domain + self.non_default_contact.save(skip_epp_save=True) + + # 4. Create a contact using the default helper function but with old email + self.default_registrant_old_email = PublicContact.get_default_registrant() + self.default_registrant_old_email.registry_id = "failReg" + self.default_registrant_old_email.domain = self.domain + self.default_registrant_old_email.email = DefaultEmail.LEGACY_DEFAULT + self.default_registrant_old_email.save(skip_epp_save=True) + + def tearDown(self): + """Clean up test data.""" + super().tearDown() + PublicContact.objects.all().delete() + Domain.objects.all().delete() + DomainRequest.objects.all().delete() + DomainInformation.objects.all().delete() + User.objects.all().delete() + + @patch("registrar.management.commands.utility.terminal_helper.TerminalHelper.query_yes_no_exit", return_value=True) + @less_console_noise_decorator + def run_update_default_public_contacts(self, mock_prompt, **kwargs): + """Execute the update_default_public_contacts command with options.""" + call_command("update_default_public_contacts", **kwargs) + + @less_console_noise_decorator + def test_updates_old_default_contact(self): + """ + Test that contacts with old default values are updated to new default values. + Also tests for string normalization. + """ + self.run_update_default_public_contacts() + self.old_default_contact.refresh_from_db() + + # Verify updates occurred + self.assertEqual(self.old_default_contact.name, "CSD/CB – Attn: .gov TLD") + self.assertEqual(self.old_default_contact.street1, "1110 N. Glebe Rd") + self.assertEqual(self.old_default_contact.pc, "22201") + self.assertEqual(self.old_default_contact.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) + + # Verify EPP create/update calls were made + expected_update = self._convertPublicContactToEpp( + self.old_default_contact, + disclose_email=True, + createContact=False, + disclose_fields={"email", "voice", "addr"}, + disclose_types={"addr": "loc"}, + ) + self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) + + @less_console_noise_decorator + def test_updates_with_default_contact_values(self): + """ + Test that contacts created from the default helper function with old email are updated. + """ + self.run_update_default_public_contacts() + self.default_registrant_old_email.refresh_from_db() + + # Verify updates occurred + self.assertEqual(self.default_registrant_old_email.name, "CSD/CB – Attn: .gov TLD") + self.assertEqual(self.default_registrant_old_email.street1, "1110 N. Glebe Rd") + self.assertEqual(self.default_registrant_old_email.pc, "22201") + self.assertEqual(self.default_registrant_old_email.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) + + # Verify values match the default + default_admin = PublicContact.get_default_administrative() + self.assertEqual(self.default_registrant_old_email.name, default_admin.name) + self.assertEqual(self.default_registrant_old_email.street1, default_admin.street1) + self.assertEqual(self.default_registrant_old_email.pc, default_admin.pc) + self.assertEqual(self.default_registrant_old_email.email, default_admin.email) + + # Verify EPP create/update calls were made + expected_update = self._convertPublicContactToEpp( + self.default_registrant_old_email, + disclose_email=False, + createContact=False, + disclose_fields={} + ) + self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) + + @less_console_noise_decorator + def test_skips_non_default_contacts(self): + """ + Test that contacts with non-default values are skipped. + """ + original_name = self.non_default_contact.name + original_street1 = self.non_default_contact.street1 + original_pc = self.non_default_contact.pc + original_email = self.non_default_contact.email + + self.run_update_default_public_contacts() + self.non_default_contact.refresh_from_db() + + # Verify no updates occurred + self.assertEqual(self.non_default_contact.name, original_name) + self.assertEqual(self.non_default_contact.street1, original_street1) + self.assertEqual(self.non_default_contact.pc, original_pc) + self.assertEqual(self.non_default_contact.email, original_email) + + # Ensure that the update is still skipped even with the override flag + self.run_update_default_public_contacts(overwrite_updated_contacts=True) + self.non_default_contact.refresh_from_db() + + # Verify no updates occurred + self.assertEqual(self.non_default_contact.name, original_name) + self.assertEqual(self.non_default_contact.street1, original_street1) + self.assertEqual(self.non_default_contact.pc, original_pc) + self.assertEqual(self.non_default_contact.email, original_email) + + + @less_console_noise_decorator + def test_skips_contacts_with_current_default_email_by_default(self): + """ + Test that contacts with the current default email are skipped when not using the override flag. + """ + # Get original values + original_name = self.mixed_default_contact.name + original_street1 = self.mixed_default_contact.street1 + + self.run_update_default_public_contacts() + self.mixed_default_contact.refresh_from_db() + + # Verify no updates occurred + self.assertEqual(self.mixed_default_contact.name, original_name) + self.assertEqual(self.mixed_default_contact.street1, original_street1) + self.assertEqual(self.mixed_default_contact.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) + + @less_console_noise_decorator + def test_updates_with_overwrite_flag(self): + """ + Test that contacts with the current default email are updated when using the override flag. + """ + # Run the command with the override flag + self.run_update_default_public_contacts(overwrite_updated_contacts=True) + self.mixed_default_contact.refresh_from_db() + + # Verify updates occurred + self.assertEqual(self.mixed_default_contact.name, "CSD/CB – Attn: .gov TLD") + self.assertEqual(self.mixed_default_contact.street1, "1110 N. Glebe Rd") + self.assertEqual(self.mixed_default_contact.pc, "22201") + self.assertEqual(self.mixed_default_contact.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) + + # Verify EPP create/update calls were made + expected_update = self._convertPublicContactToEpp( + self.mixed_default_contact, + disclose_email=False, + createContact=False, + disclose_fields={} + ) + print(f"call args: {self.mockedSendFunction.call_args_list}") + self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) From 93029ae78f26285cd824acdf0dc15a6682d865ed Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 14 Mar 2025 14:27:30 -0600 Subject: [PATCH 08/36] unit tests --- src/registrar/tests/common.py | 16 +---- .../tests/test_management_scripts.py | 62 ++++++++----------- 2 files changed, 28 insertions(+), 50 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index daa231e9d..66de70ed4 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -1910,13 +1910,7 @@ class MockEppLib(TestCase): return MagicMock(res_data=[mocked_result]) def mockCreateContactCommands(self, _request, cleaned): - ids_to_throw_already_exists = [ - "failAdmin", - "failTech", - "failSec", - "failReg", - "fail" - ] + ids_to_throw_already_exists = ["failAdmin", "failTech", "failSec", "failReg", "fail"] if getattr(_request, "id", None) in ids_to_throw_already_exists and self.mockedSendFunction.call_count == 3: # use this for when a contact is being updated # sets the second send() to fail @@ -1932,13 +1926,7 @@ class MockEppLib(TestCase): return MagicMock(res_data=[self.mockDataInfoHosts]) def mockDeleteContactCommands(self, _request, cleaned): - ids_to_throw_already_exists = [ - "failAdmin", - "failTech", - "failSec", - "failReg", - "fail" - ] + ids_to_throw_already_exists = ["failAdmin", "failTech", "failSec", "failReg", "fail"] if getattr(_request, "id", None) in ids_to_throw_already_exists: raise RegistryError(code=ErrorCode.OBJECT_EXISTS) else: diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index 2f6bf95c3..d768b2e47 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -2524,41 +2524,40 @@ class TestUpdateDefaultPublicContacts(MockEppLib): self.domain = self.domain_request.approved_domain # 1. PublicContact with all old default values - self.old_default_contact = PublicContact.get_default_administrative() + self.old_default_contact = self.domain.get_default_administrative_contact() self.old_default_contact.registry_id = "failAdmin" self.old_default_contact.name = "CSD/CB – ATTN: Cameron Dixon" self.old_default_contact.street1 = "CISA – NGR STOP 0645" self.old_default_contact.pc = "20598-0645" self.old_default_contact.email = DefaultEmail.OLD_PUBLIC_CONTACT_DEFAULT - self.old_default_contact.domain = self.domain self.old_default_contact.save() # 2. PublicContact with current default email but old values for other fields - self.mixed_default_contact = PublicContact.get_default_technical() + self.mixed_default_contact = self.domain.get_default_technical_contact() self.mixed_default_contact.registry_id = "failTech" + self.mixed_default_contact.save(skip_epp_save=True) self.mixed_default_contact.name = "registry customer service" self.mixed_default_contact.street1 = "4200 Wilson Blvd." self.mixed_default_contact.pc = "22201" self.mixed_default_contact.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT - self.mixed_default_contact.domain = self.domain - self.mixed_default_contact.save(skip_epp_save=True) - + self.mixed_default_contact.save() + # 3. PublicContact with non-default values - self.non_default_contact = PublicContact.get_default_security() + self.non_default_contact = self.domain.get_default_security_contact() self.non_default_contact.registry_id = "failSec" + self.non_default_contact.domain = self.domain + self.non_default_contact.save(skip_epp_save=True) self.non_default_contact.name = "Hotdogs" self.non_default_contact.street1 = "123 hotdog town" self.non_default_contact.pc = "22111" self.non_default_contact.email = "thehotdogman@igorville.gov" - self.non_default_contact.domain = self.domain - self.non_default_contact.save(skip_epp_save=True) + self.non_default_contact.save() - # 4. Create a contact using the default helper function but with old email - self.default_registrant_old_email = PublicContact.get_default_registrant() + # 4. Create a default contact but with an old email + self.default_registrant_old_email = self.domain.get_default_registrant_contact() self.default_registrant_old_email.registry_id = "failReg" - self.default_registrant_old_email.domain = self.domain self.default_registrant_old_email.email = DefaultEmail.LEGACY_DEFAULT - self.default_registrant_old_email.save(skip_epp_save=True) + self.default_registrant_old_email.save() def tearDown(self): """Clean up test data.""" @@ -2575,7 +2574,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): """Execute the update_default_public_contacts command with options.""" call_command("update_default_public_contacts", **kwargs) - @less_console_noise_decorator + # @less_console_noise_decorator def test_updates_old_default_contact(self): """ Test that contacts with old default values are updated to new default values. @@ -2583,18 +2582,17 @@ class TestUpdateDefaultPublicContacts(MockEppLib): """ self.run_update_default_public_contacts() self.old_default_contact.refresh_from_db() - + # Verify updates occurred self.assertEqual(self.old_default_contact.name, "CSD/CB – Attn: .gov TLD") self.assertEqual(self.old_default_contact.street1, "1110 N. Glebe Rd") self.assertEqual(self.old_default_contact.pc, "22201") self.assertEqual(self.old_default_contact.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) - + # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( - self.old_default_contact, + self.old_default_contact, disclose_email=True, - createContact=False, disclose_fields={"email", "voice", "addr"}, disclose_types={"addr": "loc"}, ) @@ -2607,26 +2605,23 @@ class TestUpdateDefaultPublicContacts(MockEppLib): """ self.run_update_default_public_contacts() self.default_registrant_old_email.refresh_from_db() - + # Verify updates occurred self.assertEqual(self.default_registrant_old_email.name, "CSD/CB – Attn: .gov TLD") self.assertEqual(self.default_registrant_old_email.street1, "1110 N. Glebe Rd") self.assertEqual(self.default_registrant_old_email.pc, "22201") self.assertEqual(self.default_registrant_old_email.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) - + # Verify values match the default default_admin = PublicContact.get_default_administrative() self.assertEqual(self.default_registrant_old_email.name, default_admin.name) self.assertEqual(self.default_registrant_old_email.street1, default_admin.street1) self.assertEqual(self.default_registrant_old_email.pc, default_admin.pc) self.assertEqual(self.default_registrant_old_email.email, default_admin.email) - + # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( - self.default_registrant_old_email, - disclose_email=False, - createContact=False, - disclose_fields={} + self.default_registrant_old_email, disclose_email=False, disclose_fields=[] ) self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) @@ -2642,7 +2637,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): self.run_update_default_public_contacts() self.non_default_contact.refresh_from_db() - + # Verify no updates occurred self.assertEqual(self.non_default_contact.name, original_name) self.assertEqual(self.non_default_contact.street1, original_street1) @@ -2652,14 +2647,13 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # Ensure that the update is still skipped even with the override flag self.run_update_default_public_contacts(overwrite_updated_contacts=True) self.non_default_contact.refresh_from_db() - + # Verify no updates occurred self.assertEqual(self.non_default_contact.name, original_name) self.assertEqual(self.non_default_contact.street1, original_street1) self.assertEqual(self.non_default_contact.pc, original_pc) self.assertEqual(self.non_default_contact.email, original_email) - @less_console_noise_decorator def test_skips_contacts_with_current_default_email_by_default(self): """ @@ -2671,7 +2665,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): self.run_update_default_public_contacts() self.mixed_default_contact.refresh_from_db() - + # Verify no updates occurred self.assertEqual(self.mixed_default_contact.name, original_name) self.assertEqual(self.mixed_default_contact.street1, original_street1) @@ -2685,19 +2679,15 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # Run the command with the override flag self.run_update_default_public_contacts(overwrite_updated_contacts=True) self.mixed_default_contact.refresh_from_db() - + # Verify updates occurred self.assertEqual(self.mixed_default_contact.name, "CSD/CB – Attn: .gov TLD") self.assertEqual(self.mixed_default_contact.street1, "1110 N. Glebe Rd") self.assertEqual(self.mixed_default_contact.pc, "22201") self.assertEqual(self.mixed_default_contact.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) - + # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( - self.mixed_default_contact, - disclose_email=False, - createContact=False, - disclose_fields={} + self.mixed_default_contact, disclose_email=False, disclose_fields=[] ) - print(f"call args: {self.mockedSendFunction.call_args_list}") self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) From 6d794d45c569b3e9dfb47df771f03a5babdab40e Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 10:33:51 -0600 Subject: [PATCH 09/36] unit tests --- src/registrar/tests/test_management_scripts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index d768b2e47..e819174e0 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -2621,7 +2621,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( - self.default_registrant_old_email, disclose_email=False, disclose_fields=[] + self.default_registrant_old_email, disclose_email=False, disclose_fields={} ) self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) @@ -2688,6 +2688,6 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( - self.mixed_default_contact, disclose_email=False, disclose_fields=[] + self.mixed_default_contact, disclose_email=False, disclose_fields={} ) self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) From 9d20769e6e859750a902c323998e5863efc451c8 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 10:49:30 -0600 Subject: [PATCH 10/36] Add arg to target a particular domain --- .../commands/update_default_public_contacts.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index b59ab35a5..ed23a3c6e 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -25,9 +25,18 @@ class Command(BaseCommand, PopulateScriptTemplate): ), ) + parser.add_argument( + "--target_domain", + help=( + "Updates the public contact on a given domain name (case insensitive). " + "Use this option to avoid doing a mass-update to every public contact record." + ), + ) + def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" overwrite_updated_contacts = kwargs.get("overwrite_updated_contacts") + target_domain = kwargs.get("target_domain") default_emails = {email for email in DefaultEmail} # Don't update records we've already updated @@ -49,7 +58,10 @@ class Command(BaseCommand, PopulateScriptTemplate): "email": default_emails, } - filter_condition = {"email__in": default_emails} + if not target_domain: + filter_condition = {"email__in": default_emails} + else: + filter_condition = {"email__in": default_emails, "domain__name": target_domain} self.mass_update_records(PublicContact, filter_condition, [], skip_bulk_update=True) def update_record(self, record: PublicContact): From 20bde7df3944198d592c8a10e2c8ebbee704a229 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:57:09 -0600 Subject: [PATCH 11/36] Test performance upgrade --- src/registrar/admin.py | 4 ++- .../update_default_public_contacts.py | 32 ++++++++++++----- .../commands/utility/terminal_helper.py | 15 ++++---- src/registrar/models/public_contact.py | 36 ++++++++++--------- 4 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 343624915..b7172953b 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -4147,11 +4147,13 @@ class PublicContactResource(resources.ModelResource): class PublicContactAdmin(ListHeaderAdmin, ImportExportModelAdmin): """Custom PublicContact admin class.""" - resource_classes = [PublicContactResource] change_form_template = "django/admin/email_clipboard_change_form.html" autocomplete_fields = ["domain"] + list_display = ("registry_id", "contact_type", "domain", "name") + search_fields = ["registry_id", "domain", "name"] + search_help_text = "Search by registry id, domain, or name." def changeform_view(self, request, object_id=None, form_url="", extra_context=None): if extra_context is None: diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index ed23a3c6e..1af0c1a45 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -2,9 +2,8 @@ import logging import argparse from django.core.management import BaseCommand from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalHelper -from registrar.models import PublicContact, Domain -from django.db.models import Q - +from registrar.models import PublicContact +from django.db import transaction from registrar.models.utility.generic_helper import normalize_string from registrar.utility.enums import DefaultEmail @@ -33,6 +32,8 @@ class Command(BaseCommand, PopulateScriptTemplate): ), ) + # print to file setting! + def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" overwrite_updated_contacts = kwargs.get("overwrite_updated_contacts") @@ -57,12 +58,21 @@ class Command(BaseCommand, PopulateScriptTemplate): "pc": {"22201", "20598-0645"}, "email": default_emails, } - + # 16 if not target_domain: filter_condition = {"email__in": default_emails} else: filter_condition = {"email__in": default_emails, "domain__name": target_domain} - self.mass_update_records(PublicContact, filter_condition, [], skip_bulk_update=True) + fields_to_update = ["name", "street1", "pc", "email"] + self.mass_update_records(PublicContact, filter_condition, fields_to_update) + + def bulk_update_fields(self, object_class, to_update, fields_to_update): + with transaction.atomic(): + super().bulk_update_fields(object_class, to_update, fields_to_update) + TerminalHelper.colorful_logger("INFO", "MAGENTA", f"Updating records in EPP...") + for record in to_update: + record.add_to_domain_in_epp() + TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updated '{record}' in EPP.") def update_record(self, record: PublicContact): """Defines how we update the verification_type field""" @@ -71,16 +81,22 @@ class Command(BaseCommand, PopulateScriptTemplate): record.pc = "22201" record.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updating default values for '{record}'.") - record.save() - TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updated record in EPP.") def should_skip_record(self, record) -> bool: # noqa """Skips updating a public contact if it contains different default info.""" + if record.registry_id and len(record.registry_id) < 16: + message = ( + f"Skipping legacy verisign contact '{record}'. " + f"The registry_id field has a length less than 16 characters." + ) + TerminalHelper.colorful_logger("WARNING", "YELLOW", message) + return True + for key, expected_values in self.old_and_new_default_contact_values.items(): record_field = normalize_string(getattr(record, key)) if record_field not in expected_values: message = ( - f"Skipping '{record}' to avoid data corruption. " + f"Skipping '{record}' to avoid potential data corruption. " f"The field '{key}' does not match the default.\n" f"Details: DB value - {record_field}, expected value(s) - {expected_values}" ) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index fec21fd4d..197a44ed3 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -87,7 +87,7 @@ class PopulateScriptTemplate(ABC): raise NotImplementedError def mass_update_records( - self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False, skip_bulk_update=False + self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False ): """Loops through each valid "object_class" object - specified by filter_conditions - and updates fields defined by fields_to_update using update_record. @@ -108,11 +108,6 @@ class PopulateScriptTemplate(ABC): verbose: Whether to print a detailed run summary *before* run confirmation. Default: False. - skip_bulk_update: Whether to avoid doing a bulk update or not. - This setting assumes that you are doing a save in the update_record class. - IMPORANT: this setting invalidates 'fields_to_update'. - Default: False - Raises: NotImplementedError: If you do not define update_record before using this function. TypeError: If custom_filter is not Callable. @@ -162,8 +157,7 @@ class PopulateScriptTemplate(ABC): logger.error(fail_message) # Do a bulk update on the desired field - if not skip_bulk_update: - ScriptDataHelper.bulk_update_fields(object_class, to_update, fields_to_update) + self.bulk_update_fields(object_class, to_update, fields_to_update) # Log what happened TerminalHelper.log_script_run_summary( @@ -175,6 +169,9 @@ class PopulateScriptTemplate(ABC): display_as_str=True, ) + def bulk_update_fields(self, object_class, to_update, fields_to_update): + ScriptDataHelper.bulk_update_fields(object_class, to_update, fields_to_update) + def get_class_name(self, sender) -> str: """Returns the class name that we want to display for the terminal prompt. Example: DomainRequest => "Domain Request" @@ -472,4 +469,4 @@ class TerminalHelper: terminal_color = color colored_message = f"{terminal_color}{message}{TerminalColors.ENDC}" - log_method(colored_message, exc_info=exc_info) + return log_method(colored_message, exc_info=exc_info) diff --git a/src/registrar/models/public_contact.py b/src/registrar/models/public_contact.py index d3167960d..6ca2267f0 100644 --- a/src/registrar/models/public_contact.py +++ b/src/registrar/models/public_contact.py @@ -41,21 +41,6 @@ class PublicContact(TimeStampedModel): TECHNICAL = "tech", "Technical" SECURITY = "security", "Security" - def save(self, *args, **kwargs): - """Save to the registry and also locally in the registrar database.""" - skip_epp_save = kwargs.pop("skip_epp_save", False) - if hasattr(self, "domain") and not skip_epp_save: - match self.contact_type: - case PublicContact.ContactTypeChoices.REGISTRANT: - self.domain.registrant_contact = self - case PublicContact.ContactTypeChoices.ADMINISTRATIVE: - self.domain.administrative_contact = self - case PublicContact.ContactTypeChoices.TECHNICAL: - self.domain.technical_contact = self - case PublicContact.ContactTypeChoices.SECURITY: - self.domain.security_contact = self - super().save(*args, **kwargs) - contact_type = models.CharField( max_length=14, choices=ContactTypeChoices.choices, @@ -91,6 +76,25 @@ class PublicContact(TimeStampedModel): ) pw = models.CharField(null=False, help_text="Contact's authorization code. 16 characters minimum.") + def save(self, *args, **kwargs): + """Save to the registry and also locally in the registrar database.""" + skip_epp_save = kwargs.pop("skip_epp_save", False) + if hasattr(self, "domain") and not skip_epp_save: + self.add_to_domain_in_epp() + super().save(*args, **kwargs) + + def add_to_domain_in_epp(self): + """Adds the current contact to the underlying domain in EPP.""" + match self.contact_type: + case PublicContact.ContactTypeChoices.REGISTRANT: + self.domain.registrant_contact = self + case PublicContact.ContactTypeChoices.ADMINISTRATIVE: + self.domain.administrative_contact = self + case PublicContact.ContactTypeChoices.TECHNICAL: + self.domain.technical_contact = self + case PublicContact.ContactTypeChoices.SECURITY: + self.domain.security_contact = self + def print_contact_info_epp(self): """Prints registry data for this PublicContact for easier debugging""" results = self.domain._request_contact_info(self, get_result_as_dict=True) @@ -172,4 +176,4 @@ class PublicContact(TimeStampedModel): return cls._meta.get_field("registry_id").max_length def __str__(self): - return f"{self.name} <{self.email}>" f"id: {self.registry_id} " f"type: {self.contact_type}" + return self.registry_id From e4c2511ef375d18d93d8bf7c1de87c945b80ec6d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:21:01 -0600 Subject: [PATCH 12/36] script cleanup --- src/registrar/admin.py | 2 +- .../update_default_public_contacts.py | 23 ++++++++----------- .../commands/utility/terminal_helper.py | 1 + 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index b7172953b..1c46e4351 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -4152,7 +4152,7 @@ class PublicContactAdmin(ListHeaderAdmin, ImportExportModelAdmin): change_form_template = "django/admin/email_clipboard_change_form.html" autocomplete_fields = ["domain"] list_display = ("registry_id", "contact_type", "domain", "name") - search_fields = ["registry_id", "domain", "name"] + search_fields = ["registry_id", "domain__name", "name"] search_help_text = "Search by registry id, domain, or name." def changeform_view(self, request, object_id=None, form_url="", extra_context=None): diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 1af0c1a45..496e80790 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -1,7 +1,7 @@ import logging import argparse from django.core.management import BaseCommand -from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalHelper +from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors, TerminalHelper from registrar.models import PublicContact from django.db import transaction from registrar.models.utility.generic_helper import normalize_string @@ -32,8 +32,6 @@ class Command(BaseCommand, PopulateScriptTemplate): ), ) - # print to file setting! - def handle(self, **kwargs): """Loops through each valid User object and updates its verification_type value""" overwrite_updated_contacts = kwargs.get("overwrite_updated_contacts") @@ -63,16 +61,13 @@ class Command(BaseCommand, PopulateScriptTemplate): filter_condition = {"email__in": default_emails} else: filter_condition = {"email__in": default_emails, "domain__name": target_domain} + # This is decorative since we are skipping bulk update fields_to_update = ["name", "street1", "pc", "email"] self.mass_update_records(PublicContact, filter_condition, fields_to_update) - def bulk_update_fields(self, object_class, to_update, fields_to_update): - with transaction.atomic(): - super().bulk_update_fields(object_class, to_update, fields_to_update) - TerminalHelper.colorful_logger("INFO", "MAGENTA", f"Updating records in EPP...") - for record in to_update: - record.add_to_domain_in_epp() - TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updated '{record}' in EPP.") + def bulk_update_fields(self, *args, **kwargs): + """Skip bulk update since we need to manually save each field""" + return None def update_record(self, record: PublicContact): """Defines how we update the verification_type field""" @@ -80,7 +75,9 @@ class Command(BaseCommand, PopulateScriptTemplate): record.street1 = "1110 N. Glebe Rd" record.pc = "22201" record.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT - TerminalHelper.colorful_logger("INFO", "OKCYAN", f"Updating default values for '{record}'.") + logger.info(f"Updating default values for '{record}'.") + record.save() + logger.info(f"Updated '{record}' in EPP.") def should_skip_record(self, record) -> bool: # noqa """Skips updating a public contact if it contains different default info.""" @@ -89,7 +86,7 @@ class Command(BaseCommand, PopulateScriptTemplate): f"Skipping legacy verisign contact '{record}'. " f"The registry_id field has a length less than 16 characters." ) - TerminalHelper.colorful_logger("WARNING", "YELLOW", message) + logger.warning(f"{TerminalColors.OKCYAN}{message}{TerminalColors.ENDC}") return True for key, expected_values in self.old_and_new_default_contact_values.items(): @@ -100,6 +97,6 @@ class Command(BaseCommand, PopulateScriptTemplate): f"The field '{key}' does not match the default.\n" f"Details: DB value - {record_field}, expected value(s) - {expected_values}" ) - TerminalHelper.colorful_logger("WARNING", "YELLOW", message) + logger.warning(f"{TerminalColors.OKCYAN}{message}{TerminalColors.ENDC}") return True return False diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 197a44ed3..3a86ebab0 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -170,6 +170,7 @@ class PopulateScriptTemplate(ABC): ) def bulk_update_fields(self, object_class, to_update, fields_to_update): + """Bulk updates the given fields""" ScriptDataHelper.bulk_update_fields(object_class, to_update, fields_to_update) def get_class_name(self, sender) -> str: From c7146601d35d9fde88ddfec20c47362deedda55c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:38:21 -0600 Subject: [PATCH 13/36] add record counter --- .../commands/update_default_public_contacts.py | 7 +++---- .../management/commands/utility/terminal_helper.py | 9 ++++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 496e80790..9796e0fa2 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -75,9 +75,8 @@ class Command(BaseCommand, PopulateScriptTemplate): record.street1 = "1110 N. Glebe Rd" record.pc = "22201" record.email = DefaultEmail.PUBLIC_CONTACT_DEFAULT - logger.info(f"Updating default values for '{record}'.") record.save() - logger.info(f"Updated '{record}' in EPP.") + logger.info(f"{TerminalColors.OKCYAN}Updated '{record}' in EPP.{TerminalColors.ENDC}") def should_skip_record(self, record) -> bool: # noqa """Skips updating a public contact if it contains different default info.""" @@ -86,7 +85,7 @@ class Command(BaseCommand, PopulateScriptTemplate): f"Skipping legacy verisign contact '{record}'. " f"The registry_id field has a length less than 16 characters." ) - logger.warning(f"{TerminalColors.OKCYAN}{message}{TerminalColors.ENDC}") + logger.warning(f"{TerminalColors.YELLOW}{message}{TerminalColors.ENDC}") return True for key, expected_values in self.old_and_new_default_contact_values.items(): @@ -97,6 +96,6 @@ class Command(BaseCommand, PopulateScriptTemplate): f"The field '{key}' does not match the default.\n" f"Details: DB value - {record_field}, expected value(s) - {expected_values}" ) - logger.warning(f"{TerminalColors.OKCYAN}{message}{TerminalColors.ENDC}") + logger.warning(f"{TerminalColors.YELLOW}{message}{TerminalColors.ENDC}") return True return False diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 3a86ebab0..a77cdaf42 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -87,7 +87,7 @@ class PopulateScriptTemplate(ABC): raise NotImplementedError def mass_update_records( - self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False + self, object_class, filter_conditions, fields_to_update, debug=True, verbose=False, show_record_count=False ): """Loops through each valid "object_class" object - specified by filter_conditions - and updates fields defined by fields_to_update using update_record. @@ -117,13 +117,14 @@ class PopulateScriptTemplate(ABC): # apply custom filter records = self.custom_filter(records) + records_length = len(records) readable_class_name = self.get_class_name(object_class) # for use in the execution prompt. proposed_changes = ( "==Proposed Changes==\n" - f"Number of {readable_class_name} objects to change: {len(records)}\n" + f"Number of {readable_class_name} objects to change: {len(records_length)}\n" f"These fields will be updated on each record: {fields_to_update}" ) @@ -143,9 +144,11 @@ class PopulateScriptTemplate(ABC): to_update: List[object_class] = [] to_skip: List[object_class] = [] failed_to_update: List[object_class] = [] - for record in records: + for i, record in enumerate(records, start=1): try: if not self.should_skip_record(record): + if show_record_count: + logger.info(f"{TerminalColors.BOLD}Record {i}/{records_length}{TerminalColors.ENDC}") self.update_record(record) to_update.append(record) else: From 6d916246e35451ae9881573705b88acf005b6435 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:47:35 -0600 Subject: [PATCH 14/36] Update terminal_helper.py --- src/registrar/management/commands/utility/terminal_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index a77cdaf42..c97b151b7 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -124,7 +124,7 @@ class PopulateScriptTemplate(ABC): # for use in the execution prompt. proposed_changes = ( "==Proposed Changes==\n" - f"Number of {readable_class_name} objects to change: {len(records_length)}\n" + f"Number of {readable_class_name} objects to change: {records_length}\n" f"These fields will be updated on each record: {fields_to_update}" ) From 62cdc337861f290afe6811b433038c733bebf112 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:53:53 -0600 Subject: [PATCH 15/36] show record count --- .../management/commands/update_default_public_contacts.py | 2 +- src/registrar/management/commands/utility/terminal_helper.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 9796e0fa2..6a68ecf1b 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -63,7 +63,7 @@ class Command(BaseCommand, PopulateScriptTemplate): filter_condition = {"email__in": default_emails, "domain__name": target_domain} # This is decorative since we are skipping bulk update fields_to_update = ["name", "street1", "pc", "email"] - self.mass_update_records(PublicContact, filter_condition, fields_to_update) + self.mass_update_records(PublicContact, filter_condition, fields_to_update, show_record_count=True) def bulk_update_fields(self, *args, **kwargs): """Skip bulk update since we need to manually save each field""" diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index c97b151b7..ff057da45 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -107,6 +107,9 @@ class PopulateScriptTemplate(ABC): verbose: Whether to print a detailed run summary *before* run confirmation. Default: False. + + show_record_count: Whether to show a 'Record 1/10' dialog when running update. + Default: False. Raises: NotImplementedError: If you do not define update_record before using this function. From 9f675cee9cca1a05ca4315cdf186ae6d7cf6f73d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 13:56:37 -0600 Subject: [PATCH 16/36] Update admin.py --- src/registrar/admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 1c46e4351..428af3859 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -4154,6 +4154,7 @@ class PublicContactAdmin(ListHeaderAdmin, ImportExportModelAdmin): list_display = ("registry_id", "contact_type", "domain", "name") search_fields = ["registry_id", "domain__name", "name"] search_help_text = "Search by registry id, domain, or name." + list_filter = ("contact_type",) def changeform_view(self, request, object_id=None, form_url="", extra_context=None): if extra_context is None: From 4c75d4eb1443a42ea50cf06c52ad2c21b50409ee Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:01:32 -0600 Subject: [PATCH 17/36] Update terminal_helper.py --- src/registrar/management/commands/utility/terminal_helper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index ff057da45..16f057ce1 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -148,10 +148,10 @@ class PopulateScriptTemplate(ABC): to_skip: List[object_class] = [] failed_to_update: List[object_class] = [] for i, record in enumerate(records, start=1): + if show_record_count: + logger.info(f"{TerminalColors.BOLD}Record {i}/{records_length}{TerminalColors.ENDC}") try: if not self.should_skip_record(record): - if show_record_count: - logger.info(f"{TerminalColors.BOLD}Record {i}/{records_length}{TerminalColors.ENDC}") self.update_record(record) to_update.append(record) else: From 8d6d06c461a91858d7839e87128d6f12f30fa512 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:14:25 -0600 Subject: [PATCH 18/36] lint and fix tests --- .../commands/utility/terminal_helper.py | 2 +- src/registrar/models/public_contact.py | 2 +- src/registrar/tests/common.py | 16 ++++++++++++++-- src/registrar/tests/test_management_scripts.py | 8 ++++---- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 16f057ce1..1fdabac11 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -107,7 +107,7 @@ class PopulateScriptTemplate(ABC): verbose: Whether to print a detailed run summary *before* run confirmation. Default: False. - + show_record_count: Whether to show a 'Record 1/10' dialog when running update. Default: False. diff --git a/src/registrar/models/public_contact.py b/src/registrar/models/public_contact.py index 6ca2267f0..8435bccbe 100644 --- a/src/registrar/models/public_contact.py +++ b/src/registrar/models/public_contact.py @@ -82,7 +82,7 @@ class PublicContact(TimeStampedModel): if hasattr(self, "domain") and not skip_epp_save: self.add_to_domain_in_epp() super().save(*args, **kwargs) - + def add_to_domain_in_epp(self): """Adds the current contact to the underlying domain in EPP.""" match self.contact_type: diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 66de70ed4..d2ab60bf6 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -1910,7 +1910,13 @@ class MockEppLib(TestCase): return MagicMock(res_data=[mocked_result]) def mockCreateContactCommands(self, _request, cleaned): - ids_to_throw_already_exists = ["failAdmin", "failTech", "failSec", "failReg", "fail"] + ids_to_throw_already_exists = [ + "failAdmin1234567", + "failTech12345678", + "failSec123456789", + "failReg123456789", + "fail", + ] if getattr(_request, "id", None) in ids_to_throw_already_exists and self.mockedSendFunction.call_count == 3: # use this for when a contact is being updated # sets the second send() to fail @@ -1926,7 +1932,13 @@ class MockEppLib(TestCase): return MagicMock(res_data=[self.mockDataInfoHosts]) def mockDeleteContactCommands(self, _request, cleaned): - ids_to_throw_already_exists = ["failAdmin", "failTech", "failSec", "failReg", "fail"] + ids_to_throw_already_exists = [ + "failAdmin1234567", + "failTech12345678", + "failSec123456789", + "failReg123456789", + "fail", + ] if getattr(_request, "id", None) in ids_to_throw_already_exists: raise RegistryError(code=ErrorCode.OBJECT_EXISTS) else: diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index e819174e0..af4489b9b 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -2525,7 +2525,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # 1. PublicContact with all old default values self.old_default_contact = self.domain.get_default_administrative_contact() - self.old_default_contact.registry_id = "failAdmin" + self.old_default_contact.registry_id = "failAdmin1234567" self.old_default_contact.name = "CSD/CB – ATTN: Cameron Dixon" self.old_default_contact.street1 = "CISA – NGR STOP 0645" self.old_default_contact.pc = "20598-0645" @@ -2534,7 +2534,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # 2. PublicContact with current default email but old values for other fields self.mixed_default_contact = self.domain.get_default_technical_contact() - self.mixed_default_contact.registry_id = "failTech" + self.mixed_default_contact.registry_id = "failTech12345678" self.mixed_default_contact.save(skip_epp_save=True) self.mixed_default_contact.name = "registry customer service" self.mixed_default_contact.street1 = "4200 Wilson Blvd." @@ -2544,7 +2544,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # 3. PublicContact with non-default values self.non_default_contact = self.domain.get_default_security_contact() - self.non_default_contact.registry_id = "failSec" + self.non_default_contact.registry_id = "failSec123456789" self.non_default_contact.domain = self.domain self.non_default_contact.save(skip_epp_save=True) self.non_default_contact.name = "Hotdogs" @@ -2555,7 +2555,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # 4. Create a default contact but with an old email self.default_registrant_old_email = self.domain.get_default_registrant_contact() - self.default_registrant_old_email.registry_id = "failReg" + self.default_registrant_old_email.registry_id = "failReg123456789" self.default_registrant_old_email.email = DefaultEmail.LEGACY_DEFAULT self.default_registrant_old_email.save() From 710fd395b5b04a8cd857ea250c110c0c8c0cc3ce Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:14:33 -0600 Subject: [PATCH 19/36] Update admin.py --- src/registrar/admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 428af3859..41496823a 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -4147,6 +4147,7 @@ class PublicContactResource(resources.ModelResource): class PublicContactAdmin(ListHeaderAdmin, ImportExportModelAdmin): """Custom PublicContact admin class.""" + resource_classes = [PublicContactResource] change_form_template = "django/admin/email_clipboard_change_form.html" From 6f4f88607a580cd561adac364932cea6105fee33 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:18:36 -0600 Subject: [PATCH 20/36] Add searchbar to hostip --- src/registrar/admin.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 41496823a..644c723fb 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1248,6 +1248,11 @@ class HostIpAdmin(AuditedAdmin, ImportExportModelAdmin): resource_classes = [HostIpResource] model = models.HostIP + search_fields = ["host__name", "address"] + search_help_text = "Search by host name or address." + list_display = ("host", "address",) + + class ContactResource(resources.ModelResource): """defines how each field in the referenced model should be mapped to the corresponding fields in the From ee38897da9acde063260cdea18b7510ad69a8dbc Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 17 Mar 2025 14:59:43 -0600 Subject: [PATCH 21/36] lint --- src/registrar/admin.py | 6 ++++-- .../management/commands/update_default_public_contacts.py | 8 +++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 644c723fb..719510006 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1250,8 +1250,10 @@ class HostIpAdmin(AuditedAdmin, ImportExportModelAdmin): search_fields = ["host__name", "address"] search_help_text = "Search by host name or address." - list_display = ("host", "address",) - + list_display = ( + "host", + "address", + ) class ContactResource(resources.ModelResource): diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 6a68ecf1b..7382ac97e 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -1,9 +1,8 @@ import logging import argparse from django.core.management import BaseCommand -from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors, TerminalHelper +from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors from registrar.models import PublicContact -from django.db import transaction from registrar.models.utility.generic_helper import normalize_string from registrar.utility.enums import DefaultEmail @@ -56,12 +55,11 @@ class Command(BaseCommand, PopulateScriptTemplate): "pc": {"22201", "20598-0645"}, "email": default_emails, } - # 16 if not target_domain: filter_condition = {"email__in": default_emails} else: - filter_condition = {"email__in": default_emails, "domain__name": target_domain} - # This is decorative since we are skipping bulk update + filter_condition = {"email__in": default_emails, "domain__name__iexact": target_domain} + # This variable is decorative since we are skipping bulk update fields_to_update = ["name", "street1", "pc", "email"] self.mass_update_records(PublicContact, filter_condition, fields_to_update, show_record_count=True) From c38b28c4649ed77c61c47cac991abf6ed56e563a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 18 Mar 2025 15:36:48 -0600 Subject: [PATCH 22/36] Update test_management_scripts.py --- .../tests/test_management_scripts.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index af4489b9b..be696ced1 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -2558,6 +2558,9 @@ class TestUpdateDefaultPublicContacts(MockEppLib): self.default_registrant_old_email.registry_id = "failReg123456789" self.default_registrant_old_email.email = DefaultEmail.LEGACY_DEFAULT self.default_registrant_old_email.save() + DF = common.DiscloseField + excluded_disclose_fields = {DF.NOTIFY_EMAIL, DF.VAT, DF.IDENT} + self.all_disclose_fields = {field for field in DF} - excluded_disclose_fields def tearDown(self): """Clean up test data.""" @@ -2592,9 +2595,8 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( self.old_default_contact, - disclose_email=True, - disclose_fields={"email", "voice", "addr"}, - disclose_types={"addr": "loc"}, + disclose=False, + disclose_fields=self.all_disclose_fields - {"email", "voice", "addr"}, ) self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) @@ -2613,15 +2615,15 @@ class TestUpdateDefaultPublicContacts(MockEppLib): self.assertEqual(self.default_registrant_old_email.email, DefaultEmail.PUBLIC_CONTACT_DEFAULT) # Verify values match the default - default_admin = PublicContact.get_default_administrative() - self.assertEqual(self.default_registrant_old_email.name, default_admin.name) - self.assertEqual(self.default_registrant_old_email.street1, default_admin.street1) - self.assertEqual(self.default_registrant_old_email.pc, default_admin.pc) - self.assertEqual(self.default_registrant_old_email.email, default_admin.email) + default_reg = PublicContact.get_default_registrant() + self.assertEqual(self.default_registrant_old_email.name, default_reg.name) + self.assertEqual(self.default_registrant_old_email.street1, default_reg.street1) + self.assertEqual(self.default_registrant_old_email.pc, default_reg.pc) + self.assertEqual(self.default_registrant_old_email.email, default_reg.email) # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( - self.default_registrant_old_email, disclose_email=False, disclose_fields={} + self.default_registrant_old_email, disclose=False, disclose_fields=self.all_disclose_fields ) self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) @@ -2688,6 +2690,6 @@ class TestUpdateDefaultPublicContacts(MockEppLib): # Verify EPP create/update calls were made expected_update = self._convertPublicContactToEpp( - self.mixed_default_contact, disclose_email=False, disclose_fields={} + self.mixed_default_contact, disclose=False, disclose_fields=self.all_disclose_fields ) self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) From 37c3cc821331273df1bfeb59cba5b59e684b24bd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:13:48 -0600 Subject: [PATCH 23/36] Update public_contact.py --- src/registrar/models/public_contact.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/registrar/models/public_contact.py b/src/registrar/models/public_contact.py index 7508e2519..fc0130c54 100644 --- a/src/registrar/models/public_contact.py +++ b/src/registrar/models/public_contact.py @@ -41,6 +41,21 @@ class PublicContact(TimeStampedModel): TECHNICAL = "tech", "Technical" SECURITY = "security", "Security" + def save(self, *args, **kwargs): + """Save to the registry and also locally in the registrar database.""" + skip_epp_save = kwargs.pop("skip_epp_save", False) + if hasattr(self, "domain") and not skip_epp_save: + match self.contact_type: + case PublicContact.ContactTypeChoices.REGISTRANT: + self.domain.registrant_contact = self + case PublicContact.ContactTypeChoices.ADMINISTRATIVE: + self.domain.administrative_contact = self + case PublicContact.ContactTypeChoices.TECHNICAL: + self.domain.technical_contact = self + case PublicContact.ContactTypeChoices.SECURITY: + self.domain.security_contact = self + super().save(*args, **kwargs) + contact_type = models.CharField( max_length=14, choices=ContactTypeChoices.choices, From 0cf55a875f0e4632a1b7d3c701932dff6663e982 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:52:05 -0600 Subject: [PATCH 24/36] add disclose --- src/Pipfile.lock | 782 +++++++++++++++++---------------- src/registrar/models/domain.py | 4 +- src/requirements.txt | 45 +- 3 files changed, 424 insertions(+), 407 deletions(-) diff --git a/src/Pipfile.lock b/src/Pipfile.lock index 76f2c914d..6cec6c03a 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -32,37 +32,37 @@ }, "boto3": { "hashes": [ - "sha256:ba391982f6cada136c5bba99e85d7fe1bc4e157c53a22a78e4aca35d1b39152e", - "sha256:eecef248f8743ab30036cd9c916808a0892fc9036e1a35434d8222060c08bbd2" + "sha256:1545c943f36db41853cdfdb6ff09c4eda9220dd95bd2fae76fc73091603525d1", + "sha256:9b272268794172b0b8bb9fb1f3c470c3b6c0ffb92fbd4882465cc740e40fbdcd" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.91" + "version": "==1.37.18" }, "botocore": { "hashes": [ - "sha256:7b0b9c5954701fff4d2c516918f45641b04ff4ca92bbd9f5b37c0b80f8c14220", - "sha256:93de9d0f52f7e36a2c190d55520d3b2654f32c5a628fdd484bffa00bc7865e1d" + "sha256:99e8eefd5df6347ead15df07ce55f4e62a51ea7b54de1127522a08597923b726", + "sha256:a8b97d217d82b3c4f6bcc906e264df7ebb51e2c6a62b3548a97cd173fb8759a1" ], "markers": "python_version >= '3.8'", - "version": "==1.35.91" + "version": "==1.37.18" }, "cachetools": { "hashes": [ - "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292", - "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a" + "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", + "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==5.5.0" + "version": "==5.5.2" }, "certifi": { "hashes": [ - "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56", - "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db" + "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", + "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" ], "markers": "python_version >= '3.6'", - "version": "==2024.12.14" + "version": "==2025.1.31" }, "cfenv": { "hashes": [ @@ -245,36 +245,44 @@ }, "cryptography": { "hashes": [ - "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", - "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731", - "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", - "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", - "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", - "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c", - "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", - "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", - "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", - "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", - "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", - "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c", - "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", - "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", - "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", - "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", - "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa", - "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", - "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", - "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", - "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", - "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", - "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", - "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", - "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", - "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756", - "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4" + "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390", + "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41", + "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688", + "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5", + "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1", + "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d", + "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7", + "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843", + "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5", + "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c", + "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a", + "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79", + "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6", + "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181", + "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4", + "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5", + "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562", + "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639", + "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922", + "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3", + "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d", + "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471", + "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd", + "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa", + "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb", + "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699", + "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb", + "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa", + "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0", + "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23", + "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9", + "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615", + "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea", + "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7", + "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308" ], "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", - "version": "==44.0.0" + "version": "==44.0.2" }, "defusedxml": { "hashes": [ @@ -349,12 +357,12 @@ }, "django-cors-headers": { "hashes": [ - "sha256:14d76b4b4c8d39375baeddd89e4f08899051eeaf177cb02a29bd6eae8cf63aa8", - "sha256:8edbc0497e611c24d5150e0055d3b178c6534b8ed826fb6f53b21c63f5d48ba3" + "sha256:6fdf31bf9c6d6448ba09ef57157db2268d515d94fc5c89a0a1028e1fc03ee52b", + "sha256:f1c125dcd58479fe7a67fe2499c16ee38b81b397463cf025f0e2c42937421070" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==4.6.0" + "version": "==4.7.0" }, "django-csp": { "hashes": [ @@ -374,12 +382,12 @@ }, "django-import-export": { "hashes": [ - "sha256:91b47c9a2701a5b039667df5c46ee682a41bb224ac215a0e66b177a459e35983", - "sha256:b261f44aedf572a69f975655afba15bff1e354eddd91d9c1bbd32d3cee44168d" + "sha256:5514d09636e84e823a42cd5e79292f70f20d6d2feed117a145f5b64a5b44f168", + "sha256:bd3fe0aa15a2bce9de4be1a2f882e2c4539fdbfdfa16f2052c98dd7aec0f085c" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==4.3.3" + "version": "==4.3.7" }, "django-login-required-middleware": { "hashes": [ @@ -422,31 +430,31 @@ "django" ], "hashes": [ - "sha256:9d2080cf25807a26fc0d4301e2d7b62c64fbf547540f21e3a30cc02bc5fbe948", - "sha256:e068ae3174cef52ba4b95ead22e639056a02465f616e62323e04ae08e86a75a4" + "sha256:03db7ee2d50ec697b68814cd175a3a05a7c7954804e4e419ca8b570dc5a835cf", + "sha256:45bc56f1d53bbc59d8dd69bba97377dd88ec28b8229d81cedbd455b21789445b" ], - "markers": "python_version >= '3.8'", - "version": "==11.2.1" + "markers": "python_version >= '3.9'", + "version": "==14.1.1" }, "faker": { "hashes": [ - "sha256:1c925fc0e86a51fc46648b504078c88d0cd48da1da2595c4e712841cab43a1e4", - "sha256:d30c5f0e2796b8970de68978365247657486eb0311c5abe88d0b895b68dff05d" + "sha256:8955706c56c28099585e9e2b6f814eb0a3a227eb36a2ee3eb9ab577c4764eacc", + "sha256:948bd27706478d3aa0b6f9f58b9f25207098f6ca79852c7b49c44a8ced2bc59b" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==33.1.0" + "markers": "python_version >= '3.9'", + "version": "==37.0.2" }, "fred-epplib": { "git": "https://github.com/cisagov/epplib.git", - "ref": "d56d183f1664f34c40ca9716a3a9a345f0ef561c" + "ref": "9f0fd0e69665001767f15a034c9c0c919dab5cdd" }, "furl": { "hashes": [ - "sha256:5a6188fe2666c484a12159c18be97a1977a71d632ef5bb867ef15f54af39cc4e", - "sha256:9ab425062c4217f9802508e45feb4a83e54324273ac4b202f1850363309666c0" + "sha256:877657501266c929269739fb5f5980534a41abd6bbabcb367c136d1d3b2a6015", + "sha256:da34d0b34e53ffe2d2e6851a7085a05d96922b5b578620a37377ff1dbeeb11c8" ], - "version": "==2.1.3" + "version": "==2.1.4" }, "future": { "hashes": [ @@ -608,155 +616,155 @@ }, "lxml": { "hashes": [ - "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e", - "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229", - "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3", - "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5", - "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70", - "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15", - "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", - "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", - "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22", - "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf", - "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22", - "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", - "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727", - "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e", - "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", - "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f", - "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f", - "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", - "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", - "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de", - "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875", - "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42", - "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e", - "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6", - "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391", - "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc", - "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b", - "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237", - "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", - "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86", - "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f", - "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a", - "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8", - "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", - "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903", - "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03", - "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e", - "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99", - "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7", - "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab", - "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", - "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22", - "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", - "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b", - "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3", - "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be", - "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469", - "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", - "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a", - "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c", - "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a", - "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", - "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94", - "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", - "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", - "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84", - "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c", - "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9", - "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1", - "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", - "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", - "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", - "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21", - "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa", - "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16", - "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d", - "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe", - "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83", - "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba", - "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040", - "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", - "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8", - "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff", - "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2", - "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a", - "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b", - "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce", - "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c", - "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577", - "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", - "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71", - "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512", - "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540", - "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", - "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2", - "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", - "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce", - "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e", - "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2", - "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27", - "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1", - "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d", - "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1", - "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330", - "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920", - "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99", - "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff", - "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18", - "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", - "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c", - "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", - "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080", - "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19", - "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d", - "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70", - "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32", - "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a", - "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2", - "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79", - "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", - "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5", - "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", - "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d", - "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3", - "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b", - "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753", - "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9", - "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", - "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033", - "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", - "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656", - "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab", - "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", - "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", - "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", - "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859", - "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11", - "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c", - "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", - "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005", - "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654", - "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80", - "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e", - "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", - "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7", - "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965", - "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945", - "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8" + "sha256:016b96c58e9a4528219bb563acf1aaaa8bc5452e7651004894a973f03b84ba81", + "sha256:05123fad495a429f123307ac6d8fd6f977b71e9a0b6d9aeeb8f80c017cb17131", + "sha256:057e30d0012439bc54ca427a83d458752ccda725c1c161cc283db07bcad43cf9", + "sha256:06a20d607a86fccab2fc15a77aa445f2bdef7b49ec0520a842c5c5afd8381576", + "sha256:094b28ed8a8a072b9e9e2113a81fda668d2053f2ca9f2d202c2c8c7c2d6516b1", + "sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c", + "sha256:0d6b2fa86becfa81f0a0271ccb9eb127ad45fb597733a77b92e8a35e53414914", + "sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607", + "sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8", + "sha256:133f3493253a00db2c870d3740bc458ebb7d937bd0a6a4f9328373e0db305709", + "sha256:136bf638d92848a939fd8f0e06fcf92d9f2e4b57969d94faae27c55f3d85c05b", + "sha256:155e1a5693cf4b55af652f5c0f78ef36596c7f680ff3ec6eb4d7d85367259b2c", + "sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7", + "sha256:172d65f7c72a35a6879217bcdb4bb11bc88d55fb4879e7569f55616062d387c2", + "sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c", + "sha256:198bb4b4dd888e8390afa4f170d4fa28467a7eaf857f1952589f16cfbb67af27", + "sha256:1b6f92e35e2658a5ed51c6634ceb5ddae32053182851d8cad2a5bc102a359b33", + "sha256:1b92fe86e04f680b848fff594a908edfa72b31bfc3499ef7433790c11d4c8cd8", + "sha256:1bcc211542f7af6f2dfb705f5f8b74e865592778e6cafdfd19c792c244ccce19", + "sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae", + "sha256:203b1d3eaebd34277be06a3eb880050f18a4e4d60861efba4fb946e31071a295", + "sha256:22ec2b3c191f43ed21f9545e9df94c37c6b49a5af0a874008ddc9132d49a2d9c", + "sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b", + "sha256:236610b77589faf462337b3305a1be91756c8abc5a45ff7ca8f245a71c5dab70", + "sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf", + "sha256:2b8969dbc8d09d9cd2ae06362c3bad27d03f433252601ef658a49bd9f2b22d79", + "sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84", + "sha256:2df7ed5edeb6bd5590914cd61df76eb6cce9d590ed04ec7c183cf5509f73530d", + "sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423", + "sha256:2f1be45d4c15f237209bbf123a0e05b5d630c8717c42f59f31ea9eae2ad89394", + "sha256:2f23cf50eccb3255b6e913188291af0150d89dab44137a69e14e4dcb7be981f1", + "sha256:3031e4c16b59424e8d78522c69b062d301d951dc55ad8685736c3335a97fc270", + "sha256:33e06717c00c788ab4e79bc4726ecc50c54b9bfb55355eae21473c145d83c2d2", + "sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b", + "sha256:3715cdf0dd31b836433af9ee9197af10e3df41d273c19bb249230043667a5dfd", + "sha256:3bb8149840daf2c3f97cebf00e4ed4a65a0baff888bf2605a8d0135ff5cf764e", + "sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f", + "sha256:3d68eeef7b4d08a25e51897dac29bcb62aba830e9ac6c4e3297ee7c6a0cf6439", + "sha256:3dddf0fb832486cc1ea71d189cb92eb887826e8deebe128884e15020bb6e3f61", + "sha256:3edbb9c9130bac05d8c3fe150c51c337a471cc7fdb6d2a0a7d3a88e88a829314", + "sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0", + "sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2", + "sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8", + "sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0", + "sha256:45fbb70ccbc8683f2fb58bea89498a7274af1d9ec7995e9f4af5604e028233fc", + "sha256:4867361c049761a56bd21de507cab2c2a608c55102311d142ade7dab67b34f32", + "sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499", + "sha256:4b0d5cdba1b655d5b18042ac9c9ff50bda33568eb80feaaca4fc237b9c4fbfde", + "sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468", + "sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe", + "sha256:514fe78fc4b87e7a7601c92492210b20a1b0c6ab20e71e81307d9c2e377c64de", + "sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877", + "sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5", + "sha256:52d82b0d436edd6a1d22d94a344b9a58abd6c68c357ed44f22d4ba8179b37629", + "sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7", + "sha256:585c4dc429deebc4307187d2b71ebe914843185ae16a4d582ee030e6cfbb4d8a", + "sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5", + "sha256:5881aaa4bf3a2d086c5f20371d3a5856199a0d8ac72dd8d0dbd7a2ecfc26ab73", + "sha256:5885bc586f1edb48e5d68e7a4b4757b5feb2a496b64f462b4d65950f5af3364f", + "sha256:5a11b16a33656ffc43c92a5343a28dc71eefe460bcc2a4923a96f292692709f6", + "sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac", + "sha256:5be8f5e4044146a69c96077c7e08f0709c13a314aa5315981185c1f00235fe65", + "sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519", + "sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5", + "sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8", + "sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d", + "sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51", + "sha256:6c4dd3bfd0c82400060896717dd261137398edb7e524527438c54a8c34f736bf", + "sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20", + "sha256:7512b4d0fc5339d5abbb14d1843f70499cab90d0b864f790e73f780f041615d7", + "sha256:75fa3d6946d317ffc7016a6fcc44f42db6d514b7fdb8b4b28cbe058303cb6e53", + "sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e", + "sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5", + "sha256:7aae7a3d63b935babfdc6864b31196afd5145878ddd22f5200729006366bc4d5", + "sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9", + "sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2", + "sha256:7eda194dd46e40ec745bf76795a7cccb02a6a41f445ad49d3cf66518b0bd9cff", + "sha256:82a4bb10b0beef1434fb23a09f001ab5ca87895596b4581fd53f1e5145a8934a", + "sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c", + "sha256:88b72eb7222d918c967202024812c2bfb4048deeb69ca328363fb8e15254c549", + "sha256:89934f9f791566e54c1d92cdc8f8fd0009447a5ecdb1ec6b810d5f8c4955f6be", + "sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9", + "sha256:8ffb141361108e864ab5f1813f66e4e1164181227f9b1f105b042729b6c15125", + "sha256:8fffc08de02071c37865a155e5ea5fce0282e1546fd5bde7f6149fcaa32558ac", + "sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332", + "sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2", + "sha256:96eef5b9f336f623ffc555ab47a775495e7e8846dde88de5f941e2906453a1ce", + "sha256:a0611da6b07dd3720f492db1b463a4d1175b096b49438761cc9f35f0d9eaaef5", + "sha256:a091026c3bf7519ab1e64655a3f52a59ad4a4e019a6f830c24d6430695b1cf6a", + "sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23", + "sha256:a243132767150a44e6a93cd1dde41010036e1cbc63cc3e9fe1712b277d926ce3", + "sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c", + "sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414", + "sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b", + "sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9", + "sha256:a83d3adea1e0ee36dac34627f78ddd7f093bb9cfc0a8e97f1572a949b695cb98", + "sha256:a8ade0363f776f87f982572c2860cc43c65ace208db49c76df0a21dde4ddd16e", + "sha256:aa59974880ab5ad8ef3afaa26f9bda148c5f39e06b11a8ada4660ecc9fb2feb3", + "sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406", + "sha256:aaca5a812f050ab55426c32177091130b1e49329b3f002a32934cd0245571307", + "sha256:ae82fce1d964f065c32c9517309f0c7be588772352d2f40b1574a214bd6e6098", + "sha256:aed57b541b589fa05ac248f4cb1c46cbb432ab82cbd467d1c4f6a2bdc18aecf9", + "sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725", + "sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd", + "sha256:b2aca14c235c7a08558fe0a4786a1a05873a01e86b474dfa8f6df49101853a4e", + "sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322", + "sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd", + "sha256:bfbbab9316330cf81656fed435311386610f78b6c93cc5db4bebbce8dd146675", + "sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e", + "sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a", + "sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1", + "sha256:c5ae125276f254b01daa73e2c103363d3e99e3e10505686ac7d9d2442dd4627a", + "sha256:c6aacf00d05b38a5069826e50ae72751cb5bc27bdc4d5746203988e429b385bb", + "sha256:c76722b5ed4a31ba103e0dc77ab869222ec36efe1a614e42e9bcea88a36186fe", + "sha256:c809eef167bf4a57af4b03007004896f5c60bd38dc3852fcd97a26eae3d4c9e6", + "sha256:c92ea6d9dd84a750b2bae72ff5e8cf5fdd13e58dda79c33e057862c29a8d5b50", + "sha256:cb659702a45136c743bc130760c6f137870d4df3a9e14386478b8a0511abcfca", + "sha256:ce0930a963ff593e8bb6fda49a503911accc67dee7e5445eec972668e672a0f0", + "sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78", + "sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367", + "sha256:d2a3e412ce1849be34b45922bfef03df32d1410a06d1cdeb793a343c2f1fd666", + "sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645", + "sha256:db0c742aad702fd5d0c6611a73f9602f20aec2007c102630c06d7633d9c8f09a", + "sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3", + "sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252", + "sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79", + "sha256:df7e5edac4778127f2bf452e0721a58a1cfa4d1d9eac63bdd650535eb8543615", + "sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f", + "sha256:e3c623923967f3e5961d272718655946e5322b8d058e094764180cdee7bab1af", + "sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c", + "sha256:e8e0d177b1fe251c3b1b914ab64135475c5273c8cfd2857964b2e3bb0fe196a7", + "sha256:ef45f31aec9be01379fc6c10f1d9c677f032f2bac9383c827d44f620e8a88407", + "sha256:f1208c1c67ec9e151d78aa3435aa9b08a488b53d9cfac9b699f15255a3461ef2", + "sha256:f12582b8d3b4c6be1d298c49cb7ae64a3a73efaf4c2ab4e37db182e3545815ac", + "sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9", + "sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5", + "sha256:f7b64fcd670bca8800bc10ced36620c6bbb321e7bc1214b9c0c0df269c1dddc2", + "sha256:fb7c61d4be18e930f75948705e9718618862e6fc2ed0d7159b2262be73f167a2" ], "markers": "python_version >= '3.6'", - "version": "==5.3.0" + "version": "==5.3.1" }, "mako": { "hashes": [ - "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627", - "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8" + "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1", + "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac" ], "markers": "python_version >= '3.8'", - "version": "==1.3.8" + "version": "==1.3.9" }, "markupsafe": { "hashes": [ @@ -827,11 +835,11 @@ }, "marshmallow": { "hashes": [ - "sha256:bcaf2d6fd74fb1459f8450e85d994997ad3e70036452cbfa4ab685acb19479b3", - "sha256:c448ac6455ca4d794773f00bae22c2f351d62d739929f761dce5eacb5c468d7f" + "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", + "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6" ], "markers": "python_version >= '3.9'", - "version": "==3.23.2" + "version": "==3.26.1" }, "oic": { "hashes": [ @@ -859,10 +867,10 @@ }, "phonenumberslite": { "hashes": [ - "sha256:02da5e78c67b213bae95afd6289f40486c93e302e518769911dfa5e7287ddeee", - "sha256:dfa44a4bae2e46d737ae5301cb96b14cdcbf45063236c74c6ddb08f5fd471b0d" + "sha256:85e9d75b8de1a5eeb716dc99b3937743cbd5265182dc5c1132204aa491ae0724", + "sha256:cda36bc06b5bd7c042d913408f9ca37c5e6c872f851393f15ad1e9a243e5c753" ], - "version": "==8.13.52" + "version": "==9.0.1" }, "psycopg2-binary": { "hashes": [ @@ -878,6 +886,7 @@ "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", @@ -948,50 +957,47 @@ }, "pycryptodomex": { "hashes": [ - "sha256:0df2608682db8279a9ebbaf05a72f62a321433522ed0e499bc486a6889b96bf3", - "sha256:103c133d6cd832ae7266feb0a65b69e3a5e4dbbd6f3a3ae3211a557fd653f516", - "sha256:1233443f19d278c72c4daae749872a4af3787a813e05c3561c73ab0c153c7b0f", - "sha256:222d0bd05381dd25c32dd6065c071ebf084212ab79bab4599ba9e6a3e0009e6c", - "sha256:27e84eeff24250ffec32722334749ac2a57a5fd60332cd6a0680090e7c42877e", - "sha256:34325b84c8b380675fd2320d0649cdcbc9cf1e0d1526edbe8fce43ed858cdc7e", - "sha256:365aa5a66d52fd1f9e0530ea97f392c48c409c2f01ff8b9a39c73ed6f527d36c", - "sha256:3efddfc50ac0ca143364042324046800c126a1d63816d532f2e19e6f2d8c0c31", - "sha256:46eb1f0c8d309da63a2064c28de54e5e614ad17b7e2f88df0faef58ce192fc7b", - "sha256:5241bdb53bcf32a9568770a6584774b1b8109342bd033398e4ff2da052123832", - "sha256:52e23a0a6e61691134aa8c8beba89de420602541afaae70f66e16060fdcd677e", - "sha256:56435c7124dd0ce0c8bdd99c52e5d183a0ca7fdcd06c5d5509423843f487dd0b", - "sha256:5823d03e904ea3e53aebd6799d6b8ec63b7675b5d2f4a4bd5e3adcb512d03b37", - "sha256:65d275e3f866cf6fe891411be9c1454fb58809ccc5de6d3770654c47197acd65", - "sha256:770d630a5c46605ec83393feaa73a9635a60e55b112e1fb0c3cea84c2897aa0a", - "sha256:77ac2ea80bcb4b4e1c6a596734c775a1615d23e31794967416afc14852a639d3", - "sha256:7a1058e6dfe827f4209c5cae466e67610bcd0d66f2f037465daa2a29d92d952b", - "sha256:8a9d8342cf22b74a746e3c6c9453cb0cfbb55943410e3a2619bd9164b48dc9d9", - "sha256:8ef436cdeea794015263853311f84c1ff0341b98fc7908e8a70595a68cefd971", - "sha256:9aa0cf13a1a1128b3e964dc667e5fe5c6235f7d7cfb0277213f0e2a783837cc2", - "sha256:9ba09a5b407cbb3bcb325221e346a140605714b5e880741dc9a1e9ecf1688d42", - "sha256:a192fb46c95489beba9c3f002ed7d93979423d1b2a53eab8771dbb1339eb3ddd", - "sha256:a3d77919e6ff56d89aada1bd009b727b874d464cb0e2e3f00a49f7d2e709d76e", - "sha256:b0e9765f93fe4890f39875e6c90c96cb341767833cfa767f41b490b506fa9ec0", - "sha256:bbb07f88e277162b8bfca7134b34f18b400d84eac7375ce73117f865e3c80d4c", - "sha256:c07e64867a54f7e93186a55bec08a18b7302e7bee1b02fd84c6089ec215e723a", - "sha256:cc7e111e66c274b0df5f4efa679eb31e23c7545d702333dfd2df10ab02c2a2ce", - "sha256:da76ebf6650323eae7236b54b1b1f0e57c16483be6e3c1ebf901d4ada47563b6", - "sha256:dbeb84a399373df84a69e0919c1d733b89e049752426041deeb30d68e9867822", - "sha256:e859e53d983b7fe18cb8f1b0e29d991a5c93be2c8dd25db7db1fe3bd3617f6f9", - "sha256:ef046b2e6c425647971b51424f0f88d8a2e0a2a63d3531817968c42078895c00", - "sha256:feaecdce4e5c0045e7a287de0c4351284391fe170729aa9182f6bd967631b3a8" + "sha256:140b27caa68a36d0501b05eb247bd33afa5f854c1ee04140e38af63c750d4e39", + "sha256:1a8b0c5ba061ace4bcd03496d42702c3927003db805b8ec619ea6506080b381d", + "sha256:1ab0d89d1761959b608952c7b347b0e76a32d1a5bb278afbaa10a7f3eaef9a0a", + "sha256:259664c4803a1fa260d5afb322972813c5fe30ea8b43e54b03b7e3a27b30856b", + "sha256:276be1ed006e8fd01bba00d9bd9b60a0151e478033e86ea1cb37447bbc057edc", + "sha256:2cac9ed5c343bb3d0075db6e797e6112514764d08d667c74cb89b931aac9dddd", + "sha256:41673e5cc39a8524557a0472077635d981172182c9fe39ce0b5f5c19381ffaff", + "sha256:5ac608a6dce9418d4f300fab7ba2f7d499a96b462f2b9b5c90d8d994cd36dcad", + "sha256:5bf3ce9211d2a9877b00b8e524593e2209e370a287b3d5e61a8c45f5198487e2", + "sha256:5e64164f816f5e43fd69f8ed98eb28f98157faf68208cd19c44ed9d8e72d33e8", + "sha256:5ebc09b7d8964654aaf8a4f5ac325f2b0cc038af9bea12efff0cd4a5bb19aa42", + "sha256:644834b1836bb8e1d304afaf794d5ae98a1d637bd6e140c9be7dd192b5374811", + "sha256:684cb57812cd243217c3d1e01a720c5844b30f0b7b64bb1a49679f7e1e8a54ac", + "sha256:7127d9de3c7ce20339e06bcd4f16f1a1a77f1471bcf04e3b704306dde101b719", + "sha256:72c506aba3318505dbeecf821ed7b9a9f86f422ed085e2d79c4fba0ae669920a", + "sha256:7a24f681365ec9757ccd69b85868bbd7216ba451d0f86f6ea0eed75eeb6975db", + "sha256:7cd39f7a110c1ab97ce9ee3459b8bc615920344dc00e56d1b709628965fba3f2", + "sha256:813e57da5ceb4b549bab96fa548781d9a63f49f1d68fdb148eeac846238056b7", + "sha256:a1da61bacc22f93a91cbe690e3eb2022a03ab4123690ab16c46abb693a9df63d", + "sha256:aef4590263b9f2f6283469e998574d0bd45c14fb262241c27055b82727426157", + "sha256:b3746dedf74787da43e4a2f85bd78f5ec14d2469eb299ddce22518b3891f16ea", + "sha256:bfe4fe3233ef3e58028a3ad8f28473653b78c6d56e088ea04fe7550c63d4d16b", + "sha256:c8cffb03f5dee1026e3f892f7cffd79926a538c67c34f8b07c90c0bd5c834e27", + "sha256:d7beeacb5394765aa8dabed135389a11ee322d3ee16160d178adc7f8ee3e1f65", + "sha256:e4eaaf6163ff13788c1f8f615ad60cdc69efac6d3bf7b310b21e8cfe5f46c801", + "sha256:eac39e237d65981554c2d4c6668192dc7051ad61ab5fc383ed0ba049e4007ca2", + "sha256:ee75067b35c93cc18b38af47b7c0664998d8815174cfc66dd00ea1e244eb27e6", + "sha256:f005de31efad6f9acefc417296c641f13b720be7dbfec90edeaca601c0fab048", + "sha256:ff46212fda7ee86ec2f4a64016c994e8ad80f11ef748131753adb67e9b722ebd" ], "index": "pypi", - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==3.21.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", + "version": "==3.22.0" }, "pydantic": { "hashes": [ - "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d", - "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06" + "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", + "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236" ], "markers": "python_version >= '3.8'", - "version": "==2.10.4" + "version": "==2.10.6" }, "pydantic-core": { "hashes": [ @@ -1101,11 +1107,11 @@ }, "pydantic-settings": { "hashes": [ - "sha256:10c9caad35e64bfb3c2fbf70a078c0e25cc92499782e5200747f942a065dec93", - "sha256:590be9e6e24d06db33a4262829edef682500ef008565a969c73d39d5f8bfb3fd" + "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", + "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585" ], "markers": "python_version >= '3.8'", - "version": "==2.7.1" + "version": "==2.8.1" }, "pyjwkest": { "hashes": [ @@ -1150,19 +1156,19 @@ }, "s3transfer": { "hashes": [ - "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", - "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" + "sha256:559f161658e1cf0a911f45940552c696735f5c74e64362e515f333ebed87d679", + "sha256:ac265fa68318763a03bf2dc4f39d5cbd6a9e178d81cc9483ad27da33637e320d" ], "markers": "python_version >= '3.8'", - "version": "==0.10.4" + "version": "==0.11.4" }, "setuptools": { "hashes": [ - "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", - "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" + "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945", + "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c" ], "markers": "python_version >= '3.9'", - "version": "==75.6.0" + "version": "==77.0.3" }, "six": { "hashes": [ @@ -1182,11 +1188,11 @@ }, "tablib": { "hashes": [ - "sha256:9a6930037cfe0f782377963ca3f2b1dae3fd4cdbf0883848f22f1447e7bb718b", - "sha256:f9db84ed398df5109bd69c11d46613d16cc572fb9ad3213f10d95e2b5f12c18e" + "sha256:35bdb9d4ec7052232f8803908f9c7a9c3c65807188b70618fa7a7d8ccd560b4d", + "sha256:94d8bcdc65a715a0024a6d5b701a5f31e45bd159269e62c73731de79f048db2b" ], "markers": "python_version >= '3.9'", - "version": "==3.7.0" + "version": "==3.8.0" }, "tblib": { "hashes": [ @@ -1206,6 +1212,14 @@ "markers": "python_version >= '3.8'", "version": "==4.12.2" }, + "tzdata": { + "hashes": [ + "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", + "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" + ], + "markers": "python_version >= '2'", + "version": "==2025.1" + }, "urllib3": { "hashes": [ "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", @@ -1216,12 +1230,12 @@ }, "whitenoise": { "hashes": [ - "sha256:486bd7267a375fa9650b136daaec156ac572971acc8bf99add90817a530dd1d4", - "sha256:df12dce147a043d1956d81d288c6f0044147c6d2ab9726e5772ac50fb45d2280" + "sha256:8c4a7c9d384694990c26f3047e118c691557481d624f069b7f7752a2f735d609", + "sha256:c8a489049b7ee9889617bb4c274a153f3d979e8f51d2efd0f5b403caf41c57df" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==6.8.2" + "version": "==6.9.0" }, "zope.event": { "hashes": [ @@ -1286,49 +1300,49 @@ }, "bandit": { "hashes": [ - "sha256:b1a61d829c0968aed625381e426aa378904b996529d048f8d908fa28f6b13e38", - "sha256:b5bfe55a095abd9fe20099178a7c6c060f844bfd4fe4c76d28e35e4c52b9d31e" + "sha256:28f04dc0d258e1dd0f99dee8eefa13d1cb5e3fde1a5ab0c523971f97b289bcd8", + "sha256:f5847beb654d309422985c36644649924e0ea4425c76dec2e89110b87506193a" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==1.8.0" + "version": "==1.8.3" }, "beautifulsoup4": { "hashes": [ - "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", - "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed" + "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", + "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16" ], - "markers": "python_full_version >= '3.6.0'", - "version": "==4.12.3" + "markers": "python_full_version >= '3.7.0'", + "version": "==4.13.3" }, "black": { "hashes": [ - "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", - "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd", - "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", - "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", - "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", - "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7", - "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", - "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", - "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", - "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", - "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", - "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f", - "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", - "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", - "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", - "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", - "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800", - "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", - "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", - "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", - "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", - "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e" + "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", + "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7", + "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da", + "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2", + "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc", + "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", + "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f", + "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b", + "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32", + "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", + "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", + "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299", + "sha256:a1ee0a0c330f7b5130ce0caed9936a904793576ef4d2b98c40835d6a65afa6a0", + "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", + "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0", + "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", + "sha256:bacabb307dca5ebaf9c118d2d2f6903da0d62c9faa82bd21a33eecc319559355", + "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096", + "sha256:d9e6827d563a2c820772b32ce8a42828dc6790f095f441beef18f96aa6f8294e", + "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9", + "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba", + "sha256:f3df5f1bf91d36002b0a75389ca8663510cf0531cca8aa5c1ef695b46d98655f" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==24.10.0" + "version": "==25.1.0" }, "blinker": { "hashes": [ @@ -1340,12 +1354,12 @@ }, "boto3": { "hashes": [ - "sha256:ba391982f6cada136c5bba99e85d7fe1bc4e157c53a22a78e4aca35d1b39152e", - "sha256:eecef248f8743ab30036cd9c916808a0892fc9036e1a35434d8222060c08bbd2" + "sha256:1545c943f36db41853cdfdb6ff09c4eda9220dd95bd2fae76fc73091603525d1", + "sha256:9b272268794172b0b8bb9fb1f3c470c3b6c0ffb92fbd4882465cc740e40fbdcd" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.91" + "version": "==1.37.18" }, "boto3-mocking": { "hashes": [ @@ -1358,28 +1372,28 @@ }, "boto3-stubs": { "hashes": [ - "sha256:780f71406147b78f9860d78907b5c015874537d821364588ec837c4cd1eecf91", - "sha256:e4301b9d05b31fbfea382d0d1d950c2178f7fca03058b31373fac9a4cdf89438" + "sha256:30ecf8dfaa848469f55279e41248ff46b1ecf5e2ee7b3b292e3ec39332e2f90e", + "sha256:db866104d3b9bea63c4769255759ad5c61bc5e5591cea0b692df4c08082a841e" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.35.91" + "version": "==1.37.18" }, "botocore": { "hashes": [ - "sha256:7b0b9c5954701fff4d2c516918f45641b04ff4ca92bbd9f5b37c0b80f8c14220", - "sha256:93de9d0f52f7e36a2c190d55520d3b2654f32c5a628fdd484bffa00bc7865e1d" + "sha256:99e8eefd5df6347ead15df07ce55f4e62a51ea7b54de1127522a08597923b726", + "sha256:a8b97d217d82b3c4f6bcc906e264df7ebb51e2c6a62b3548a97cd173fb8759a1" ], "markers": "python_version >= '3.8'", - "version": "==1.35.91" + "version": "==1.37.18" }, "botocore-stubs": { "hashes": [ - "sha256:c6b294cae436eaaf87dcb717e4348c250ea1fc170336579da114b693663d8e42", - "sha256:f7fd78d84f49d28692662b9bdeb4c92f1bf8a5707d0c28c8544399005b02823b" + "sha256:937c9b787e4f784019f321fa1d88a505965c25f425e810bde45e23b7ca564282", + "sha256:bb9a9e7cd2f48ecb429a7d0df0387f63399db8fb363bdfa38eba285854d622a2" ], "markers": "python_version >= '3.8'", - "version": "==1.35.90" + "version": "==1.37.18" }, "click": { "hashes": [ @@ -1400,12 +1414,12 @@ }, "django-debug-toolbar": { "hashes": [ - "sha256:36e421cb908c2f0675e07f9f41e3d1d8618dc386392ec82d23bcfcd5d29c7044", - "sha256:3beb671c9ec44ffb817fad2780667f172bd1c067dbcabad6268ce39a81335f45" + "sha256:8a3b9da4aeab8d384a366e20304bd939a451f0242523c5b7b402248ad474eed2", + "sha256:c0591e338ee9603bdfce5aebf8d18ca7341fdbb69595e2b0b34869be5857180e" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.4.6" + "markers": "python_version >= '3.9'", + "version": "==5.1.0" }, "django-model2puml": { "hashes": [ @@ -1416,20 +1430,20 @@ }, "django-stubs": { "hashes": [ - "sha256:126d354bbdff4906c4e93e6361197f6fbfb6231c3df6def85a291dae6f9f577b", - "sha256:c4dc64260bd72e6d32b9e536e8dd0d9247922f0271f82d1d5132a18f24b388ac" + "sha256:716758ced158b439213062e52de6df3cff7c586f9f9ad7ab59210efbea5dfe78", + "sha256:8c230bc5bebee6da282ba8a27ad1503c84a0c4cd2f46e63d149e76d2a63e639a" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==5.1.1" + "version": "==5.1.3" }, "django-stubs-ext": { "hashes": [ - "sha256:3907f99e178c93323e2ce908aef8352adb8c047605161f8d9e5e7b4efb5a6a9c", - "sha256:db7364e4f50ae7e5360993dbd58a3a57ea4b2e7e5bab0fbd525ccdb3e7975d1c" + "sha256:3e60f82337f0d40a362f349bf15539144b96e4ceb4dbd0239be1cd71f6a74ad0", + "sha256:64561fbc53e963cc1eed2c8eb27e18b8e48dcb90771205180fe29fc8a59e55fd" ], "markers": "python_version >= '3.8'", - "version": "==5.1.1" + "version": "==5.1.3" }, "django-webtest": { "hashes": [ @@ -1441,12 +1455,12 @@ }, "flake8": { "hashes": [ - "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38", - "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213" + "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a", + "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd" ], "index": "pypi", "markers": "python_full_version >= '3.8.1'", - "version": "==7.1.1" + "version": "==7.1.2" }, "jmespath": { "hashes": [ @@ -1482,48 +1496,42 @@ }, "mypy": { "hashes": [ - "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c", - "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", - "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f", - "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0", - "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9", - "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b", - "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", - "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35", - "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319", - "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc", - "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb", - "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb", - "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", - "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60", - "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31", - "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f", - "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", - "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", - "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", - "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", - "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837", - "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6", - "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", - "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d", - "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", - "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae", - "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", - "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8", - "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b", - "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac", - "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", - "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", - "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1", - "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", - "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427", - "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1", - "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c", - "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89" + "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", + "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", + "sha256:1905f494bfd7d85a23a88c5d97840888a7bd516545fc5aaedff0267e0bb54e2f", + "sha256:1fbb8da62dc352133d7d7ca90ed2fb0e9d42bb1a32724c287d3c76c58cbaa9c2", + "sha256:2922d42e16d6de288022e5ca321cd0618b238cfc5570e0263e5ba0a77dbef56f", + "sha256:2e2c2e6d3593f6451b18588848e66260ff62ccca522dd231cd4dd59b0160668b", + "sha256:2ee2d57e01a7c35de00f4634ba1bbf015185b219e4dc5909e281016df43f5ee5", + "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", + "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", + "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", + "sha256:5a95fb17c13e29d2d5195869262f8125dfdb5c134dc8d9a9d0aecf7525b10c2c", + "sha256:6983aae8b2f653e098edb77f893f7b6aca69f6cffb19b2cc7443f23cce5f4828", + "sha256:712e962a6357634fef20412699a3655c610110e01cdaa6180acec7fc9f8513ba", + "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", + "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", + "sha256:8f8722560a14cde92fdb1e31597760dc35f9f5524cce17836c0d22841830fd5b", + "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", + "sha256:973500e0774b85d9689715feeffcc980193086551110fd678ebe1f4342fb7c5e", + "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13", + "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", + "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", + "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", + "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", + "sha256:be68172e9fd9ad8fb876c6389f16d1c1b5f100ffa779f77b1fb2176fcc9ab95b", + "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", + "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559", + "sha256:c7be1e46525adfa0d97681432ee9fcd61a3964c2446795714699a998d193f1a3", + "sha256:c9817fa23833ff189db061e6d2eff49b2f3b6ed9856b4a0a73046e41932d744f", + "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", + "sha256:d10d994b41fb3497719bbf866f227b3489048ea4bbbb5015357db306249f7980", + "sha256:e601a7fa172c2131bff456bb3ee08a88360760d0d2f8cbd7a75a65497e2df078", + "sha256:f95579473af29ab73a10bada2f9722856792a36ec5af5399b653aa28360290a5" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.14.1" + "markers": "python_version >= '3.9'", + "version": "==1.15.0" }, "mypy-extensions": { "hashes": [ @@ -1559,19 +1567,19 @@ }, "pbr": { "hashes": [ - "sha256:788183e382e3d1d7707db08978239965e8b9e4e5ed42669bf4758186734d5f24", - "sha256:a776ae228892d8013649c0aeccbb3d5f99ee15e005a4cbb7e61d55a067b28a2a" + "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76", + "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b" ], "markers": "python_version >= '2.6'", - "version": "==6.1.0" + "version": "==6.1.1" }, "platformdirs": { "hashes": [ - "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", - "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" + "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", + "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351" ], - "markers": "python_version >= '3.8'", - "version": "==4.3.6" + "markers": "python_version >= '3.9'", + "version": "==4.3.7" }, "pycodestyle": { "hashes": [ @@ -1591,11 +1599,11 @@ }, "pygments": { "hashes": [ - "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", - "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" + "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", + "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c" ], "markers": "python_version >= '3.8'", - "version": "==2.18.0" + "version": "==2.19.1" }, "python-dateutil": { "hashes": [ @@ -1674,11 +1682,19 @@ }, "s3transfer": { "hashes": [ - "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", - "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" + "sha256:559f161658e1cf0a911f45940552c696735f5c74e64362e515f333ebed87d679", + "sha256:ac265fa68318763a03bf2dc4f39d5cbd6a9e178d81cc9483ad27da33637e320d" ], "markers": "python_version >= '3.8'", - "version": "==0.10.4" + "version": "==0.11.4" + }, + "setuptools": { + "hashes": [ + "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945", + "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c" + ], + "markers": "python_version >= '3.9'", + "version": "==77.0.3" }, "six": { "hashes": [ @@ -1706,11 +1722,11 @@ }, "stevedore": { "hashes": [ - "sha256:79e92235ecb828fe952b6b8b0c6c87863248631922c8e8e0fa5b17b232c4514d", - "sha256:b0be3c4748b3ea7b854b265dcb4caa891015e442416422be16f8b31756107857" + "sha256:3135b5ae50fe12816ef291baff420acb727fcd356106e3e9cbfa9e5985cd6f4b", + "sha256:d10a31c7b86cba16c1f6e8d15416955fc797052351a56af15e608ad20811fcfe" ], "markers": "python_version >= '3.9'", - "version": "==5.4.0" + "version": "==5.4.1" }, "tomli": { "hashes": [ @@ -1752,11 +1768,11 @@ }, "types-awscrt": { "hashes": [ - "sha256:405bce8c281f9e7c6c92a229225cc0bf10d30729a6a601123213389bd524b8b1", - "sha256:fbf9c221af5607b24bf17f8431217ce8b9a27917139edbc984891eb63fd5a593" + "sha256:345ab84a4f75b26bfb816b249657855824a4f2d1ce5b58268c549f81fce6eccc", + "sha256:5826baf69ad5d29c76be49fc7df00222281fa31b14f99e9fb4492d71ec98fea5" ], "markers": "python_version >= '3.8'", - "version": "==0.23.6" + "version": "==0.24.2" }, "types-cachetools": { "hashes": [ @@ -1777,20 +1793,20 @@ }, "types-requests": { "hashes": [ - "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95", - "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747" + "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1", + "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.32.0.20241016" + "markers": "python_version >= '3.9'", + "version": "==2.32.0.20250306" }, "types-s3transfer": { "hashes": [ - "sha256:03123477e3064c81efe712bf9d372c7c72f2790711431f9baa59cf96ea607267", - "sha256:22ac1aabc98f9d7f2928eb3fb4d5c02bf7435687f0913345a97dd3b84d0c217d" + "sha256:05fde593c84270f19fd053f0b1e08f5a057d7c5f036b9884e68fb8cd3041ac30", + "sha256:2a76d92c07d4a3cb469e5343b2e7560e0b8078b2e03696a65407b8c44c861b61" ], "markers": "python_version >= '3.8'", - "version": "==0.10.4" + "version": "==0.11.4" }, "typing-extensions": { "hashes": [ @@ -1827,11 +1843,11 @@ }, "webtest": { "hashes": [ - "sha256:0b2de681c16f57b31da5cce6e94ff03cdc77bd86c37a57ba0ee27fed8e065ceb", - "sha256:799846e169d15e0c1233ab4ab00ee4de59a5d964407d6f2945d89249328dbbdb" + "sha256:5b3d8c69ac9057f17750ed5b45320a411423c2b4196bec6450961be98b03d8c1", + "sha256:94778d19a37e5abd7388dad4d93874410ecced53a1739a8e5ff2dbcba1cfc0c4" ], - "markers": "python_version >= '3.7'", - "version": "==3.0.2" + "markers": "python_version >= '3.9'", + "version": "==3.0.4" } } } diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index b054afb5b..dd525fd00 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -1703,14 +1703,14 @@ class Domain(TimeStampedModel, DomainHelper): # https://github.com/cisagov/epplib/blob/master/epplib/models/common.py#L32 DF = epp.DiscloseField all_disclose_fields = {field for field in DF} - disclose_args = {"fields": all_disclose_fields, "flag": False, "types": {DF.ADDR: "loc"}} + disclose_args = {"fields": all_disclose_fields, "flag": False, "types": {DF.ADDR: "loc", DF.NAME}} fields_to_remove = {DF.NOTIFY_EMAIL, DF.VAT, DF.IDENT} if contact.contact_type == contact.ContactTypeChoices.SECURITY: if contact.email not in DefaultEmail.get_all_emails(): fields_to_remove.add(DF.EMAIL) elif contact.contact_type == contact.ContactTypeChoices.ADMINISTRATIVE: - fields_to_remove.update({DF.EMAIL, DF.VOICE, DF.ADDR}) + fields_to_remove.update({DF.NAME, DF.EMAIL, DF.VOICE, DF.ADDR}) disclose_args["fields"].difference_update(fields_to_remove) # type: ignore diff --git a/src/requirements.txt b/src/requirements.txt index c3ed17604..01babf2d4 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,14 +1,14 @@ -i https://pypi.python.org/simple annotated-types==0.7.0; python_version >= '3.8' asgiref==3.8.1; python_version >= '3.8' -boto3==1.35.91; python_version >= '3.8' -botocore==1.35.91; python_version >= '3.8' -cachetools==5.5.0; python_version >= '3.7' -certifi==2024.12.14; python_version >= '3.6' +boto3==1.37.18; python_version >= '3.8' +botocore==1.37.18; python_version >= '3.8' +cachetools==5.5.2; python_version >= '3.7' +certifi==2025.1.31; python_version >= '3.6' cfenv==0.5.3 cffi==1.17.1; python_version >= '3.8' charset-normalizer==3.4.1; python_version >= '3.7' -cryptography==44.0.0; python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1' +cryptography==44.0.2; python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1' defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' diff-match-patch==20241021; python_version >= '3.7' dj-database-url==2.3.0 @@ -18,51 +18,52 @@ django-admin-multiple-choice-list-filter==0.1.1 django-allow-cidr==0.7.1 django-auditlog==3.0.0; python_version >= '3.8' django-cache-url==3.4.5 -django-cors-headers==4.6.0; python_version >= '3.9' +django-cors-headers==4.7.0; python_version >= '3.9' django-csp==3.8 django-fsm==2.8.1 -django-import-export==4.3.3; python_version >= '3.9' +django-import-export==4.3.7; python_version >= '3.9' django-login-required-middleware==0.9.0 django-phonenumber-field[phonenumberslite]==8.0.0; python_version >= '3.8' django-waffle==4.2.0; python_version >= '3.8' django-widget-tweaks==1.5.0; python_version >= '3.8' -environs[django]==11.2.1; python_version >= '3.8' -faker==33.1.0; python_version >= '3.8' -fred-epplib @ git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c -furl==2.1.3 +environs[django]==14.1.1; python_version >= '3.9' +faker==37.0.2; python_version >= '3.9' +fred-epplib @ git+https://github.com/cisagov/epplib.git@9f0fd0e69665001767f15a034c9c0c919dab5cdd +furl==2.1.4 future==1.0.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2' gevent==24.11.1; python_version >= '3.9' greenlet==3.1.1; python_version >= '3.7' gunicorn==23.0.0; python_version >= '3.7' idna==3.10; python_version >= '3.6' jmespath==1.0.1; python_version >= '3.7' -lxml==5.3.0; python_version >= '3.6' -mako==1.3.8; python_version >= '3.8' +lxml==5.3.1; python_version >= '3.6' +mako==1.3.9; python_version >= '3.8' markupsafe==3.0.2; python_version >= '3.9' -marshmallow==3.23.2; python_version >= '3.9' +marshmallow==3.26.1; python_version >= '3.9' oic==1.7.0; python_version ~= '3.8' orderedmultidict==1.0.1 packaging==24.2; python_version >= '3.8' -phonenumberslite==8.13.52 +phonenumberslite==9.0.1 psycopg2-binary==2.9.10; python_version >= '3.8' pycparser==2.22; python_version >= '3.8' -pycryptodomex==3.21.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' -pydantic==2.10.4; python_version >= '3.8' +pycryptodomex==3.22.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6' +pydantic==2.10.6; python_version >= '3.8' pydantic-core==2.27.2; python_version >= '3.8' -pydantic-settings==2.7.1; python_version >= '3.8' +pydantic-settings==2.8.1; python_version >= '3.8' pyjwkest==1.4.2 python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' python-dotenv==1.0.1; python_version >= '3.8' pyzipper==0.3.6; python_version >= '3.4' requests==2.32.3; python_version >= '3.8' -s3transfer==0.10.4; python_version >= '3.8' -setuptools==75.6.0; python_version >= '3.9' +s3transfer==0.11.4; python_version >= '3.8' +setuptools==77.0.3; python_version >= '3.9' six==1.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' sqlparse==0.5.3; python_version >= '3.8' -tablib==3.7.0; python_version >= '3.9' +tablib==3.8.0; python_version >= '3.9' tblib==3.0.0; python_version >= '3.8' typing-extensions==4.12.2; python_version >= '3.8' +tzdata==2025.1; python_version >= '2' urllib3==2.3.0; python_version >= '3.9' -whitenoise==6.8.2; python_version >= '3.9' +whitenoise==6.9.0; python_version >= '3.9' zope.event==5.0; python_version >= '3.7' zope.interface==7.2; python_version >= '3.8' From 498d0b4ebbf3b3cc8f24e3328a0223a1c245d180 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:54:32 -0600 Subject: [PATCH 25/36] Update domain.py --- src/registrar/models/domain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index dd525fd00..39e5ae8f5 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -1703,7 +1703,7 @@ class Domain(TimeStampedModel, DomainHelper): # https://github.com/cisagov/epplib/blob/master/epplib/models/common.py#L32 DF = epp.DiscloseField all_disclose_fields = {field for field in DF} - disclose_args = {"fields": all_disclose_fields, "flag": False, "types": {DF.ADDR: "loc", DF.NAME}} + disclose_args = {"fields": all_disclose_fields, "flag": False, "types": {DF.ADDR: "loc", DF.NAME: "loc"}} fields_to_remove = {DF.NOTIFY_EMAIL, DF.VAT, DF.IDENT} if contact.contact_type == contact.ContactTypeChoices.SECURITY: From 40b373481a570a17082f4aea9a7affa5378be768 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 24 Mar 2025 07:59:48 -0600 Subject: [PATCH 26/36] Update unit tests to include DF.NAME --- src/registrar/tests/common.py | 2 +- src/registrar/tests/test_models_domain.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 742efc040..3d9eaa410 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -1988,7 +1988,7 @@ class MockEppLib(TestCase): disclose_fields = {field for field in DF} - fields if disclose_types is None: - disclose_types = {DF.ADDR: "loc"} + disclose_types = {DF.ADDR: "loc", DF.NAME: "loc"} di = common.Disclose(flag=disclose, fields=disclose_fields, types=disclose_types) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 5ca32140c..beea338f2 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -1003,12 +1003,11 @@ class TestRegistrantContacts(MockEppLib): expected_contact, disclose=False, disclose_fields=disclose_fields ) elif expected_contact.contact_type == PublicContact.ContactTypeChoices.ADMINISTRATIVE: - disclose_fields = self.all_disclose_fields - {"email", "voice", "addr"} + disclose_fields = self.all_disclose_fields - {"name", "email", "voice", "addr"} expectedCreateCommand = self._convertPublicContactToEpp( expected_contact, disclose=False, disclose_fields=disclose_fields, - disclose_types={"addr": "loc"}, ) else: expectedCreateCommand = self._convertPublicContactToEpp( @@ -1029,7 +1028,9 @@ class TestRegistrantContacts(MockEppLib): DF = common.DiscloseField expected_disclose = { "auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"), - "disclose": common.Disclose(flag=False, fields=disclose_email_field, types={DF.ADDR: "loc"}), + "disclose": common.Disclose( + flag=False, fields=disclose_email_field, types={DF.ADDR: "loc", DF.NAME: "loc"} + ), "email": "help@get.gov", "extensions": [], "fax": None, @@ -1054,7 +1055,9 @@ class TestRegistrantContacts(MockEppLib): # Separated for linter expected_not_disclose = { "auth_info": common.ContactAuthInfo(pw="2fooBAR123fooBaz"), - "disclose": common.Disclose(flag=False, fields=disclose_email_field, types={DF.ADDR: "loc"}), + "disclose": common.Disclose( + flag=False, fields=disclose_email_field, types={DF.ADDR: "loc", DF.NAME: "loc"} + ), "email": "help@get.gov", "extensions": [], "fax": None, @@ -1113,7 +1116,7 @@ class TestRegistrantContacts(MockEppLib): # Verify disclosure settings self.assertEqual(result.disclose.flag, True) self.assertEqual(result.disclose.fields, {DF.EMAIL}) - self.assertEqual(result.disclose.types, {DF.ADDR: "loc"}) + self.assertEqual(result.disclose.types, {DF.ADDR: "loc", DF.NAME: "loc"}) def test_not_disclosed_on_default_security_contact(self): """ From b7789aad807e596ecd78a3f9bbfc1bf131901346 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 24 Mar 2025 12:26:31 -0600 Subject: [PATCH 27/36] Update test_management_scripts.py --- src/registrar/tests/test_management_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/tests/test_management_scripts.py b/src/registrar/tests/test_management_scripts.py index be696ced1..da74a8482 100644 --- a/src/registrar/tests/test_management_scripts.py +++ b/src/registrar/tests/test_management_scripts.py @@ -2596,7 +2596,7 @@ class TestUpdateDefaultPublicContacts(MockEppLib): expected_update = self._convertPublicContactToEpp( self.old_default_contact, disclose=False, - disclose_fields=self.all_disclose_fields - {"email", "voice", "addr"}, + disclose_fields=self.all_disclose_fields - {"name", "email", "voice", "addr"}, ) self.mockedSendFunction.assert_any_call(expected_update, cleaned=True) From 022ff51c8867cac48b42e021f6129a7de50a6168 Mon Sep 17 00:00:00 2001 From: Kristina <140533113+witha-k@users.noreply.github.com> Date: Tue, 25 Mar 2025 03:25:18 -0400 Subject: [PATCH 28/36] Update renewal content on requirements pg --- src/registrar/templates/domain_request_requirements.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/templates/domain_request_requirements.html b/src/registrar/templates/domain_request_requirements.html index 4d49b235e..70ee42723 100644 --- a/src/registrar/templates/domain_request_requirements.html +++ b/src/registrar/templates/domain_request_requirements.html @@ -49,9 +49,9 @@

Domain renewal

-

.Gov domains are registered for a one-year period. To renew your domain, you'll be asked to verify your organization’s eligibility and your contact information.

+

.Gov domains are registered for a one-year period. To renew the domain, you’ll be asked to verify your contact information and some details about the domain.

-

Though a domain may expire, it will not automatically be put on hold or deleted. We’ll make extensive efforts to contact your organization before holding or deleting a domain.

+

.Gov domains are registered for a one-year period. To renew the domain, you’ll be asked to verify your contact information and some details about the domain.

{% endblock %} {% block form_required_fields_help_text %} From 9279087b0347715d25d44490e6d49a73f07fe863 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 25 Mar 2025 08:24:23 -0600 Subject: [PATCH 29/36] refresh pipfile --- src/Pipfile.lock | 68 ++++++++++++++++++++++---------------------- src/requirements.txt | 12 ++++---- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Pipfile.lock b/src/Pipfile.lock index 914a217d0..3327cf111 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -32,20 +32,20 @@ }, "boto3": { "hashes": [ - "sha256:1545c943f36db41853cdfdb6ff09c4eda9220dd95bd2fae76fc73091603525d1", - "sha256:9b272268794172b0b8bb9fb1f3c470c3b6c0ffb92fbd4882465cc740e40fbdcd" + "sha256:c69c90500f18fd72d782d1612170b7d3db9a98ed51a4da3bebe38e693497ebf8", + "sha256:fbfc2c43ad686b63c8aa02aee634c269f856eed68941d8e570cc45950be52130" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.37.18" + "version": "==1.37.19" }, "botocore": { "hashes": [ - "sha256:99e8eefd5df6347ead15df07ce55f4e62a51ea7b54de1127522a08597923b726", - "sha256:a8b97d217d82b3c4f6bcc906e264df7ebb51e2c6a62b3548a97cd173fb8759a1" + "sha256:6e1337e73a6b8146c1ec20a6a72d67e2809bd4c0af076431fe6e1561e0c89415", + "sha256:eadcdc37de09df25cf1e62e8106660c61f60a68e984acfc1a8d43fb6267e53b8" ], "markers": "python_version >= '3.8'", - "version": "==1.37.18" + "version": "==1.37.19" }, "cachetools": { "hashes": [ @@ -439,12 +439,12 @@ }, "faker": { "hashes": [ - "sha256:8955706c56c28099585e9e2b6f814eb0a3a227eb36a2ee3eb9ab577c4764eacc", - "sha256:948bd27706478d3aa0b6f9f58b9f25207098f6ca79852c7b49c44a8ced2bc59b" + "sha256:ad9dc66a3b84888b837ca729e85299a96b58fdaef0323ed0baace93c9614af06", + "sha256:dc2f730be71cb770e9c715b13374d80dbcee879675121ab51f9683d262ae9a1c" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==37.0.2" + "version": "==37.1.0" }, "fred-epplib": { "git": "https://github.com/cisagov/epplib.git", @@ -1131,11 +1131,11 @@ }, "python-dotenv": { "hashes": [ - "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", - "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", + "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d" ], - "markers": "python_version >= '3.8'", - "version": "==1.0.1" + "markers": "python_version >= '3.9'", + "version": "==1.1.0" }, "pyzipper": { "hashes": [ @@ -1165,11 +1165,11 @@ }, "setuptools": { "hashes": [ - "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945", - "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c" + "sha256:137525e6afb9022f019d6e884a319017f9bf879a0d8783985d32cbc8683cab93", + "sha256:4a612c80e1f1d71b80e4906ce730152e8dec23df439f82731d9d0b608d7b700d" ], "markers": "python_version >= '3.9'", - "version": "==77.0.3" + "version": "==78.0.2" }, "six": { "hashes": [ @@ -1215,11 +1215,11 @@ }, "tzdata": { "hashes": [ - "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", - "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" + "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", + "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9" ], "markers": "python_version >= '2'", - "version": "==2025.1" + "version": "==2025.2" }, "urllib3": { "hashes": [ @@ -1355,12 +1355,12 @@ }, "boto3": { "hashes": [ - "sha256:1545c943f36db41853cdfdb6ff09c4eda9220dd95bd2fae76fc73091603525d1", - "sha256:9b272268794172b0b8bb9fb1f3c470c3b6c0ffb92fbd4882465cc740e40fbdcd" + "sha256:c69c90500f18fd72d782d1612170b7d3db9a98ed51a4da3bebe38e693497ebf8", + "sha256:fbfc2c43ad686b63c8aa02aee634c269f856eed68941d8e570cc45950be52130" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.37.18" + "version": "==1.37.19" }, "boto3-mocking": { "hashes": [ @@ -1373,28 +1373,28 @@ }, "boto3-stubs": { "hashes": [ - "sha256:30ecf8dfaa848469f55279e41248ff46b1ecf5e2ee7b3b292e3ec39332e2f90e", - "sha256:db866104d3b9bea63c4769255759ad5c61bc5e5591cea0b692df4c08082a841e" + "sha256:95c665b1375b6578067fdc3a7854b58eca710b288240bd1cd2c9b1954e3f5f5d", + "sha256:af67bca13956004eaab81a7af179c689392fd8341f761a27d5b49cd17639a472" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.37.18" + "version": "==1.37.19" }, "botocore": { "hashes": [ - "sha256:99e8eefd5df6347ead15df07ce55f4e62a51ea7b54de1127522a08597923b726", - "sha256:a8b97d217d82b3c4f6bcc906e264df7ebb51e2c6a62b3548a97cd173fb8759a1" + "sha256:6e1337e73a6b8146c1ec20a6a72d67e2809bd4c0af076431fe6e1561e0c89415", + "sha256:eadcdc37de09df25cf1e62e8106660c61f60a68e984acfc1a8d43fb6267e53b8" ], "markers": "python_version >= '3.8'", - "version": "==1.37.18" + "version": "==1.37.19" }, "botocore-stubs": { "hashes": [ - "sha256:b9c3a1e8fb57fb70b49aa5380cabefab32ec028d8a1d8f5ac83dd836c5b429a8", - "sha256:c6cb18979a86db311a365448b67e4a492a530c3f4fb313432d41deaee6268b95" + "sha256:c33db42760989cd3c3bfc57dc676445c0b644bfb60d2c0ef409ab7e051445f63", + "sha256:d8fcf941d10ff9af71cf7a4bda4b2e4f458d780f7c691f93811a130d636963f1" ], "markers": "python_version >= '3.8'", - "version": "==1.37.17" + "version": "==1.37.19" }, "click": { "hashes": [ @@ -1691,11 +1691,11 @@ }, "setuptools": { "hashes": [ - "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945", - "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c" + "sha256:137525e6afb9022f019d6e884a319017f9bf879a0d8783985d32cbc8683cab93", + "sha256:4a612c80e1f1d71b80e4906ce730152e8dec23df439f82731d9d0b608d7b700d" ], "markers": "python_version >= '3.9'", - "version": "==77.0.3" + "version": "==78.0.2" }, "six": { "hashes": [ diff --git a/src/requirements.txt b/src/requirements.txt index 8518f6655..b9f408466 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,8 +1,8 @@ -i https://pypi.python.org/simple annotated-types==0.7.0; python_version >= '3.8' asgiref==3.8.1; python_version >= '3.8' -boto3==1.37.18; python_version >= '3.8' -botocore==1.37.18; python_version >= '3.8' +boto3==1.37.19; python_version >= '3.8' +botocore==1.37.19; python_version >= '3.8' cachetools==5.5.2; python_version >= '3.7' certifi==2025.1.31; python_version >= '3.6' cfenv==0.5.3 @@ -27,7 +27,7 @@ django-phonenumber-field[phonenumberslite]==8.0.0; python_version >= '3.8' django-waffle==4.2.0; python_version >= '3.8' django-widget-tweaks==1.5.0; python_version >= '3.8' environs[django]==14.1.1; python_version >= '3.9' -faker==37.0.2; python_version >= '3.9' +faker==37.1.0; python_version >= '3.9' fred-epplib @ git+https://github.com/cisagov/epplib.git@9f0fd0e69665001767f15a034c9c0c919dab5cdd furl==2.1.4 future==1.0.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2' @@ -52,17 +52,17 @@ pydantic-core==2.27.2; python_version >= '3.8' pydantic-settings==2.8.1; python_version >= '3.8' pyjwkest==1.4.2 python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' -python-dotenv==1.0.1; python_version >= '3.8' +python-dotenv==1.1.0; python_version >= '3.9' pyzipper==0.3.6; python_version >= '3.4' requests==2.32.3; python_version >= '3.8' s3transfer==0.11.4; python_version >= '3.8' -setuptools==77.0.3; python_version >= '3.9' +setuptools==78.0.2; python_version >= '3.9' six==1.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' sqlparse==0.5.3; python_version >= '3.8' tablib==3.8.0; python_version >= '3.9' tblib==3.0.0; python_version >= '3.8' typing-extensions==4.12.2; python_version >= '3.8' -tzdata==2025.1; python_version >= '2' +tzdata==2025.2; python_version >= '2' urllib3==2.3.0; python_version >= '3.9' whitenoise==6.9.0; python_version >= '3.9' zope.event==5.0; python_version >= '3.7' From 8d307c168ba6f37b8269eac533e57e5a9f3f62d6 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:45:09 -0600 Subject: [PATCH 30/36] revert piplock changes --- src/Pipfile.lock | 70 ++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Pipfile.lock b/src/Pipfile.lock index 3327cf111..0754b737a 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -32,20 +32,20 @@ }, "boto3": { "hashes": [ - "sha256:c69c90500f18fd72d782d1612170b7d3db9a98ed51a4da3bebe38e693497ebf8", - "sha256:fbfc2c43ad686b63c8aa02aee634c269f856eed68941d8e570cc45950be52130" + "sha256:1545c943f36db41853cdfdb6ff09c4eda9220dd95bd2fae76fc73091603525d1", + "sha256:9b272268794172b0b8bb9fb1f3c470c3b6c0ffb92fbd4882465cc740e40fbdcd" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.37.19" + "version": "==1.37.18" }, "botocore": { "hashes": [ - "sha256:6e1337e73a6b8146c1ec20a6a72d67e2809bd4c0af076431fe6e1561e0c89415", - "sha256:eadcdc37de09df25cf1e62e8106660c61f60a68e984acfc1a8d43fb6267e53b8" + "sha256:99e8eefd5df6347ead15df07ce55f4e62a51ea7b54de1127522a08597923b726", + "sha256:a8b97d217d82b3c4f6bcc906e264df7ebb51e2c6a62b3548a97cd173fb8759a1" ], "markers": "python_version >= '3.8'", - "version": "==1.37.19" + "version": "==1.37.18" }, "cachetools": { "hashes": [ @@ -439,12 +439,12 @@ }, "faker": { "hashes": [ - "sha256:ad9dc66a3b84888b837ca729e85299a96b58fdaef0323ed0baace93c9614af06", - "sha256:dc2f730be71cb770e9c715b13374d80dbcee879675121ab51f9683d262ae9a1c" + "sha256:8955706c56c28099585e9e2b6f814eb0a3a227eb36a2ee3eb9ab577c4764eacc", + "sha256:948bd27706478d3aa0b6f9f58b9f25207098f6ca79852c7b49c44a8ced2bc59b" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==37.1.0" + "version": "==37.0.2" }, "fred-epplib": { "git": "https://github.com/cisagov/epplib.git", @@ -1131,11 +1131,11 @@ }, "python-dotenv": { "hashes": [ - "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", - "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d" + "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", + "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" ], - "markers": "python_version >= '3.9'", - "version": "==1.1.0" + "markers": "python_version >= '3.8'", + "version": "==1.0.1" }, "pyzipper": { "hashes": [ @@ -1165,11 +1165,11 @@ }, "setuptools": { "hashes": [ - "sha256:137525e6afb9022f019d6e884a319017f9bf879a0d8783985d32cbc8683cab93", - "sha256:4a612c80e1f1d71b80e4906ce730152e8dec23df439f82731d9d0b608d7b700d" + "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945", + "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c" ], "markers": "python_version >= '3.9'", - "version": "==78.0.2" + "version": "==77.0.3" }, "six": { "hashes": [ @@ -1215,11 +1215,11 @@ }, "tzdata": { "hashes": [ - "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", - "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9" + "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", + "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" ], "markers": "python_version >= '2'", - "version": "==2025.2" + "version": "==2025.1" }, "urllib3": { "hashes": [ @@ -1355,12 +1355,12 @@ }, "boto3": { "hashes": [ - "sha256:c69c90500f18fd72d782d1612170b7d3db9a98ed51a4da3bebe38e693497ebf8", - "sha256:fbfc2c43ad686b63c8aa02aee634c269f856eed68941d8e570cc45950be52130" + "sha256:1545c943f36db41853cdfdb6ff09c4eda9220dd95bd2fae76fc73091603525d1", + "sha256:9b272268794172b0b8bb9fb1f3c470c3b6c0ffb92fbd4882465cc740e40fbdcd" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.37.19" + "version": "==1.37.18" }, "boto3-mocking": { "hashes": [ @@ -1373,28 +1373,28 @@ }, "boto3-stubs": { "hashes": [ - "sha256:95c665b1375b6578067fdc3a7854b58eca710b288240bd1cd2c9b1954e3f5f5d", - "sha256:af67bca13956004eaab81a7af179c689392fd8341f761a27d5b49cd17639a472" + "sha256:30ecf8dfaa848469f55279e41248ff46b1ecf5e2ee7b3b292e3ec39332e2f90e", + "sha256:db866104d3b9bea63c4769255759ad5c61bc5e5591cea0b692df4c08082a841e" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==1.37.19" + "version": "==1.37.18" }, "botocore": { "hashes": [ - "sha256:6e1337e73a6b8146c1ec20a6a72d67e2809bd4c0af076431fe6e1561e0c89415", - "sha256:eadcdc37de09df25cf1e62e8106660c61f60a68e984acfc1a8d43fb6267e53b8" + "sha256:99e8eefd5df6347ead15df07ce55f4e62a51ea7b54de1127522a08597923b726", + "sha256:a8b97d217d82b3c4f6bcc906e264df7ebb51e2c6a62b3548a97cd173fb8759a1" ], "markers": "python_version >= '3.8'", - "version": "==1.37.19" + "version": "==1.37.18" }, "botocore-stubs": { "hashes": [ - "sha256:c33db42760989cd3c3bfc57dc676445c0b644bfb60d2c0ef409ab7e051445f63", - "sha256:d8fcf941d10ff9af71cf7a4bda4b2e4f458d780f7c691f93811a130d636963f1" + "sha256:b9c3a1e8fb57fb70b49aa5380cabefab32ec028d8a1d8f5ac83dd836c5b429a8", + "sha256:c6cb18979a86db311a365448b67e4a492a530c3f4fb313432d41deaee6268b95" ], "markers": "python_version >= '3.8'", - "version": "==1.37.19" + "version": "==1.37.17" }, "click": { "hashes": [ @@ -1691,11 +1691,11 @@ }, "setuptools": { "hashes": [ - "sha256:137525e6afb9022f019d6e884a319017f9bf879a0d8783985d32cbc8683cab93", - "sha256:4a612c80e1f1d71b80e4906ce730152e8dec23df439f82731d9d0b608d7b700d" + "sha256:583b361c8da8de57403743e756609670de6fb2345920e36dc5c2d914c319c945", + "sha256:67122e78221da5cf550ddd04cf8742c8fe12094483749a792d56cd669d6cf58c" ], "markers": "python_version >= '3.9'", - "version": "==78.0.2" + "version": "==77.0.3" }, "six": { "hashes": [ @@ -1851,4 +1851,4 @@ "version": "==3.0.4" } } -} +} \ No newline at end of file From f161ec5924aae7ceb3ae18f6b1a52dbda9c853f3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:57:54 -0600 Subject: [PATCH 31/36] undo changes --- src/Pipfile.lock | 2 +- src/requirements.txt | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Pipfile.lock b/src/Pipfile.lock index 0754b737a..914a217d0 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -1851,4 +1851,4 @@ "version": "==3.0.4" } } -} \ No newline at end of file +} diff --git a/src/requirements.txt b/src/requirements.txt index b9f408466..8518f6655 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,8 +1,8 @@ -i https://pypi.python.org/simple annotated-types==0.7.0; python_version >= '3.8' asgiref==3.8.1; python_version >= '3.8' -boto3==1.37.19; python_version >= '3.8' -botocore==1.37.19; python_version >= '3.8' +boto3==1.37.18; python_version >= '3.8' +botocore==1.37.18; python_version >= '3.8' cachetools==5.5.2; python_version >= '3.7' certifi==2025.1.31; python_version >= '3.6' cfenv==0.5.3 @@ -27,7 +27,7 @@ django-phonenumber-field[phonenumberslite]==8.0.0; python_version >= '3.8' django-waffle==4.2.0; python_version >= '3.8' django-widget-tweaks==1.5.0; python_version >= '3.8' environs[django]==14.1.1; python_version >= '3.9' -faker==37.1.0; python_version >= '3.9' +faker==37.0.2; python_version >= '3.9' fred-epplib @ git+https://github.com/cisagov/epplib.git@9f0fd0e69665001767f15a034c9c0c919dab5cdd furl==2.1.4 future==1.0.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2' @@ -52,17 +52,17 @@ pydantic-core==2.27.2; python_version >= '3.8' pydantic-settings==2.8.1; python_version >= '3.8' pyjwkest==1.4.2 python-dateutil==2.9.0.post0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' -python-dotenv==1.1.0; python_version >= '3.9' +python-dotenv==1.0.1; python_version >= '3.8' pyzipper==0.3.6; python_version >= '3.4' requests==2.32.3; python_version >= '3.8' s3transfer==0.11.4; python_version >= '3.8' -setuptools==78.0.2; python_version >= '3.9' +setuptools==77.0.3; python_version >= '3.9' six==1.17.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2' sqlparse==0.5.3; python_version >= '3.8' tablib==3.8.0; python_version >= '3.9' tblib==3.0.0; python_version >= '3.8' typing-extensions==4.12.2; python_version >= '3.8' -tzdata==2025.2; python_version >= '2' +tzdata==2025.1; python_version >= '2' urllib3==2.3.0; python_version >= '3.9' whitenoise==6.9.0; python_version >= '3.9' zope.event==5.0; python_version >= '3.7' From d42993b0fdade48eccbda2ab7eae5e50c8b1304d Mon Sep 17 00:00:00 2001 From: Kristina <140533113+witha-k@users.noreply.github.com> Date: Tue, 25 Mar 2025 12:31:50 -0400 Subject: [PATCH 32/36] Update domain_request_requirements.html --- src/registrar/templates/domain_request_requirements.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/templates/domain_request_requirements.html b/src/registrar/templates/domain_request_requirements.html index 70ee42723..4625162ea 100644 --- a/src/registrar/templates/domain_request_requirements.html +++ b/src/registrar/templates/domain_request_requirements.html @@ -51,7 +51,7 @@

.Gov domains are registered for a one-year period. To renew the domain, you’ll be asked to verify your contact information and some details about the domain.

-

.Gov domains are registered for a one-year period. To renew the domain, you’ll be asked to verify your contact information and some details about the domain.

+

Though a domain may expire, it will not automatically be put on hold or deleted. We’ll make extensive efforts to contact your organization before holding or deleting a domain.

{% endblock %} {% block form_required_fields_help_text %} From 33667b0605eac08e82df105baa20b67a791b1836 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 26 Mar 2025 09:32:17 -0600 Subject: [PATCH 33/36] Update src/registrar/management/commands/update_default_public_contacts.py Co-authored-by: Rachid Mrad <107004823+rachidatecs@users.noreply.github.com> --- .../management/commands/update_default_public_contacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 7382ac97e..92e1419f6 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -18,7 +18,7 @@ class Command(BaseCommand, PopulateScriptTemplate): "--overwrite_updated_contacts", action=argparse.BooleanOptionalAction, help=( - "Loops over PublicContacts with the email 'help@get.gov' when enabled." + "Loops over PublicContacts with an email of 'help@get.gov' when enabled." "Use this setting if the record was updated in the DB but not correctly in EPP." ), ) From db9808ccfd121cfa678d36634764f785082599bd Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 26 Mar 2025 09:32:30 -0600 Subject: [PATCH 34/36] Update src/registrar/management/commands/update_default_public_contacts.py Co-authored-by: Rachid Mrad <107004823+rachidatecs@users.noreply.github.com> --- .../management/commands/update_default_public_contacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 92e1419f6..52b7f3937 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -27,7 +27,7 @@ class Command(BaseCommand, PopulateScriptTemplate): "--target_domain", help=( "Updates the public contact on a given domain name (case insensitive). " - "Use this option to avoid doing a mass-update to every public contact record." + "Use this option to avoid doing a mass-update of every public contact record." ), ) From af22542b947e4f19eb56d50b618a818a322d8d28 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 26 Mar 2025 09:36:50 -0600 Subject: [PATCH 35/36] Add better comment --- .../management/commands/update_default_public_contacts.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index 7382ac97e..7ad721134 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -64,7 +64,13 @@ class Command(BaseCommand, PopulateScriptTemplate): self.mass_update_records(PublicContact, filter_condition, fields_to_update, show_record_count=True) def bulk_update_fields(self, *args, **kwargs): - """Skip bulk update since we need to manually save each field""" + """Skip bulk update since we need to manually save each field. + Our EPP logic is tied to an override of .save(), and this also associates + with our caching logic for this area of the code. + + Since bulk update does not trigger .save() for each field, we have to + call it manually. + """ return None def update_record(self, record: PublicContact): From 6768caa90ec90f44df23e1aa399f8b96584216c3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:12:13 -0600 Subject: [PATCH 36/36] Update update_default_public_contacts.py --- .../management/commands/update_default_public_contacts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/management/commands/update_default_public_contacts.py b/src/registrar/management/commands/update_default_public_contacts.py index b58e64cb5..ac8c542db 100644 --- a/src/registrar/management/commands/update_default_public_contacts.py +++ b/src/registrar/management/commands/update_default_public_contacts.py @@ -67,7 +67,7 @@ class Command(BaseCommand, PopulateScriptTemplate): """Skip bulk update since we need to manually save each field. Our EPP logic is tied to an override of .save(), and this also associates with our caching logic for this area of the code. - + Since bulk update does not trigger .save() for each field, we have to call it manually. """