diff --git a/src/registrar/admin.py b/src/registrar/admin.py index ea5253ded..262cebd18 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -5,14 +5,14 @@ import datetime from django import forms from django.db.models import Avg, F, Value, CharField, Q from django.db.models.functions import Concat, Coalesce -from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponseRedirect from django.shortcuts import redirect, render from django_fsm import get_available_FIELD_transitions from django.contrib import admin, messages from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType -from django.urls import path, reverse +from django.urls import reverse from dateutil.relativedelta import relativedelta # type: ignore from epplibwrapper.errors import ErrorCode, RegistryError from registrar.models import Contact, Domain, DomainApplication, DraftDomain, User, Website @@ -365,126 +365,119 @@ class UserContactInline(admin.StackedInline): def analytics(request): - thirty_days_ago = datetime.datetime.today() - datetime.timedelta(days=30) + thirty_days_ago = datetime.datetime.today() - datetime.timedelta(days=30) - last_30_days_applications = models.DomainApplication.objects.filter( - created_at__gt=thirty_days_ago - ) - last_30_days_approved_applications = models.DomainApplication.objects.filter( - created_at__gt=thirty_days_ago, status=DomainApplication.ApplicationStatus.APPROVED - ) - avg_approval_time = last_30_days_approved_applications.annotate( - approval_time=F("approved_domain__created_at") - F("submission_date") - ).aggregate(Avg("approval_time"))["approval_time__avg"] - # format the timedelta? - avg_approval_time = str(avg_approval_time) + last_30_days_applications = models.DomainApplication.objects.filter(created_at__gt=thirty_days_ago) + last_30_days_approved_applications = models.DomainApplication.objects.filter( + created_at__gt=thirty_days_ago, status=DomainApplication.ApplicationStatus.APPROVED + ) + avg_approval_time = last_30_days_approved_applications.annotate( + approval_time=F("approved_domain__created_at") - F("submission_date") + ).aggregate(Avg("approval_time"))["approval_time__avg"] + # format the timedelta? + avg_approval_time = str(avg_approval_time) - start_date = request.GET.get("start_date", "") - end_date = request.GET.get("end_date", "") + start_date = request.GET.get("start_date", "") + end_date = request.GET.get("end_date", "") - start_date_formatted = csv_export.format_start_date(start_date) - end_date_formatted = csv_export.format_end_date(end_date) + start_date_formatted = csv_export.format_start_date(start_date) + end_date_formatted = csv_export.format_end_date(end_date) - # Managed vs Unmanaged - filter_managed_domains_start_date = { - "domain__permissions__isnull": False, - "domain__first_ready__lte": start_date_formatted, - } - managed_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_managed_domains_start_date) - - filter_unmanaged_domains_start_date = { - "domain__permissions__isnull": True, - "domain__first_ready__lte": start_date_formatted, - } - unmanaged_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_unmanaged_domains_start_date) - filter_managed_domains_end_date = { - "domain__permissions__isnull": False, - "domain__first_ready__lte": end_date_formatted, - } - managed_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_managed_domains_end_date) - filter_unmanaged_domains_end_date = { - "domain__permissions__isnull": True, - "domain__first_ready__lte": end_date_formatted, - } - unmanaged_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_unmanaged_domains_end_date) + # Managed vs Unmanaged + filter_managed_domains_start_date = { + "domain__permissions__isnull": False, + "domain__first_ready__lte": start_date_formatted, + } + managed_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_managed_domains_start_date) - # Ready and Deleted domains - filter_ready_domains_start_date = { - "domain__state__in": [Domain.State.READY], - "domain__first_ready__lte": start_date_formatted, - } - ready_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_ready_domains_start_date) - - filter_deleted_domains_start_date = { - "domain__state__in": [Domain.State.DELETED], - "domain__deleted__lte": start_date_formatted, - } - deleted_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_deleted_domains_start_date) + filter_unmanaged_domains_start_date = { + "domain__permissions__isnull": True, + "domain__first_ready__lte": start_date_formatted, + } + unmanaged_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_unmanaged_domains_start_date) + filter_managed_domains_end_date = { + "domain__permissions__isnull": False, + "domain__first_ready__lte": end_date_formatted, + } + managed_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_managed_domains_end_date) + filter_unmanaged_domains_end_date = { + "domain__permissions__isnull": True, + "domain__first_ready__lte": end_date_formatted, + } + unmanaged_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_unmanaged_domains_end_date) - filter_ready_domains_end_date = { - "domain__state__in": [Domain.State.READY], - "domain__first_ready__lte": end_date_formatted, - } - ready_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_ready_domains_end_date) - - filter_deleted_domains_end_date = { - "domain__state__in": [Domain.State.DELETED], - "domain__deleted__lte": end_date_formatted, - } - deleted_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_deleted_domains_end_date) + # Ready and Deleted domains + filter_ready_domains_start_date = { + "domain__state__in": [Domain.State.READY], + "domain__first_ready__lte": start_date_formatted, + } + ready_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_ready_domains_start_date) + filter_deleted_domains_start_date = { + "domain__state__in": [Domain.State.DELETED], + "domain__deleted__lte": start_date_formatted, + } + deleted_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_deleted_domains_start_date) - - # Created and Submitted requests - filter_requests_start_date = { - "created_at__lte": start_date_formatted, - } - requests_sliced_at_start_date = csv_export.get_sliced_requests(filter_requests_start_date) - - filter_submitted_requests_start_date = { - "status": DomainApplication.ApplicationStatus.SUBMITTED, - "submission_date__lte": start_date_formatted, - } - submitted_requests_sliced_at_start_date = csv_export.get_sliced_requests(filter_submitted_requests_start_date) - - filter_requests_end_date = { - "created_at__lte": end_date_formatted, - } - requests_sliced_at_end_date = csv_export.get_sliced_requests(filter_requests_end_date) - - filter_submitted_requests_end_date = { - "status": DomainApplication.ApplicationStatus.SUBMITTED, - "submission_date__lte": end_date_formatted, - } - submitted_requests_sliced_at_end_date = csv_export.get_sliced_requests(filter_submitted_requests_end_date) + filter_ready_domains_end_date = { + "domain__state__in": [Domain.State.READY], + "domain__first_ready__lte": end_date_formatted, + } + ready_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_ready_domains_end_date) + + filter_deleted_domains_end_date = { + "domain__state__in": [Domain.State.DELETED], + "domain__deleted__lte": end_date_formatted, + } + deleted_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_deleted_domains_end_date) + + # Created and Submitted requests + filter_requests_start_date = { + "created_at__lte": start_date_formatted, + } + requests_sliced_at_start_date = csv_export.get_sliced_requests(filter_requests_start_date) + + filter_submitted_requests_start_date = { + "status": DomainApplication.ApplicationStatus.SUBMITTED, + "submission_date__lte": start_date_formatted, + } + submitted_requests_sliced_at_start_date = csv_export.get_sliced_requests(filter_submitted_requests_start_date) + + filter_requests_end_date = { + "created_at__lte": end_date_formatted, + } + requests_sliced_at_end_date = csv_export.get_sliced_requests(filter_requests_end_date) + + filter_submitted_requests_end_date = { + "status": DomainApplication.ApplicationStatus.SUBMITTED, + "submission_date__lte": end_date_formatted, + } + submitted_requests_sliced_at_end_date = csv_export.get_sliced_requests(filter_submitted_requests_end_date) + + context = dict( + **admin.site.each_context(request), + data=dict( + user_count=models.User.objects.all().count(), + domain_count=models.Domain.objects.all().count(), + last_30_days_applications=last_30_days_applications.count(), + last_30_days_approved_applications=last_30_days_approved_applications.count(), + average_application_approval_time_last_30_days=avg_approval_time, + managed_domains_sliced_at_start_date=managed_domains_sliced_at_start_date, + unmanaged_domains_sliced_at_start_date=unmanaged_domains_sliced_at_start_date, + managed_domains_sliced_at_end_date=managed_domains_sliced_at_end_date, + unmanaged_domains_sliced_at_end_date=unmanaged_domains_sliced_at_end_date, + ready_domains_sliced_at_start_date=ready_domains_sliced_at_start_date, + deleted_domains_sliced_at_start_date=deleted_domains_sliced_at_start_date, + ready_domains_sliced_at_end_date=ready_domains_sliced_at_end_date, + deleted_domains_sliced_at_end_date=deleted_domains_sliced_at_end_date, + requests_sliced_at_start_date=requests_sliced_at_start_date, + submitted_requests_sliced_at_start_date=submitted_requests_sliced_at_start_date, + requests_sliced_at_end_date=requests_sliced_at_end_date, + submitted_requests_sliced_at_end_date=submitted_requests_sliced_at_end_date, + ), + ) + return render(request, "admin/analytics.html", context) - context = dict( - **admin.site.each_context(request), - data=dict( - user_count=models.User.objects.all().count(), - domain_count=models.Domain.objects.all().count(), - last_30_days_applications=last_30_days_applications.count(), - last_30_days_approved_applications=last_30_days_approved_applications.count(), - average_application_approval_time_last_30_days=avg_approval_time, - - managed_domains_sliced_at_start_date=managed_domains_sliced_at_start_date, - unmanaged_domains_sliced_at_start_date=unmanaged_domains_sliced_at_start_date, - managed_domains_sliced_at_end_date=managed_domains_sliced_at_end_date, - unmanaged_domains_sliced_at_end_date=unmanaged_domains_sliced_at_end_date, - - ready_domains_sliced_at_start_date=ready_domains_sliced_at_start_date, - deleted_domains_sliced_at_start_date=deleted_domains_sliced_at_start_date, - ready_domains_sliced_at_end_date=ready_domains_sliced_at_end_date, - deleted_domains_sliced_at_end_date=deleted_domains_sliced_at_end_date, - - requests_sliced_at_start_date=requests_sliced_at_start_date, - submitted_requests_sliced_at_start_date=submitted_requests_sliced_at_start_date, - requests_sliced_at_end_date=requests_sliced_at_end_date, - submitted_requests_sliced_at_end_date=submitted_requests_sliced_at_end_date, - - ), - ) - return render(request, "admin/analytics.html", context) class MyUserAdmin(BaseUserAdmin): """Custom user admin class to use our inlines.""" diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index 6592ee538..cbf64fd86 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -10,7 +10,15 @@ from django.views.generic import RedirectView from registrar import views from registrar.admin import analytics -from registrar.views.admin_views import ExportDataDomainsGrowth, ExportDataFederal, ExportDataFull, ExportDataManagedDomains, ExportDataRequestsGrowth, ExportDataType, ExportDataUnmanagedDomains +from registrar.views.admin_views import ( + ExportDataDomainsGrowth, + ExportDataFederal, + ExportDataFull, + ExportDataManagedDomains, + ExportDataRequestsGrowth, + ExportDataType, + ExportDataUnmanagedDomains, +) from registrar.views.application import Step from registrar.views.utility import always_404 @@ -91,7 +99,6 @@ urlpatterns = [ admin.site.admin_view(analytics), name="analytics", ), - path("admin/", admin.site.urls), path( "application//edit/", diff --git a/src/registrar/signals.py b/src/registrar/signals.py index ef09e605b..74dc8a063 100644 --- a/src/registrar/signals.py +++ b/src/registrar/signals.py @@ -27,7 +27,7 @@ def handle_profile(sender, instance, **kwargs): last_name = getattr(instance, "last_name", "") email = getattr(instance, "email", "") phone = getattr(instance, "phone", "") - logger.info(f'in handle_profile first {instance}') + logger.info(f"in handle_profile first {instance}") is_new_user = kwargs.get("created", False) @@ -37,7 +37,7 @@ def handle_profile(sender, instance, **kwargs): contacts = Contact.objects.filter(user=instance) if len(contacts) == 0: # no matching contact - logger.info(f'inside no matching contacts for first {first_name} last {last_name} email {email}') + logger.info(f"inside no matching contacts for first {first_name} last {last_name} email {email}") Contact.objects.create( user=instance, first_name=first_name, diff --git a/src/registrar/templates/admin/analytics.html b/src/registrar/templates/admin/analytics.html index 505d6dcc3..29faffd3b 100644 --- a/src/registrar/templates/admin/analytics.html +++ b/src/registrar/templates/admin/analytics.html @@ -10,162 +10,159 @@
-
-

