From 2995792375830a4b6247059d5473ca48df31b627 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:19:59 -0700 Subject: [PATCH 01/12] Add DomainGroup and Suborg models --- src/registrar/admin.py | 23 +++++++++++ .../0105_suborganization_domaingroup.py | 41 +++++++++++++++++++ src/registrar/models/__init__.py | 6 +++ src/registrar/models/domain_group.py | 33 +++++++++++++++ src/registrar/models/portfolio.py | 3 ++ src/registrar/models/suborganization.py | 23 +++++++++++ 6 files changed, 129 insertions(+) create mode 100644 src/registrar/migrations/0105_suborganization_domaingroup.py create mode 100644 src/registrar/models/domain_group.py create mode 100644 src/registrar/models/suborganization.py diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 215239d662..c0f52c6963 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -2655,6 +2655,27 @@ class Meta: model = models.WaffleFlag fields = "__all__" +class DomainGroupResource(resources.ModelResource): + """defines how each field in the referenced model should be mapped to the corresponding fields in the + import/export file""" + + class Meta: + model = models.DomainGroup + +class DomainGroupAdmin(ListHeaderAdmin, ImportExportModelAdmin): + resource_classes = [DomainGroupResource] + list_display = ["name", "portfolio"] + +class SuborganizationResource(resources.ModelResource): + """defines how each field in the referenced model should be mapped to the corresponding fields in the + import/export file""" + + class Meta: + model = models.Suborganization + +class SuborganizationAdmin(ListHeaderAdmin, ImportExportModelAdmin): + resource_classes = [SuborganizationResource] + list_display = ["name", "portfolio"] admin.site.unregister(LogEntry) # Unregister the default registration @@ -2679,6 +2700,8 @@ class Meta: admin.site.register(models.TransitionDomain, TransitionDomainAdmin) admin.site.register(models.VerifiedByStaff, VerifiedByStaffAdmin) admin.site.register(models.Portfolio, PortfolioAdmin) +admin.site.register(models.DomainGroup, DomainGroupAdmin) +admin.site.register(models.Suborganization, SuborganizationAdmin) # Register our custom waffle implementations admin.site.register(models.WaffleFlag, WaffleFlagAdmin) diff --git a/src/registrar/migrations/0105_suborganization_domaingroup.py b/src/registrar/migrations/0105_suborganization_domaingroup.py new file mode 100644 index 0000000000..3d99a40011 --- /dev/null +++ b/src/registrar/migrations/0105_suborganization_domaingroup.py @@ -0,0 +1,41 @@ +# Generated by Django 4.2.10 on 2024-06-20 21:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0104_create_groups_v13"), + ] + + operations = [ + migrations.CreateModel( + name="Suborganization", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("name", models.CharField(blank=True, help_text="Domain group", null=True, unique=True)), + ("portfolio", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="registrar.portfolio")), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="DomainGroup", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("name", models.CharField(blank=True, help_text="Domain group", null=True, unique=True)), + ("domains", models.ManyToManyField(blank=True, to="registrar.domaininformation")), + ("portfolio", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="registrar.portfolio")), + ], + options={ + "unique_together": {("name", "portfolio")}, + }, + ), + ] diff --git a/src/registrar/models/__init__.py b/src/registrar/models/__init__.py index b2cffaf32c..2af89ce00b 100644 --- a/src/registrar/models/__init__.py +++ b/src/registrar/models/__init__.py @@ -17,6 +17,8 @@ from .verified_by_staff import VerifiedByStaff from .waffle_flag import WaffleFlag from .portfolio import Portfolio +from .domain_group import DomainGroup +from .suborganization import Suborganization __all__ = [ @@ -38,6 +40,8 @@ "VerifiedByStaff", "WaffleFlag", "Portfolio", + "DomainGroup", + "Suborganization" ] auditlog.register(Contact) @@ -58,3 +62,5 @@ auditlog.register(VerifiedByStaff) auditlog.register(WaffleFlag) auditlog.register(Portfolio) +auditlog.register(DomainGroup) +auditlog.register(Suborganization) diff --git a/src/registrar/models/domain_group.py b/src/registrar/models/domain_group.py new file mode 100644 index 0000000000..aa6d64e1f7 --- /dev/null +++ b/src/registrar/models/domain_group.py @@ -0,0 +1,33 @@ +from django.db import models +from .utility.time_stamped_model import TimeStampedModel +from registrar.models.portfolio import Portfolio +from registrar.models.domain_information import DomainInformation + + +class DomainGroup(TimeStampedModel): + + class Meta: + unique_together = [("name", "portfolio")] + + """ + TODO: Write DomainGroup description + """ + name = models.CharField( + null=True, + blank=True, + unique=True, + help_text="Domain group", + ) + + portfolio = models.ForeignKey( + "registrar.Portfolio", + on_delete=models.PROTECT + ) + + domains = models.ManyToManyField( + "registrar.DomainInformation", + blank=True + ) + + def __str__(self) -> str: + return f"{self.name}" \ No newline at end of file diff --git a/src/registrar/models/portfolio.py b/src/registrar/models/portfolio.py index a054229604..0ea036bb79 100644 --- a/src/registrar/models/portfolio.py +++ b/src/registrar/models/portfolio.py @@ -97,3 +97,6 @@ class Portfolio(TimeStampedModel): verbose_name="security contact e-mail", max_length=320, ) + + def __str__(self) -> str: + return f"{self.organization_name}" diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py new file mode 100644 index 0000000000..6ebae62e29 --- /dev/null +++ b/src/registrar/models/suborganization.py @@ -0,0 +1,23 @@ +from django.db import models +from .utility.time_stamped_model import TimeStampedModel +from registrar.models.portfolio import Portfolio + + +class Suborganization(TimeStampedModel): + """ + TODO: Write DomainGroup description + """ + name = models.CharField( + null=True, + blank=True, + unique=True, + help_text="Domain group", + ) + + portfolio = models.ForeignKey( + "registrar.Portfolio", + on_delete=models.PROTECT, + ) + + def __str__(self) -> str: + return f"{self.name}" From b48802e0d7cfe73b2b7f44bb015c28145e4996b6 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:21:57 -0700 Subject: [PATCH 02/12] Change suborganization name help text --- src/registrar/migrations/0105_suborganization_domaingroup.py | 4 ++-- src/registrar/models/suborganization.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/migrations/0105_suborganization_domaingroup.py b/src/registrar/migrations/0105_suborganization_domaingroup.py index 3d99a40011..95d16e3b4c 100644 --- a/src/registrar/migrations/0105_suborganization_domaingroup.py +++ b/src/registrar/migrations/0105_suborganization_domaingroup.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-06-20 21:18 +# Generated by Django 4.2.10 on 2024-06-20 21:21 from django.db import migrations, models import django.db.models.deletion @@ -17,7 +17,7 @@ class Migration(migrations.Migration): ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), - ("name", models.CharField(blank=True, help_text="Domain group", null=True, unique=True)), + ("name", models.CharField(blank=True, help_text="Suborganization", null=True, unique=True)), ("portfolio", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="registrar.portfolio")), ], options={ diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py index 6ebae62e29..15612e7d2a 100644 --- a/src/registrar/models/suborganization.py +++ b/src/registrar/models/suborganization.py @@ -11,7 +11,7 @@ class Suborganization(TimeStampedModel): null=True, blank=True, unique=True, - help_text="Domain group", + help_text="Suborganization", ) portfolio = models.ForeignKey( From 0a80cb1c62b68c0dbccfbc237dd98ffee4caf0ad Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:42:27 -0700 Subject: [PATCH 03/12] Update descriptions of DomainGroup and Suborg --- src/registrar/models/domain_group.py | 2 +- src/registrar/models/suborganization.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/models/domain_group.py b/src/registrar/models/domain_group.py index aa6d64e1f7..f43627251a 100644 --- a/src/registrar/models/domain_group.py +++ b/src/registrar/models/domain_group.py @@ -10,7 +10,7 @@ class Meta: unique_together = [("name", "portfolio")] """ - TODO: Write DomainGroup description + Organized group of domains. """ name = models.CharField( null=True, diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py index 15612e7d2a..c90a76f27f 100644 --- a/src/registrar/models/suborganization.py +++ b/src/registrar/models/suborganization.py @@ -5,7 +5,7 @@ class Suborganization(TimeStampedModel): """ - TODO: Write DomainGroup description + Suborganization under an organization (portfolio) """ name = models.CharField( null=True, From a2a43b66b4a394bd8683178584e2241419125483 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:43:22 -0700 Subject: [PATCH 04/12] Fix lint errors --- src/registrar/admin.py | 5 +++++ src/registrar/models/__init__.py | 2 +- src/registrar/models/domain_group.py | 12 +++--------- src/registrar/models/suborganization.py | 1 + 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index c0f52c6963..b1dcb1f6e8 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -2655,6 +2655,7 @@ class Meta: model = models.WaffleFlag fields = "__all__" + class DomainGroupResource(resources.ModelResource): """defines how each field in the referenced model should be mapped to the corresponding fields in the import/export file""" @@ -2662,10 +2663,12 @@ class DomainGroupResource(resources.ModelResource): class Meta: model = models.DomainGroup + class DomainGroupAdmin(ListHeaderAdmin, ImportExportModelAdmin): resource_classes = [DomainGroupResource] list_display = ["name", "portfolio"] + class SuborganizationResource(resources.ModelResource): """defines how each field in the referenced model should be mapped to the corresponding fields in the import/export file""" @@ -2673,10 +2676,12 @@ class SuborganizationResource(resources.ModelResource): class Meta: model = models.Suborganization + class SuborganizationAdmin(ListHeaderAdmin, ImportExportModelAdmin): resource_classes = [SuborganizationResource] list_display = ["name", "portfolio"] + admin.site.unregister(LogEntry) # Unregister the default registration admin.site.register(LogEntry, CustomLogEntryAdmin) diff --git a/src/registrar/models/__init__.py b/src/registrar/models/__init__.py index 2af89ce00b..3767398265 100644 --- a/src/registrar/models/__init__.py +++ b/src/registrar/models/__init__.py @@ -41,7 +41,7 @@ "WaffleFlag", "Portfolio", "DomainGroup", - "Suborganization" + "Suborganization", ] auditlog.register(Contact) diff --git a/src/registrar/models/domain_group.py b/src/registrar/models/domain_group.py index f43627251a..7f77cdc16e 100644 --- a/src/registrar/models/domain_group.py +++ b/src/registrar/models/domain_group.py @@ -19,15 +19,9 @@ class Meta: help_text="Domain group", ) - portfolio = models.ForeignKey( - "registrar.Portfolio", - on_delete=models.PROTECT - ) + portfolio = models.ForeignKey("registrar.Portfolio", on_delete=models.PROTECT) - domains = models.ManyToManyField( - "registrar.DomainInformation", - blank=True - ) + domains = models.ManyToManyField("registrar.DomainInformation", blank=True) def __str__(self) -> str: - return f"{self.name}" \ No newline at end of file + return f"{self.name}" diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py index c90a76f27f..c731c64b3f 100644 --- a/src/registrar/models/suborganization.py +++ b/src/registrar/models/suborganization.py @@ -7,6 +7,7 @@ class Suborganization(TimeStampedModel): """ Suborganization under an organization (portfolio) """ + name = models.CharField( null=True, blank=True, From b8588b326628e2c3bc1e6b506f04f462aa12cec4 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Thu, 20 Jun 2024 14:46:24 -0700 Subject: [PATCH 05/12] Remove unused imports --- src/registrar/models/domain_group.py | 2 -- src/registrar/models/suborganization.py | 1 - 2 files changed, 3 deletions(-) diff --git a/src/registrar/models/domain_group.py b/src/registrar/models/domain_group.py index 7f77cdc16e..61bc906614 100644 --- a/src/registrar/models/domain_group.py +++ b/src/registrar/models/domain_group.py @@ -1,7 +1,5 @@ from django.db import models from .utility.time_stamped_model import TimeStampedModel -from registrar.models.portfolio import Portfolio -from registrar.models.domain_information import DomainInformation class DomainGroup(TimeStampedModel): diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py index c731c64b3f..c96ea19f34 100644 --- a/src/registrar/models/suborganization.py +++ b/src/registrar/models/suborganization.py @@ -1,6 +1,5 @@ from django.db import models from .utility.time_stamped_model import TimeStampedModel -from registrar.models.portfolio import Portfolio class Suborganization(TimeStampedModel): From f0756683add2878bd75eaeb467f4b6ac29ab3f3d Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:06:11 -0700 Subject: [PATCH 06/12] Create new user groups migration for DomainGroup and Suborg --- .../migrations/0106_create_groups_v14.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/registrar/migrations/0106_create_groups_v14.py diff --git a/src/registrar/migrations/0106_create_groups_v14.py b/src/registrar/migrations/0106_create_groups_v14.py new file mode 100644 index 0000000000..0ce3bafa56 --- /dev/null +++ b/src/registrar/migrations/0106_create_groups_v14.py @@ -0,0 +1,37 @@ +# This migration creates the create_full_access_group and create_cisa_analyst_group groups +# It is dependent on 0079 (which populates federal agencies) +# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS +# in the user_group model then: +# [NOT RECOMMENDED] +# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions +# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups +# step 3: fake run the latest migration in the migrations list +# [RECOMMENDED] +# Alternatively: +# step 1: duplicate the migration that loads data +# step 2: docker-compose exec app ./manage.py migrate + +from django.db import migrations +from registrar.models import UserGroup +from typing import Any + + +# For linting: RunPython expects a function reference, +# so let's give it one +def create_groups(apps, schema_editor) -> Any: + UserGroup.create_cisa_analyst_group(apps, schema_editor) + UserGroup.create_full_access_group(apps, schema_editor) + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0103_portfolio_domaininformation_portfolio_and_more"), + ] + + operations = [ + migrations.RunPython( + create_groups, + reverse_code=migrations.RunPython.noop, + atomic=True, + ), + ] From c220a8a9ac74679d6f73803cba834207ae845243 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:10:19 -0700 Subject: [PATCH 07/12] Correct usergroup migration dependency --- src/registrar/migrations/0106_create_groups_v14.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/migrations/0106_create_groups_v14.py b/src/registrar/migrations/0106_create_groups_v14.py index 0ce3bafa56..816a49ac81 100644 --- a/src/registrar/migrations/0106_create_groups_v14.py +++ b/src/registrar/migrations/0106_create_groups_v14.py @@ -25,7 +25,7 @@ def create_groups(apps, schema_editor) -> Any: class Migration(migrations.Migration): dependencies = [ - ("registrar", "0103_portfolio_domaininformation_portfolio_and_more"), + ("registrar", "0105_suborganization_domaingroup"), ] operations = [ From 53991c127c2892dc2565bbb03ab69f20194055b3 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:39:12 -0700 Subject: [PATCH 08/12] Remove blank/null option and add max length to Suborg --- src/registrar/models/suborganization.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py index c96ea19f34..b1e0109534 100644 --- a/src/registrar/models/suborganization.py +++ b/src/registrar/models/suborganization.py @@ -8,9 +8,8 @@ class Suborganization(TimeStampedModel): """ name = models.CharField( - null=True, - blank=True, unique=True, + max_length=1000, help_text="Suborganization", ) From 5712596d19e6a115d1955f4c6a261c9628931ac8 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:06:25 -0700 Subject: [PATCH 09/12] Remove blank and null options from Domain Group --- src/registrar/models/domain_group.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/registrar/models/domain_group.py b/src/registrar/models/domain_group.py index 61bc906614..8699c0337d 100644 --- a/src/registrar/models/domain_group.py +++ b/src/registrar/models/domain_group.py @@ -11,8 +11,6 @@ class Meta: Organized group of domains. """ name = models.CharField( - null=True, - blank=True, unique=True, help_text="Domain group", ) From f60b79e13baac166241d6e26a4b856fd15a2d0a5 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:15:55 -0700 Subject: [PATCH 10/12] Readd migrations --- .../migrations/0105_suborganization_domaingroup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/migrations/0105_suborganization_domaingroup.py b/src/registrar/migrations/0105_suborganization_domaingroup.py index 95d16e3b4c..471f9379c0 100644 --- a/src/registrar/migrations/0105_suborganization_domaingroup.py +++ b/src/registrar/migrations/0105_suborganization_domaingroup.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-06-20 21:21 +# Generated by Django 4.2.10 on 2024-06-21 18:15 from django.db import migrations, models import django.db.models.deletion @@ -17,7 +17,7 @@ class Migration(migrations.Migration): ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), - ("name", models.CharField(blank=True, help_text="Suborganization", null=True, unique=True)), + ("name", models.CharField(help_text="Suborganization", max_length=1000, unique=True)), ("portfolio", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="registrar.portfolio")), ], options={ @@ -30,7 +30,7 @@ class Migration(migrations.Migration): ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), - ("name", models.CharField(blank=True, help_text="Domain group", null=True, unique=True)), + ("name", models.CharField(help_text="Domain group", unique=True)), ("domains", models.ManyToManyField(blank=True, to="registrar.domaininformation")), ("portfolio", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="registrar.portfolio")), ], From 60bbf5f0fc0d2885df75154d8d899f11ff9a5e87 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 21 Jun 2024 16:44:09 -0700 Subject: [PATCH 11/12] Reorder description comment for domain group --- src/registrar/models/domain_group.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/models/domain_group.py b/src/registrar/models/domain_group.py index 8699c0337d..16b1fd2030 100644 --- a/src/registrar/models/domain_group.py +++ b/src/registrar/models/domain_group.py @@ -3,13 +3,13 @@ class DomainGroup(TimeStampedModel): + """ + Organized group of domains. + """ class Meta: unique_together = [("name", "portfolio")] - """ - Organized group of domains. - """ name = models.CharField( unique=True, help_text="Domain group", From 69b81ba6d6803583024b282b872891f16ca3edde Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:28:28 -0700 Subject: [PATCH 12/12] Remove import/export resource for domain group and suborg admin classes --- src/registrar/admin.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index b1dcb1f6e8..686545f77d 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -2656,29 +2656,11 @@ class Meta: fields = "__all__" -class DomainGroupResource(resources.ModelResource): - """defines how each field in the referenced model should be mapped to the corresponding fields in the - import/export file""" - - class Meta: - model = models.DomainGroup - - class DomainGroupAdmin(ListHeaderAdmin, ImportExportModelAdmin): - resource_classes = [DomainGroupResource] list_display = ["name", "portfolio"] -class SuborganizationResource(resources.ModelResource): - """defines how each field in the referenced model should be mapped to the corresponding fields in the - import/export file""" - - class Meta: - model = models.Suborganization - - class SuborganizationAdmin(ListHeaderAdmin, ImportExportModelAdmin): - resource_classes = [SuborganizationResource] list_display = ["name", "portfolio"]