Skip to content

Commit

Permalink
Merge pull request #2374 from cisagov/rh/2258-update-ao-to-so
Browse files Browse the repository at this point in the history
ISSUE #2258: Update Authorizing Official to Senior Official
  • Loading branch information
therealslimhsiehdy authored Jul 2, 2024
2 parents 694cfa5 + ce09de9 commit 43697a5
Show file tree
Hide file tree
Showing 45 changed files with 565 additions and 442 deletions.
4 changes: 2 additions & 2 deletions docs/architecture/diagrams/model_timeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class DomainRequest {
--
creator (User)
investigator (User)
authorizing_official (Contact)
senior_official (Contact)
submitter (Contact)
other_contacts (Contacts)
approved_domain (Domain)
Expand Down Expand Up @@ -80,7 +80,7 @@ class Contact {
--
}
DomainRequest *-r-* Contact : authorizing_official, submitter, other_contacts
DomainRequest *-r-* Contact : senior_official, submitter, other_contacts
class DraftDomain {
Requested domain
Expand Down
102 changes: 84 additions & 18 deletions docs/architecture/diagrams/models_diagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ docker compose exec app ./manage.py generate_puml --include registrar
## How To regenerate the database svg image

1. Copy your puml file contents into the bottom of this file and replace the current code marked by `plantuml`
2. Run the following command
2. Navigate to the `diagram` folder and then run the following command below:

```bash
docker run -v $(pwd):$(pwd) -w $(pwd) -it plantuml/plantuml -tsvg models_diagram.md
Expand Down Expand Up @@ -103,6 +103,21 @@ class "registrar.PublicContact <Registrar>" as registrar.PublicContact #d6f4e9 {
registrar.PublicContact -- registrar.Domain
class "registrar.UserDomainRole <Registrar>" as registrar.UserDomainRole #d6f4e9 {
user domain role
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
~ user (ForeignKey)
~ domain (ForeignKey)
+ role (TextField)
--
}
registrar.UserDomainRole -- registrar.User
registrar.UserDomainRole -- registrar.Domain
class "registrar.Domain <Registrar>" as registrar.Domain #d6f4e9 {
domain
--
Expand All @@ -115,6 +130,7 @@ class "registrar.Domain <Registrar>" as registrar.Domain #d6f4e9 {
+ security_contact_registry_id (TextField)
+ deleted (DateField)
+ first_ready (DateField)
+ dsdata_last_change (TextField)
--
}
Expand All @@ -126,6 +142,7 @@ class "registrar.FederalAgency <Registrar>" as registrar.FederalAgency #d6f4e9 {
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ agency (CharField)
+ federal_type (CharField)
--
}
Expand All @@ -138,7 +155,10 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
+ updated_at (DateTimeField)
+ status (FSMField)
+ rejection_reason (TextField)
+ action_needed_reason (TextField)
+ action_needed_reason_email (TextField)
~ federal_agency (ForeignKey)
~ portfolio (ForeignKey)
~ creator (ForeignKey)
~ investigator (ForeignKey)
+ generic_org_type (CharField)
Expand All @@ -156,7 +176,7 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
+ zipcode (CharField)
+ urbanization (CharField)
+ about_your_organization (TextField)
~ authorizing_official (ForeignKey)
~ senior_official (ForeignKey)
~ approved_domain (OneToOneField)
~ requested_domain (OneToOneField)
~ submitter (ForeignKey)
Expand All @@ -165,6 +185,8 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
+ anything_else (TextField)
+ has_anything_else_text (BooleanField)
+ cisa_representative_email (EmailField)
+ cisa_representative_first_name (CharField)
+ cisa_representative_last_name (CharField)
+ has_cisa_representative (BooleanField)
+ is_policy_acknowledged (BooleanField)
+ submission_date (DateField)
Expand All @@ -175,6 +197,7 @@ class "registrar.DomainRequest <Registrar>" as registrar.DomainRequest #d6f4e9 {
--
}
registrar.DomainRequest -- registrar.FederalAgency
registrar.DomainRequest -- registrar.Portfolio
registrar.DomainRequest -- registrar.User
registrar.DomainRequest -- registrar.User
registrar.DomainRequest -- registrar.Contact
Expand All @@ -194,6 +217,7 @@ class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #
+ updated_at (DateTimeField)
~ federal_agency (ForeignKey)
~ creator (ForeignKey)
~ portfolio (ForeignKey)
~ domain_request (OneToOneField)
+ generic_org_type (CharField)
+ organization_type (CharField)
Expand All @@ -210,20 +234,25 @@ class "registrar.DomainInformation <Registrar>" as registrar.DomainInformation #
+ zipcode (CharField)
+ urbanization (CharField)
+ about_your_organization (TextField)
~ authorizing_official (ForeignKey)
~ senior_official (ForeignKey)
~ domain (OneToOneField)
~ submitter (ForeignKey)
+ purpose (TextField)
+ no_other_contacts_rationale (TextField)
+ anything_else (TextField)
+ has_anything_else_text (BooleanField)
+ cisa_representative_email (EmailField)
+ cisa_representative_first_name (CharField)
+ cisa_representative_last_name (CharField)
+ has_cisa_representative (BooleanField)
+ is_policy_acknowledged (BooleanField)
+ notes (TextField)
# other_contacts (ManyToManyField)
--
}
registrar.DomainInformation -- registrar.FederalAgency
registrar.DomainInformation -- registrar.User
registrar.DomainInformation -- registrar.Portfolio
registrar.DomainInformation -- registrar.DomainRequest
registrar.DomainInformation -- registrar.Contact
registrar.DomainInformation -- registrar.Domain
Expand All @@ -242,21 +271,6 @@ class "registrar.DraftDomain <Registrar>" as registrar.DraftDomain #d6f4e9 {
}
class "registrar.UserDomainRole <Registrar>" as registrar.UserDomainRole #d6f4e9 {
user domain role
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
~ user (ForeignKey)
~ domain (ForeignKey)
+ role (TextField)
--
}
registrar.UserDomainRole -- registrar.User
registrar.UserDomainRole -- registrar.Domain
class "registrar.DomainInvitation <Registrar>" as registrar.DomainInvitation #d6f4e9 {
domain invitation
--
Expand Down Expand Up @@ -388,6 +402,58 @@ class "registrar.WaffleFlag <Registrar>" as registrar.WaffleFlag #d6f4e9 {
registrar.WaffleFlag *--* registrar.User
class "registrar.Portfolio <Registrar>" as registrar.Portfolio #d6f4e9 {
portfolio
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
~ creator (ForeignKey)
+ notes (TextField)
~ federal_agency (ForeignKey)
+ organization_type (CharField)
+ organization_name (CharField)
+ address_line1 (CharField)
+ address_line2 (CharField)
+ city (CharField)
+ state_territory (CharField)
+ zipcode (CharField)
+ urbanization (CharField)
+ security_contact_email (EmailField)
--
}
registrar.Portfolio -- registrar.User
registrar.Portfolio -- registrar.FederalAgency
class "registrar.DomainGroup <Registrar>" as registrar.DomainGroup #d6f4e9 {
domain group
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ name (CharField)
~ portfolio (ForeignKey)
# domains (ManyToManyField)
--
}
registrar.DomainGroup -- registrar.Portfolio
registrar.DomainGroup *--* registrar.DomainInformation
class "registrar.Suborganization <Registrar>" as registrar.Suborganization #d6f4e9 {
suborganization
--
+ id (BigAutoField)
+ created_at (DateTimeField)
+ updated_at (DateTimeField)
+ name (CharField)
~ portfolio (ForeignKey)
--
}
registrar.Suborganization -- registrar.Portfolio
@enduml
```

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/diagrams/models_diagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/.pa11yci
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"http://localhost:8080/request/org_federal/",
"http://localhost:8080/request/org_election/",
"http://localhost:8080/request/org_contact/",
"http://localhost:8080/request/authorizing_official/",
"http://localhost:8080/request/senior_official/",
"http://localhost:8080/request/current_sites/",
"http://localhost:8080/request/dotgov_domain/",
"http://localhost:8080/request/purpose/",
Expand Down
12 changes: 6 additions & 6 deletions src/registrar/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ class AdminSortFields:
sort_mapping = {
# == Contact == #
"other_contacts": (Contact, _name_sort),
"authorizing_official": (Contact, _name_sort),
"senior_official": (Contact, _name_sort),
"submitter": (Contact, _name_sort),
# == User == #
"creator": (User, _name_sort),
Expand Down Expand Up @@ -1235,7 +1235,7 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
fieldsets = [
(None, {"fields": ["portfolio", "creator", "submitter", "domain_request", "notes"]}),
(".gov domain", {"fields": ["domain"]}),
("Contacts", {"fields": ["authorizing_official", "other_contacts", "no_other_contacts_rationale"]}),
("Contacts", {"fields": ["senior_official", "other_contacts", "no_other_contacts_rationale"]}),
("Background info", {"fields": ["anything_else"]}),
(
"Type of organization",
Expand Down Expand Up @@ -1309,7 +1309,7 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
autocomplete_fields = [
"creator",
"domain_request",
"authorizing_official",
"senior_official",
"domain",
"submitter",
]
Expand Down Expand Up @@ -1525,7 +1525,7 @@ def custom_election_board(self, obj):
"Contacts",
{
"fields": [
"authorizing_official",
"senior_official",
"other_contacts",
"no_other_contacts_rationale",
"cisa_representative_first_name",
Expand Down Expand Up @@ -1614,7 +1614,7 @@ def custom_election_board(self, obj):
"requested_domain",
"submitter",
"creator",
"authorizing_official",
"senior_official",
"investigator",
]
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
Expand Down Expand Up @@ -2039,7 +2039,7 @@ class DomainInformationInline(admin.StackedInline):
autocomplete_fields = [
"creator",
"domain_request",
"authorizing_official",
"senior_official",
"domain",
"submitter",
]
Expand Down
8 changes: 4 additions & 4 deletions src/registrar/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
(Step.ORGANIZATION_ELECTION, views.OrganizationElection),
(Step.ORGANIZATION_CONTACT, views.OrganizationContact),
(Step.ABOUT_YOUR_ORGANIZATION, views.AboutYourOrganization),
(Step.AUTHORIZING_OFFICIAL, views.AuthorizingOfficial),
(Step.SENIOR_OFFICIAL, views.SeniorOfficial),
(Step.CURRENT_SITES, views.CurrentSites),
(Step.DOTGOV_DOMAIN, views.DotgovDomain),
(Step.PURPOSE, views.Purpose),
Expand Down Expand Up @@ -183,9 +183,9 @@
name="domain-org-name-address",
),
path(
"domain/<int:pk>/authorizing-official",
views.DomainAuthorizingOfficialView.as_view(),
name="domain-authorizing-official",
"domain/<int:pk>/senior-official",
views.DomainSeniorOfficialView.as_view(),
name="domain-senior-official",
),
path(
"domain/<int:pk>/security-email",
Expand Down
10 changes: 5 additions & 5 deletions src/registrar/fixtures_domain_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class DomainRequestFixture:
# "purpose": None,
# "anything_else": None,
# "is_policy_acknowledged": None,
# "authorizing_official": None,
# "senior_official": None,
# "submitter": None,
# "other_contacts": [],
# "current_websites": [],
Expand Down Expand Up @@ -117,11 +117,11 @@ def _set_foreign_key_fields(cls, da: DomainRequest, app: dict, user: User):
if not da.investigator:
da.investigator = User.objects.get(username=user.username) if "investigator" in app else None

if not da.authorizing_official:
if "authorizing_official" in app and app["authorizing_official"] is not None:
da.authorizing_official, _ = Contact.objects.get_or_create(**app["authorizing_official"])
if not da.senior_official:
if "senior_official" in app and app["senior_official"] is not None:
da.senior_official, _ = Contact.objects.get_or_create(**app["senior_official"])
else:
da.authorizing_official = Contact.objects.create(**cls.fake_contact())
da.senior_official = Contact.objects.create(**cls.fake_contact())

if not da.submitter:
if "submitter" in app and app["submitter"] is not None:
Expand Down
2 changes: 1 addition & 1 deletion src/registrar/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
DomainSecurityEmailForm,
DomainOrgNameAddressForm,
ContactForm,
AuthorizingOfficialContactForm,
SeniorOfficialContactForm,
DomainDnssecForm,
DomainDsdataFormset,
DomainDsdataForm,
Expand Down
26 changes: 13 additions & 13 deletions src/registrar/forms/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,10 @@ def set_domain_info(self, domainInfo):
self.domainInfo = domainInfo


class AuthorizingOfficialContactForm(ContactForm):
"""Form for updating authorizing official contacts."""
class SeniorOfficialContactForm(ContactForm):
"""Form for updating senior official contacts."""

JOIN = "authorizing_official"
JOIN = "senior_official"

def __init__(self, disable_fields=False, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand All @@ -273,13 +273,13 @@ def __init__(self, disable_fields=False, *args, **kwargs):

# Set custom error messages
self.fields["first_name"].error_messages = {
"required": "Enter the first name / given name of your authorizing official."
"required": "Enter the first name / given name of your senior official."
}
self.fields["last_name"].error_messages = {
"required": "Enter the last name / family name of your authorizing official."
"required": "Enter the last name / family name of your senior official."
}
self.fields["title"].error_messages = {
"required": "Enter the title or role your authorizing official has in your \
"required": "Enter the title or role your senior official has in your \
organization (e.g., Chief Information Officer)."
}
self.fields["email"].error_messages = {
Expand All @@ -306,21 +306,21 @@ def save(self, commit=True):
is_federal = self.domainInfo.generic_org_type == DomainRequest.OrganizationChoices.FEDERAL
is_tribal = self.domainInfo.generic_org_type == DomainRequest.OrganizationChoices.TRIBAL

# Get the Contact object from the db for the Authorizing Official
db_ao = Contact.objects.get(id=self.instance.id)
# Get the Contact object from the db for the Senior Official
db_so = Contact.objects.get(id=self.instance.id)

if (is_federal or is_tribal) and self.has_changed():
# This action should be blocked by the UI, as the text fields are readonly.
# If they get past this point, we forbid it this way.
# This could be malicious, so lets reserve information for the backend only.
raise ValueError("Authorizing Official cannot be modified for federal or tribal domains.")
elif db_ao.has_more_than_one_join("information_authorizing_official"):
# Handle the case where the domain information object is available and the AO Contact
raise ValueError("Senior Official cannot be modified for federal or tribal domains.")
elif db_so.has_more_than_one_join("information_senior_official"):
# Handle the case where the domain information object is available and the SO Contact
# has more than one joined object.
# In this case, create a new Contact, and update the new Contact with form data.
# Then associate with domain information object as the authorizing_official
# Then associate with domain information object as the senior_official
data = dict(self.cleaned_data.items())
self.domainInfo.authorizing_official = Contact.objects.create(**data)
self.domainInfo.senior_official = Contact.objects.create(**data)
self.domainInfo.save()
else:
# If all checks pass, just save normally
Expand Down
Loading

0 comments on commit 43697a5

Please sign in to comment.