From e70fb3ae6b49c46b92216856df6fb63aea68ff8d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 09:51:34 -0600 Subject: [PATCH 01/16] Add update --- src/registrar/admin.py | 5 ++-- .../copy_names_from_contacts_to_users.py | 26 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 2d6559570..2f3aec9f0 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -594,7 +594,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): None, {"fields": ("username", "password", "status", "verification_type")}, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), ( "Permissions", { @@ -625,7 +625,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): ) }, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), ( "Permissions", { @@ -654,6 +654,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): "middle_name", "last_name", "title", + "phone", "email", "Permissions", "is_active", diff --git a/src/registrar/management/commands/copy_names_from_contacts_to_users.py b/src/registrar/management/commands/copy_names_from_contacts_to_users.py index 50e1bea3d..602a3f0b7 100644 --- a/src/registrar/management/commands/copy_names_from_contacts_to_users.py +++ b/src/registrar/management/commands/copy_names_from_contacts_to_users.py @@ -10,6 +10,7 @@ from registrar.management.commands.utility.terminal_helper import ( ) from registrar.models.contact import Contact from registrar.models.user import User +from registrar.models.utility.domain_helper import DomainHelper logger = logging.getLogger(__name__) @@ -110,15 +111,22 @@ class Command(BaseCommand): {TerminalColors.ENDC}""", # noqa ) - # ---- UPDATE THE USER IF IT DOES NOT HAVE A FIRST AND LAST NAMES - # ---- LET'S KEEP A LIGHT TOUCH - if not eligible_user.first_name and not eligible_user.last_name: - # (expression has type "str | None", variable has type "str | int | Combinable") - # so we'll ignore type - eligible_user.first_name = contact.first_name # type: ignore - eligible_user.last_name = contact.last_name # type: ignore - eligible_user.save() - processed_user = eligible_user + + # Get the fields that exist on both User and Contact. Excludes id. + common_fields = DomainHelper.get_common_fields(User, Contact) + if "email" in common_fields: + # Don't change the email field. + common_fields.remove("email") + + for field in common_fields: + # Grab the value that contact has stored for this field + new_value = getattr(contact, field) + + # Set it on the user field + setattr(eligible_user, field, new_value) + + eligible_user.save() + processed_user = eligible_user return ( eligible_user, From ddd3ed450ca08a3b488b1149893a47dfe7af64af Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 10:39:01 -0600 Subject: [PATCH 02/16] Add faster copy script --- .../commands/copy_contacts_to_users.py | 50 +++++++++++++++++++ .../commands/populate_verification_type.py | 8 +-- .../commands/utility/terminal_helper.py | 28 +++++++---- 3 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 src/registrar/management/commands/copy_contacts_to_users.py diff --git a/src/registrar/management/commands/copy_contacts_to_users.py b/src/registrar/management/commands/copy_contacts_to_users.py new file mode 100644 index 000000000..46c1c4cf4 --- /dev/null +++ b/src/registrar/management/commands/copy_contacts_to_users.py @@ -0,0 +1,50 @@ +import logging +from django.core.management import BaseCommand +from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors +from registrar.models import User, Contact +from registrar.models.utility.domain_helper import DomainHelper + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand, PopulateScriptTemplate): + help = "Loops through each valid User object and updates its verification_type value" + + # Get the fields that exist on both User and Contact. Excludes id. + common_fields = DomainHelper.get_common_fields(User, Contact) + + def handle(self, **kwargs): + """Loops through each valid User object and updates its verification_type value""" + + # Don't change the email field. + if "email" in self.common_fields: + self.common_fields.remove("email") + + filter_condition = {"contact__isnull": False} + self.mass_populate_field(User, filter_condition, self.common_fields) + + skipped_users = User.objects.filter(contact__isnull=True) + + if skipped_users and len(skipped_users) > 0: + logger.warning( + f"""{TerminalColors.YELLOW} + ===== SKIPPED USERS ===== + {list(skipped_users)} + + {TerminalColors.ENDC} + """, + ) + + def populate_field(self, object_to_update): + """Defines how we update the user field on mass_populate_field()""" + new_value = None + for field in self.common_fields: + # Grab the value that contact has stored for this field + new_value = getattr(object_to_update.contact, field) + + # Set it on the user field + setattr(object_to_update, field, new_value) + + logger.info( + f"{TerminalColors.OKCYAN}Updating {object_to_update}{TerminalColors.ENDC}" + ) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index b61521977..3dc4e268b 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -14,10 +14,10 @@ class Command(BaseCommand, PopulateScriptTemplate): filter_condition = {"verification_type__isnull": True} self.mass_populate_field(User, filter_condition, ["verification_type"]) - def populate_field(self, field_to_update): + def populate_field(self, object_to_update): """Defines how we update the verification_type field""" - field_to_update.set_user_verification_type() + object_to_update.set_user_verification_type() logger.info( - f"{TerminalColors.OKCYAN}Updating {field_to_update} => " - f"{field_to_update.verification_type}{TerminalColors.OKCYAN}" + f"{TerminalColors.OKCYAN}Updating {object_to_update} => " + f"{object_to_update.verification_type}{TerminalColors.OKCYAN}" ) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index db3e4a9d3..26f60232c 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -64,14 +64,22 @@ class PopulateScriptTemplate(ABC): Contains an ABC for generic populate scripts """ - def mass_populate_field(self, sender, filter_conditions, fields_to_update): + def get_objects_to_update(self, sender, filter_conditions): + """Given a model of type 'object', perform a filter operation on + filter_conditions and return the result. + + For example: User.objects.filter(contact__isnull: False) + """ + return sender.objects.filter(**filter_conditions) + + def mass_populate_field(self, sender, filter_conditions, objects_to_update): """Loops through each valid "sender" object - specified by filter_conditions - and updates fields defined by fields_to_update using populate_function. You must define populate_field before you can use this function. """ - objects = sender.objects.filter(**filter_conditions) + objects = self.get_objects_to_update(sender, filter_conditions) # Code execution will stop here if the user prompts "N" TerminalHelper.prompt_for_execution( @@ -79,7 +87,7 @@ class PopulateScriptTemplate(ABC): info_to_inspect=f""" ==Proposed Changes== Number of {sender} objects to change: {len(objects)} - These fields will be updated on each record: {fields_to_update} + These fields will be updated on each record: {objects_to_update} """, prompt_title="Do you wish to patch this data?", ) @@ -87,23 +95,23 @@ class PopulateScriptTemplate(ABC): to_update: List[sender] = [] failed_to_update: List[sender] = [] - for updated_object in objects: + for item in objects: try: - self.populate_field(updated_object) - to_update.append(updated_object) + self.populate_field(item) + to_update.append(item) except Exception as err: - failed_to_update.append(updated_object) + failed_to_update.append(item) logger.error(err) - logger.error(f"{TerminalColors.FAIL}" f"Failed to update {updated_object}" f"{TerminalColors.ENDC}") + logger.error(f"{TerminalColors.FAIL}" f"Failed to update {item}" f"{TerminalColors.ENDC}") # Do a bulk update on the first_ready field - ScriptDataHelper.bulk_update_fields(sender, to_update, fields_to_update) + ScriptDataHelper.bulk_update_fields(sender, to_update, objects_to_update) # Log what happened TerminalHelper.log_script_run_summary(to_update, failed_to_update, skipped=[], debug=True) @abstractmethod - def populate_field(self, field_to_update): + def populate_field(self, object_to_update): """Defines how we update each field. Must be defined before using mass_populate_field.""" raise NotImplementedError From b57219d46570fcc52e057e302658f281949d7206 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 10:44:15 -0600 Subject: [PATCH 03/16] Update terminal_helper.py --- .../commands/utility/terminal_helper.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 26f60232c..62bb1a713 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -72,7 +72,7 @@ class PopulateScriptTemplate(ABC): """ return sender.objects.filter(**filter_conditions) - def mass_populate_field(self, sender, filter_conditions, objects_to_update): + def mass_populate_field(self, sender, filter_conditions, fields_to_update): """Loops through each valid "sender" object - specified by filter_conditions - and updates fields defined by fields_to_update using populate_function. @@ -87,7 +87,7 @@ class PopulateScriptTemplate(ABC): info_to_inspect=f""" ==Proposed Changes== Number of {sender} objects to change: {len(objects)} - These fields will be updated on each record: {objects_to_update} + These fields will be updated on each record: {fields_to_update} """, prompt_title="Do you wish to patch this data?", ) @@ -95,17 +95,17 @@ class PopulateScriptTemplate(ABC): to_update: List[sender] = [] failed_to_update: List[sender] = [] - for item in objects: + for updated_object in objects: try: - self.populate_field(item) - to_update.append(item) + self.populate_field(updated_object) + to_update.append(updated_object) except Exception as err: - failed_to_update.append(item) + failed_to_update.append(updated_object) logger.error(err) - logger.error(f"{TerminalColors.FAIL}" f"Failed to update {item}" f"{TerminalColors.ENDC}") + logger.error(f"{TerminalColors.FAIL}" f"Failed to update {updated_object}" f"{TerminalColors.ENDC}") # Do a bulk update on the first_ready field - ScriptDataHelper.bulk_update_fields(sender, to_update, objects_to_update) + ScriptDataHelper.bulk_update_fields(sender, to_update, fields_to_update) # Log what happened TerminalHelper.log_script_run_summary(to_update, failed_to_update, skipped=[], debug=True) From 23988ad6b31fcd5bc0d55005cce821e28580c5f3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 10:44:55 -0600 Subject: [PATCH 04/16] Update admin.py --- src/registrar/admin.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 2f3aec9f0..2d6559570 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -594,7 +594,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): None, {"fields": ("username", "password", "status", "verification_type")}, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "title")}), ( "Permissions", { @@ -625,7 +625,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): ) }, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "title")}), ( "Permissions", { @@ -654,7 +654,6 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): "middle_name", "last_name", "title", - "phone", "email", "Permissions", "is_active", From c2dd91215ad5000074e671cce66561406dbd2a8c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 11:17:48 -0600 Subject: [PATCH 05/16] Fix up unit test --- src/registrar/models/contact.py | 10 ++++ src/registrar/signals.py | 4 ++ .../test_copy_names_from_contacts_to_users.py | 50 ++++++++++++++----- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/registrar/models/contact.py b/src/registrar/models/contact.py index a5a6ff16c..477048d05 100644 --- a/src/registrar/models/contact.py +++ b/src/registrar/models/contact.py @@ -119,11 +119,21 @@ class Contact(TimeStampedModel): self.user.last_name = self.last_name updated = True + # Update middle_name if necessary + if not self.user.middle_name: + self.user.middle_name = self.middle_name + updated = True + # Update phone if necessary if not self.user.phone: self.user.phone = self.phone updated = True + # Update title if necessary + if not self.user.title: + self.user.title = self.title + updated = True + # Save user if any updates were made if updated: self.user.save() diff --git a/src/registrar/signals.py b/src/registrar/signals.py index 4e7768ef4..bc0480b2a 100644 --- a/src/registrar/signals.py +++ b/src/registrar/signals.py @@ -24,9 +24,11 @@ def handle_profile(sender, instance, **kwargs): """ first_name = getattr(instance, "first_name", "") + middle_name = getattr(instance, "middle_name", "") last_name = getattr(instance, "last_name", "") email = getattr(instance, "email", "") phone = getattr(instance, "phone", "") + title = getattr(instance, "title", "") is_new_user = kwargs.get("created", False) @@ -39,9 +41,11 @@ def handle_profile(sender, instance, **kwargs): Contact.objects.create( user=instance, first_name=first_name, + middle_name=middle_name, last_name=last_name, email=email, phone=phone, + title=title, ) if len(contacts) >= 1 and is_new_user: # a matching contact diff --git a/src/registrar/tests/test_copy_names_from_contacts_to_users.py b/src/registrar/tests/test_copy_names_from_contacts_to_users.py index 032203f4e..7fcbede1e 100644 --- a/src/registrar/tests/test_copy_names_from_contacts_to_users.py +++ b/src/registrar/tests/test_copy_names_from_contacts_to_users.py @@ -23,15 +23,32 @@ class TestDataUpdates(TestCase): self.bs_user = User.objects.create() self.contact1 = Contact.objects.create( - user=self.user1, email="email1@igorville.gov", first_name="first1", last_name="last1" + user=self.user1, + email="email1@igorville.gov", + first_name="first1", + last_name="last1", + middle_name="middle1", + title="title1", ) self.contact2 = Contact.objects.create( - user=self.user2, email="email2@igorville.gov", first_name="first2", last_name="last2" + user=self.user2, + email="email2@igorville.gov", + first_name="first2", + last_name="last2", + middle_name="middle2", + title="title2", ) self.contact3 = Contact.objects.create( - user=self.user3, email="email3@igorville.gov", first_name="first3", last_name="last3" + user=self.user3, + email="email3@igorville.gov", + first_name="first3", + last_name="last3", + middle_name="middle3", + title="title3", + ) + self.contact4 = Contact.objects.create( + email="email4@igorville.gov", first_name="first4", last_name="last4", middle_name="middle4", title="title4" ) - self.contact4 = Contact.objects.create(email="email4@igorville.gov", first_name="first4", last_name="last4") self.command = Command() @@ -42,14 +59,15 @@ class TestDataUpdates(TestCase): Contact.objects.all().delete() def test_script_updates_linked_users(self): - """Test the script that copies contacts' first and last names into associated users that - are eligible (first or last are blank or undefined)""" + """Test the script that copies contact information to the user object""" # Set up the users' first and last names here so # they that they don't get overwritten by Contact's save() # User with no first or last names self.user1.first_name = "" self.user1.last_name = "" + self.user1.title = "dummytitle" + self.user1.middle_name = "dummymiddle" self.user1.save() # User with a first name but no last name @@ -87,12 +105,20 @@ class TestDataUpdates(TestCase): # The user that has no first and last names will get them from the contact self.assertEqual(self.user1.first_name, "first1") self.assertEqual(self.user1.last_name, "last1") - # The user that has a first but no last will be left alone - self.assertEqual(self.user2.first_name, "First name but no last name") - self.assertEqual(self.user2.last_name, "") - # The user that has a first and a last will be left alone - self.assertEqual(self.user3.first_name, "An existing first name") - self.assertEqual(self.user3.last_name, "An existing last name") + self.assertEqual(self.user1.middle_name, "middle1") + self.assertEqual(self.user1.title, "title1") + # The user that has a first but no last will be updated + self.assertEqual(self.user2.first_name, "first2") + self.assertEqual(self.user2.last_name, "last2") + self.assertEqual(self.user2.middle_name, "middle2") + self.assertEqual(self.user2.title, "title2") + # The user that has a first and a last will be updated + self.assertEqual(self.user3.first_name, "first3") + self.assertEqual(self.user3.last_name, "last3") + self.assertEqual(self.user3.middle_name, "middle3") + self.assertEqual(self.user3.title, "title3") # The unlinked user will be left alone self.assertEqual(self.user4.first_name, "") self.assertEqual(self.user4.last_name, "") + self.assertEqual(self.user4.middle_name, None) + self.assertEqual(self.user4.title, None) From 194dfe2efd2f5d3243a7bcab904e7b1a922bbb40 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 11:24:00 -0600 Subject: [PATCH 06/16] Undo copy_contacts_to_users This script goes against the AC which just wants to modify the existing script, rather than this new one --- .../commands/copy_contacts_to_users.py | 50 ------------------- .../commands/populate_verification_type.py | 8 +-- .../commands/utility/terminal_helper.py | 10 +--- 3 files changed, 5 insertions(+), 63 deletions(-) delete mode 100644 src/registrar/management/commands/copy_contacts_to_users.py diff --git a/src/registrar/management/commands/copy_contacts_to_users.py b/src/registrar/management/commands/copy_contacts_to_users.py deleted file mode 100644 index 46c1c4cf4..000000000 --- a/src/registrar/management/commands/copy_contacts_to_users.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging -from django.core.management import BaseCommand -from registrar.management.commands.utility.terminal_helper import PopulateScriptTemplate, TerminalColors -from registrar.models import User, Contact -from registrar.models.utility.domain_helper import DomainHelper - -logger = logging.getLogger(__name__) - - -class Command(BaseCommand, PopulateScriptTemplate): - help = "Loops through each valid User object and updates its verification_type value" - - # Get the fields that exist on both User and Contact. Excludes id. - common_fields = DomainHelper.get_common_fields(User, Contact) - - def handle(self, **kwargs): - """Loops through each valid User object and updates its verification_type value""" - - # Don't change the email field. - if "email" in self.common_fields: - self.common_fields.remove("email") - - filter_condition = {"contact__isnull": False} - self.mass_populate_field(User, filter_condition, self.common_fields) - - skipped_users = User.objects.filter(contact__isnull=True) - - if skipped_users and len(skipped_users) > 0: - logger.warning( - f"""{TerminalColors.YELLOW} - ===== SKIPPED USERS ===== - {list(skipped_users)} - - {TerminalColors.ENDC} - """, - ) - - def populate_field(self, object_to_update): - """Defines how we update the user field on mass_populate_field()""" - new_value = None - for field in self.common_fields: - # Grab the value that contact has stored for this field - new_value = getattr(object_to_update.contact, field) - - # Set it on the user field - setattr(object_to_update, field, new_value) - - logger.info( - f"{TerminalColors.OKCYAN}Updating {object_to_update}{TerminalColors.ENDC}" - ) diff --git a/src/registrar/management/commands/populate_verification_type.py b/src/registrar/management/commands/populate_verification_type.py index 3dc4e268b..b61521977 100644 --- a/src/registrar/management/commands/populate_verification_type.py +++ b/src/registrar/management/commands/populate_verification_type.py @@ -14,10 +14,10 @@ class Command(BaseCommand, PopulateScriptTemplate): filter_condition = {"verification_type__isnull": True} self.mass_populate_field(User, filter_condition, ["verification_type"]) - def populate_field(self, object_to_update): + def populate_field(self, field_to_update): """Defines how we update the verification_type field""" - object_to_update.set_user_verification_type() + field_to_update.set_user_verification_type() logger.info( - f"{TerminalColors.OKCYAN}Updating {object_to_update} => " - f"{object_to_update.verification_type}{TerminalColors.OKCYAN}" + f"{TerminalColors.OKCYAN}Updating {field_to_update} => " + f"{field_to_update.verification_type}{TerminalColors.OKCYAN}" ) diff --git a/src/registrar/management/commands/utility/terminal_helper.py b/src/registrar/management/commands/utility/terminal_helper.py index 62bb1a713..eb82c5037 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -64,14 +64,6 @@ class PopulateScriptTemplate(ABC): Contains an ABC for generic populate scripts """ - def get_objects_to_update(self, sender, filter_conditions): - """Given a model of type 'object', perform a filter operation on - filter_conditions and return the result. - - For example: User.objects.filter(contact__isnull: False) - """ - return sender.objects.filter(**filter_conditions) - def mass_populate_field(self, sender, filter_conditions, fields_to_update): """Loops through each valid "sender" object - specified by filter_conditions - and updates fields defined by fields_to_update using populate_function. @@ -79,7 +71,7 @@ class PopulateScriptTemplate(ABC): You must define populate_field before you can use this function. """ - objects = self.get_objects_to_update(sender, filter_conditions) + objects = sender.objects.filter(**filter_conditions) # Code execution will stop here if the user prompts "N" TerminalHelper.prompt_for_execution( From 8ee7c537ac0f9bb11bc7ac22ec05291de3eb00fb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 11:27:12 -0600 Subject: [PATCH 07/16] 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 eb82c5037..db3e4a9d3 100644 --- a/src/registrar/management/commands/utility/terminal_helper.py +++ b/src/registrar/management/commands/utility/terminal_helper.py @@ -103,7 +103,7 @@ class PopulateScriptTemplate(ABC): TerminalHelper.log_script_run_summary(to_update, failed_to_update, skipped=[], debug=True) @abstractmethod - def populate_field(self, object_to_update): + def populate_field(self, field_to_update): """Defines how we update each field. Must be defined before using mass_populate_field.""" raise NotImplementedError From 0a079c0a8633864256baaecc843dd3d2b26e804f Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 28 May 2024 14:18:06 -0600 Subject: [PATCH 08/16] Lint --- .../management/commands/copy_names_from_contacts_to_users.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/registrar/management/commands/copy_names_from_contacts_to_users.py b/src/registrar/management/commands/copy_names_from_contacts_to_users.py index 602a3f0b7..384029400 100644 --- a/src/registrar/management/commands/copy_names_from_contacts_to_users.py +++ b/src/registrar/management/commands/copy_names_from_contacts_to_users.py @@ -111,7 +111,6 @@ class Command(BaseCommand): {TerminalColors.ENDC}""", # noqa ) - # Get the fields that exist on both User and Contact. Excludes id. common_fields = DomainHelper.get_common_fields(User, Contact) if "email" in common_fields: From 2b9ec086a2d2f7f76163b53d2145b04367998870 Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 3 Jun 2024 13:49:30 -0600 Subject: [PATCH 09/16] Updated order of domain request status options --- src/registrar/models/domain_request.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 2501cdc87..bd82ad0b7 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -34,14 +34,14 @@ class DomainRequest(TimeStampedModel): # Constants for choice fields class DomainRequestStatus(models.TextChoices): - STARTED = "started", "Started" - SUBMITTED = "submitted", "Submitted" IN_REVIEW = "in review", "In review" ACTION_NEEDED = "action needed", "Action needed" APPROVED = "approved", "Approved" - WITHDRAWN = "withdrawn", "Withdrawn" REJECTED = "rejected", "Rejected" INELIGIBLE = "ineligible", "Ineligible" + SUBMITTED = "submitted", "Submitted" + WITHDRAWN = "withdrawn", "Withdrawn" + STARTED = "started", "Started" class StateTerritoryChoices(models.TextChoices): ALABAMA = "AL", "Alabama (AL)" From 1adccd9d22ab067f4a5be778abe3ba518c292cef Mon Sep 17 00:00:00 2001 From: CocoByte Date: Mon, 3 Jun 2024 18:01:10 -0600 Subject: [PATCH 10/16] added migration --- .../0096_alter_domainrequest_status.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/registrar/migrations/0096_alter_domainrequest_status.py diff --git a/src/registrar/migrations/0096_alter_domainrequest_status.py b/src/registrar/migrations/0096_alter_domainrequest_status.py new file mode 100644 index 000000000..162584a38 --- /dev/null +++ b/src/registrar/migrations/0096_alter_domainrequest_status.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.10 on 2024-06-04 00:00 + +from django.db import migrations +import django_fsm + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0095_user_middle_name_user_title"), + ] + + operations = [ + migrations.AlterField( + model_name="domainrequest", + name="status", + field=django_fsm.FSMField( + choices=[ + ("in review", "In review"), + ("action needed", "Action needed"), + ("approved", "Approved"), + ("rejected", "Rejected"), + ("ineligible", "Ineligible"), + ("submitted", "Submitted"), + ("withdrawn", "Withdrawn"), + ("started", "Started"), + ], + default="started", + max_length=50, + ), + ), + ] From 6fbbe11dee302dca1237c8f46d96cb5c6dc1cc80 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:48:41 -0600 Subject: [PATCH 11/16] Add documentation on running the copy script --- docs/operations/data_migration.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/docs/operations/data_migration.md b/docs/operations/data_migration.md index e4543a28c..472362a79 100644 --- a/docs/operations/data_migration.md +++ b/docs/operations/data_migration.md @@ -668,3 +668,32 @@ Example: `cf ssh getgov-za` #### Step 1: Running the script ```docker-compose exec app ./manage.py populate_verification_type``` + + +## Copy names from contacts to users + +### Running on sandboxes + +#### Step 1: Login to CloudFoundry +```cf login -a api.fr.cloud.gov --sso``` + +#### Step 2: SSH into your environment +```cf ssh getgov-{space}``` + +Example: `cf ssh getgov-za` + +#### Step 3: Create a shell instance +```/tmp/lifecycle/shell``` + +#### Step 4: Running the script +```./manage.py copy_names_from_contacts_to_users --debug``` + +### Running locally + +#### Step 1: Running the script +```docker-compose exec app ./manage.py copy_names_from_contacts_to_users --debug``` + +##### Optional parameters +| | Parameter | Description | +|:-:|:-------------------------- |:----------------------------------------------------------------------------| +| 1 | **debug** | Increases logging detail. Defaults to False. | From 4df2f6f79a29ff2145327ae4e5b3d3f02be0e459 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:29:36 -0600 Subject: [PATCH 12/16] Update README.md --- docs/developer/README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/developer/README.md b/docs/developer/README.md index 31a94e6e7..e3e6a3955 100644 --- a/docs/developer/README.md +++ b/docs/developer/README.md @@ -320,16 +320,6 @@ it may help to resync your laptop with time.nist.gov: sudo sntp -sS time.nist.gov ``` -### Settings -The config for the connection pool exists inside the `settings.py` file. -| Name | Purpose | -| ------------------------ | ------------------------------------------------------------------------------------------------- | -| EPP_CONNECTION_POOL_SIZE | Determines the number of concurrent sockets that should exist in the pool. | -| POOL_KEEP_ALIVE | Determines the interval in which we ping open connections in seconds. Calculated as POOL_KEEP_ALIVE / EPP_CONNECTION_POOL_SIZE | -| POOL_TIMEOUT | Determines how long we try to keep a pool alive for, before restarting it. | - -Consider updating the `POOL_TIMEOUT` or `POOL_KEEP_ALIVE` periods if the pool often restarts. If the pool only restarts after a period of inactivity, update `POOL_KEEP_ALIVE`. If it restarts during the EPP call itself, then `POOL_TIMEOUT` needs to be updated. - ## Adding a S3 instance to your sandbox This can either be done through the CLI, or through the cloud.gov dashboard. Generally, it is better to do it through the dashboard as it handles app binding for you. From 56d2da79049c4fe2fc1e48dc20a2a911965a4842 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:57:55 -0600 Subject: [PATCH 13/16] Add phone --- src/registrar/admin.py | 4 ++-- src/registrar/tests/test_admin.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 2d6559570..7babe976a 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -594,7 +594,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): None, {"fields": ("username", "password", "status", "verification_type")}, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), ( "Permissions", { @@ -625,7 +625,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): ) }, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), ( "Permissions", { diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index e2d390471..3a74d964f 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -3534,7 +3534,7 @@ class TestMyUserAdmin(TestCase): ) }, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), ) From 594f7e70bcb3856008a3526168714ca3d1d18899 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 6 Jun 2024 12:39:46 -0600 Subject: [PATCH 14/16] Modify help text Some old code snuck in here. Fixing that --- .../migrations/0097_alter_user_phone.py | 19 +++++++++++++++++++ src/registrar/models/user.py | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/registrar/migrations/0097_alter_user_phone.py diff --git a/src/registrar/migrations/0097_alter_user_phone.py b/src/registrar/migrations/0097_alter_user_phone.py new file mode 100644 index 000000000..dfa5cfba8 --- /dev/null +++ b/src/registrar/migrations/0097_alter_user_phone.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.10 on 2024-06-06 18:38 + +from django.db import migrations +import phonenumber_field.modelfields + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0096_alter_contact_email_alter_contact_first_name_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="user", + name="phone", + field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None), + ), + ] diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 705d2011c..bb0276607 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -87,7 +87,6 @@ class User(AbstractUser): phone = PhoneNumberField( null=True, blank=True, - help_text="Phone", ) middle_name = models.CharField( From db20bdc1c4f36abcc60f2444856bccb1d4acd464 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 6 Jun 2024 12:42:34 -0600 Subject: [PATCH 15/16] Fix order --- src/registrar/admin.py | 4 ++-- src/registrar/tests/test_admin.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 5f4553593..22022dd94 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -594,7 +594,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): None, {"fields": ("username", "password", "status", "verification_type")}, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}), ( "Permissions", { @@ -625,7 +625,7 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin): ) }, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}), ( "Permissions", { diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 3a74d964f..79c545420 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -3534,7 +3534,7 @@ class TestMyUserAdmin(TestCase): ) }, ), - ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "email", "phone", "title")}), + ("Personal Info", {"fields": ("first_name", "middle_name", "last_name", "title", "email", "phone")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), ) From 7ee0feac73db002721a30a75726d97520eb25be8 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Fri, 7 Jun 2024 09:27:59 -0600 Subject: [PATCH 16/16] Fix migration --- ...inrequest_status.py => 0098_alter_domainrequest_status.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/registrar/migrations/{0096_alter_domainrequest_status.py => 0098_alter_domainrequest_status.py} (88%) diff --git a/src/registrar/migrations/0096_alter_domainrequest_status.py b/src/registrar/migrations/0098_alter_domainrequest_status.py similarity index 88% rename from src/registrar/migrations/0096_alter_domainrequest_status.py rename to src/registrar/migrations/0098_alter_domainrequest_status.py index 162584a38..19fa1ded2 100644 --- a/src/registrar/migrations/0096_alter_domainrequest_status.py +++ b/src/registrar/migrations/0098_alter_domainrequest_status.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-06-04 00:00 +# Generated by Django 4.2.10 on 2024-06-07 15:27 from django.db import migrations import django_fsm @@ -7,7 +7,7 @@ import django_fsm class Migration(migrations.Migration): dependencies = [ - ("registrar", "0095_user_middle_name_user_title"), + ("registrar", "0097_alter_user_phone"), ] operations = [