At a glance

-
-
    -
  • User Count: {{ data.user_count }}
  • -
  • Domain Count: {{ data.domain_count }}
  • -
  • Domain applications (last 30 days): {{ data.last_30_days_applications }}
  • -
  • Approved applications (last 30 days): {{ data.last_30_days_approved_applications }}
  • -
  • Average approval time for applications (last 30 days): {{ data.average_application_approval_time_last_30_days }}
  • -
+
+

At a glance

+
+
    +
  • User Count: {{ data.user_count }}
  • +
  • Domain Count: {{ data.domain_count }}
  • +
  • Domain applications (last 30 days): {{ data.last_30_days_applications }}
  • +
  • Approved applications (last 30 days): {{ data.last_30_days_approved_applications }}
  • +
  • Average approval time for applications (last 30 days): {{ data.average_application_approval_time_last_30_days }}
  • +
+
+
-
- -
- -
+ +
-
-

Growth reports

-
- {% comment %} - Inputs of type date suck for accessibility. - We'll need to replace those guys with a django form once we figure out how to hook one onto this page. - The challenge is in the path definition in urls. It does NOT like admin/export_domain_growth/ - - See the commit "Review for ticket #999" - {% endcomment %} -
-
- - +
+

Growth reports

+
+ {% comment %} + Inputs of type date suck for accessibility. + We'll need to replace those guys with a django form once we figure out how to hook one onto this page. + The challenge is in the path definition in urls. It does NOT like admin/export_domain_growth/ + + See the commit "Review for ticket #999" + {% endcomment %} +
+
+ + +
+
+ + +
+
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+ +
+
+
-
- - +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
- -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
- -
+
{% endblock %} diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index 474981dc6..bec5f3835 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -22,6 +22,7 @@ def write_header(writer, columns): """ writer.writerow(columns) + def get_domain_infos(filter_condition, sort_fields): domain_infos = ( DomainInformation.objects.select_related("domain", "authorizing_official") @@ -42,6 +43,7 @@ def get_domain_infos(filter_condition, sort_fields): ) return domain_infos_cleaned + def parse_row(columns, domain_info: DomainInformation, security_emails_dict=None, get_domain_managers=False): """Given a set of columns, generate a new row from cleaned column data""" @@ -102,6 +104,7 @@ def parse_row(columns, domain_info: DomainInformation, security_emails_dict=None row = [FIELDS.get(column, "") for column in columns] return row + def _get_security_emails(sec_contact_ids): """ Retrieve security contact emails for the given security contact IDs. @@ -123,6 +126,7 @@ def _get_security_emails(sec_contact_ids): return security_emails_dict + def update_columns_with_domain_managers(columns, max_dm_count): """ Update the columns list to include "Domain manager email {#}" headers @@ -131,6 +135,7 @@ def update_columns_with_domain_managers(columns, max_dm_count): for i in range(1, max_dm_count + 1): columns.append(f"Domain manager email {i}") + def write_csv( writer, columns, @@ -180,19 +185,17 @@ def write_csv( writer.writerows(rows) + def get_requests(filter_condition, sort_fields): - requests = ( - DomainApplication.objects.all() - .filter(**filter_condition) - .order_by(*sort_fields) - ) + requests = DomainApplication.objects.all().filter(**filter_condition).order_by(*sort_fields) return requests + def parse_request_row(columns, request: DomainApplication): """Given a set of columns, generate a new row from cleaned column data""" - requested_domain_name = 'No requested domain' + requested_domain_name = "No requested domain" # Domain should never be none when parsing this information if request.requested_domain is not None: @@ -224,6 +227,7 @@ def parse_request_row(columns, request: DomainApplication): row = [FIELDS.get(column, "") for column in columns] return row + def write_requests_csv( writer, columns, @@ -231,8 +235,7 @@ def write_requests_csv( filter_condition, should_write_header=True, ): - """ - """ + """ """ all_requetsts = get_requests(filter_condition, sort_fields) @@ -257,6 +260,7 @@ def write_requests_csv( writer.writerows(rows) + def export_data_type_to_csv(csv_file): """All domains report with extra columns""" @@ -293,6 +297,7 @@ def export_data_type_to_csv(csv_file): } write_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=True, should_write_header=True) + def export_data_full_to_csv(csv_file): """All domains report""" @@ -323,6 +328,7 @@ def export_data_full_to_csv(csv_file): } write_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True) + def export_data_federal_to_csv(csv_file): """Federal domains report""" @@ -354,20 +360,25 @@ def export_data_federal_to_csv(csv_file): } write_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True) + def get_default_start_date(): # Default to a date that's prior to our first deployment return timezone.make_aware(datetime(2023, 11, 1)) + def get_default_end_date(): # Default to now() return timezone.now() + def format_start_date(start_date): return timezone.make_aware(datetime.strptime(start_date, "%Y-%m-%d")) if start_date else get_default_start_date() + def format_end_date(end_date): return timezone.make_aware(datetime.strptime(end_date, "%Y-%m-%d")) if end_date else get_default_end_date() + def export_data_domain_growth_to_csv(csv_file, start_date, end_date): """ Growth report: @@ -425,15 +436,17 @@ def export_data_domain_growth_to_csv(csv_file, start_date, end_date): should_write_header=False, ) + def get_sliced_domains(filter_condition): - """Get fitered domains counts sliced by org type and election office. - """ + """Get fitered domains counts sliced by org type and election office.""" domains = DomainInformation.objects.all().filter(**filter_condition) domains_count = domains.count() federal = domains.filter(organization_type=DomainApplication.OrganizationChoices.FEDERAL).count() interstate = domains.filter(organization_type=DomainApplication.OrganizationChoices.INTERSTATE).count() - state_or_territory = domains.filter(organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY).count() + state_or_territory = domains.filter( + organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY + ).count() tribal = domains.filter(organization_type=DomainApplication.OrganizationChoices.TRIBAL).count() county = domains.filter(organization_type=DomainApplication.OrganizationChoices.COUNTY).count() city = domains.filter(organization_type=DomainApplication.OrganizationChoices.CITY).count() @@ -441,26 +454,30 @@ def get_sliced_domains(filter_condition): school_district = domains.filter(organization_type=DomainApplication.OrganizationChoices.SCHOOL_DISTRICT).count() election_board = domains.filter(is_election_board=True).count() - return [domains_count, - federal, - interstate, - state_or_territory, - tribal, - county, - city, - special_district, - school_district, - election_board] + return [ + domains_count, + federal, + interstate, + state_or_territory, + tribal, + county, + city, + special_district, + school_district, + election_board, + ] + def get_sliced_requests(filter_condition): - """Get fitered requests counts sliced by org type and election office. - """ + """Get fitered requests counts sliced by org type and election office.""" requests = DomainApplication.objects.all().filter(**filter_condition) requests_count = requests.count() federal = requests.filter(organization_type=DomainApplication.OrganizationChoices.FEDERAL).count() interstate = requests.filter(organization_type=DomainApplication.OrganizationChoices.INTERSTATE).count() - state_or_territory = requests.filter(organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY).count() + state_or_territory = requests.filter( + organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY + ).count() tribal = requests.filter(organization_type=DomainApplication.OrganizationChoices.TRIBAL).count() county = requests.filter(organization_type=DomainApplication.OrganizationChoices.COUNTY).count() city = requests.filter(organization_type=DomainApplication.OrganizationChoices.CITY).count() @@ -468,20 +485,22 @@ def get_sliced_requests(filter_condition): school_district = requests.filter(organization_type=DomainApplication.OrganizationChoices.SCHOOL_DISTRICT).count() election_board = requests.filter(is_election_board=True).count() - return [requests_count, - federal, - interstate, - state_or_territory, - tribal, - county, - city, - special_district, - school_district, - election_board] + return [ + requests_count, + federal, + interstate, + state_or_territory, + tribal, + county, + city, + special_district, + school_district, + election_board, + ] + def export_data_managed_domains_to_csv(csv_file, start_date, end_date): - """Get domains have domain managers for two different dates. - """ + """Get domains have domain managers for two different dates.""" start_date_formatted = format_start_date(start_date) end_date_formatted = format_end_date(end_date) @@ -501,11 +520,31 @@ def export_data_managed_domains_to_csv(csv_file, start_date, end_date): managed_domains_sliced_at_start_date = get_sliced_domains(filter_managed_domains_start_date) writer.writerow(["MANAGED DOMAINS COUNTS AT SRAT DATE"]) - writer.writerow(["Total", "Federal", "Interstate", "State or territory", "Tribal", "County", "City", "Special district", "School district", "Election office"]) + writer.writerow( + [ + "Total", + "Federal", + "Interstate", + "State or territory", + "Tribal", + "County", + "City", + "Special district", + "School district", + "Election office", + ] + ) writer.writerow(managed_domains_sliced_at_start_date) writer.writerow([]) - write_csv(writer, columns, sort_fields, filter_managed_domains_start_date, get_domain_managers=True, should_write_header=True) + write_csv( + writer, + columns, + sort_fields, + filter_managed_domains_start_date, + get_domain_managers=True, + should_write_header=True, + ) writer.writerow([]) filter_managed_domains_end_date = { @@ -515,15 +554,35 @@ def export_data_managed_domains_to_csv(csv_file, start_date, end_date): managed_domains_sliced_at_end_date = get_sliced_domains(filter_managed_domains_end_date) writer.writerow(["MANAGED DOMAINS COUNTS AT END DATE"]) - writer.writerow(["Total", "Federal", "Interstate", "State or territory", "Tribal", "County", "City", "Special district", "School district", "Election office"]) + writer.writerow( + [ + "Total", + "Federal", + "Interstate", + "State or territory", + "Tribal", + "County", + "City", + "Special district", + "School district", + "Election office", + ] + ) writer.writerow(managed_domains_sliced_at_end_date) writer.writerow([]) - write_csv(writer, columns, sort_fields, filter_managed_domains_end_date, get_domain_managers=True, should_write_header=True) + write_csv( + writer, + columns, + sort_fields, + filter_managed_domains_end_date, + get_domain_managers=True, + should_write_header=True, + ) + def export_data_unmanaged_domains_to_csv(csv_file, start_date, end_date): - """ Get domains that do not have domain managers for two different dates. - """ + """Get domains that do not have domain managers for two different dates.""" start_date_formatted = format_start_date(start_date) end_date_formatted = format_end_date(end_date) @@ -535,7 +594,7 @@ def export_data_unmanaged_domains_to_csv(csv_file, start_date, end_date): sort_fields = [ "domain__name", ] - + filter_unmanaged_domains_start_date = { "domain__permissions__isnull": True, "domain__first_ready__lte": start_date_formatted, @@ -543,29 +602,69 @@ def export_data_unmanaged_domains_to_csv(csv_file, start_date, end_date): unmanaged_domains_sliced_at_start_date = get_sliced_domains(filter_unmanaged_domains_start_date) writer.writerow(["UNMANAGED DOMAINS AT START DATE"]) - writer.writerow(["Total", "Federal", "Interstate", "State or territory", "Tribal", "County", "City", "Special district", "School district", "Election office"]) + writer.writerow( + [ + "Total", + "Federal", + "Interstate", + "State or territory", + "Tribal", + "County", + "City", + "Special district", + "School district", + "Election office", + ] + ) writer.writerow(unmanaged_domains_sliced_at_start_date) writer.writerow([]) - write_csv(writer, columns, sort_fields, filter_unmanaged_domains_start_date, get_domain_managers=True, should_write_header=True) + write_csv( + writer, + columns, + sort_fields, + filter_unmanaged_domains_start_date, + get_domain_managers=True, + should_write_header=True, + ) writer.writerow([]) - + filter_unmanaged_domains_end_date = { "domain__permissions__isnull": True, "domain__first_ready__lte": end_date_formatted, } unmanaged_domains_sliced_at_end_date = get_sliced_domains(filter_unmanaged_domains_end_date) - + writer.writerow(["UNMANAGED DOMAINS AT END DATE"]) - writer.writerow(["Total", "Federal", "Interstate", "State or territory", "Tribal", "County", "City", "Special district", "School district", "Election office"]) + writer.writerow( + [ + "Total", + "Federal", + "Interstate", + "State or territory", + "Tribal", + "County", + "City", + "Special district", + "School district", + "Election office", + ] + ) writer.writerow(unmanaged_domains_sliced_at_end_date) writer.writerow([]) - write_csv(writer, columns, sort_fields, filter_unmanaged_domains_end_date, get_domain_managers=True, should_write_header=True) + write_csv( + writer, + columns, + sort_fields, + filter_unmanaged_domains_end_date, + get_domain_managers=True, + should_write_header=True, + ) + def export_data_requests_growth_to_csv(csv_file, start_date, end_date): - """ - """ + """ """ start_date_formatted = format_start_date(start_date) end_date_formatted = format_end_date(end_date) diff --git a/src/registrar/views/admin_views.py b/src/registrar/views/admin_views.py index 2e21c2161..c3769ad03 100644 --- a/src/registrar/views/admin_views.py +++ b/src/registrar/views/admin_views.py @@ -8,7 +8,8 @@ from registrar.utility import csv_export import logging logger = logging.getLogger(__name__) - + + class ExportDataType(View): def get(self, request, *args, **kwargs): # match the CSV example with all the fields @@ -16,7 +17,8 @@ class ExportDataType(View): response["Content-Disposition"] = 'attachment; filename="domains-by-type.csv"' csv_export.export_data_type_to_csv(response) return response - + + class ExportDataFull(View): def get(self, request, *args, **kwargs): # Smaller export based on 1 @@ -24,7 +26,8 @@ class ExportDataFull(View): response["Content-Disposition"] = 'attachment; filename="current-full.csv"' csv_export.export_data_full_to_csv(response) return response - + + class ExportDataFederal(View): def get(self, request, *args, **kwargs): # Federal only @@ -33,6 +36,7 @@ class ExportDataFederal(View): csv_export.export_data_federal_to_csv(response) return response + class ExportDataDomainsGrowth(View): def get(self, request, *args, **kwargs): # Get start_date and end_date from the request's GET parameters @@ -47,7 +51,8 @@ class ExportDataDomainsGrowth(View): csv_export.export_data_domain_growth_to_csv(response, start_date, end_date) return response - + + class ExportDataRequestsGrowth(View): def get(self, request, *args, **kwargs): # Get start_date and end_date from the request's GET parameters @@ -62,7 +67,8 @@ class ExportDataRequestsGrowth(View): csv_export.export_data_requests_growth_to_csv(response, start_date, end_date) return response - + + class ExportDataManagedDomains(View): def get(self, request, *args, **kwargs): # Get start_date and end_date from the request's GET parameters @@ -70,11 +76,14 @@ class ExportDataManagedDomains(View): start_date = request.GET.get("start_date", "") end_date = request.GET.get("end_date", "") response = HttpResponse(content_type="text/csv") - response["Content-Disposition"] = f'attachment; filename="managed-vs-unamanaged-domains-{start_date}-to-{end_date}.csv"' + response["Content-Disposition"] = ( + f'attachment; filename="managed-vs-unamanaged-domains-{start_date}-to-{end_date}.csv"' + ) csv_export.export_data_managed_domains_to_csv(response, start_date, end_date) return response + class ExportDataUnmanagedDomains(View): def get(self, request, *args, **kwargs): # Get start_date and end_date from the request's GET parameters @@ -82,7 +91,9 @@ class ExportDataUnmanagedDomains(View): start_date = request.GET.get("start_date", "") end_date = request.GET.get("end_date", "") response = HttpResponse(content_type="text/csv") - response["Content-Disposition"] = f'attachment; filename="managed-vs-unamanaged-domains-{start_date}-to-{end_date}.csv"' + response["Content-Disposition"] = ( + f'attachment; filename="managed-vs-unamanaged-domains-{start_date}-to-{end_date}.csv"' + ) csv_export.export_data_unmanaged_domains_to_csv(response, start_date, end_date) return response