WIP unit testing

This commit is contained in:
Rachid Mrad 2024-03-04 17:24:53 -05:00
parent 717f71f038
commit c89d76fb47
No known key found for this signature in database
9 changed files with 547 additions and 241 deletions

View file

@ -374,8 +374,8 @@ def analytics(request):
avg_approval_time = last_30_days_approved_applications.annotate( avg_approval_time = last_30_days_approved_applications.annotate(
approval_time=F("approved_domain__created_at") - F("submission_date") approval_time=F("approved_domain__created_at") - F("submission_date")
).aggregate(Avg("approval_time"))["approval_time__avg"] ).aggregate(Avg("approval_time"))["approval_time__avg"]
# format the timedelta? # Format the timedelta to display only days
avg_approval_time = str(avg_approval_time) avg_approval_time = f"{avg_approval_time.days} days"
start_date = request.GET.get("start_date", "") start_date = request.GET.get("start_date", "")
end_date = request.GET.get("end_date", "") end_date = request.GET.get("end_date", "")
@ -383,75 +383,69 @@ def analytics(request):
start_date_formatted = csv_export.format_start_date(start_date) start_date_formatted = csv_export.format_start_date(start_date)
end_date_formatted = csv_export.format_end_date(end_date) end_date_formatted = csv_export.format_end_date(end_date)
# Managed vs Unmanaged
filter_managed_domains_start_date = { filter_managed_domains_start_date = {
"domain__permissions__isnull": False, "domain__permissions__isnull": False,
"domain__first_ready__lte": start_date_formatted, "domain__first_ready__lte": start_date_formatted,
} }
filter_managed_domains_end_date = {
"domain__permissions__isnull": False,
"domain__first_ready__lte": end_date_formatted,
}
managed_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_managed_domains_start_date) managed_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_managed_domains_start_date)
managed_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_managed_domains_end_date)
filter_unmanaged_domains_start_date = { filter_unmanaged_domains_start_date = {
"domain__permissions__isnull": True, "domain__permissions__isnull": True,
"domain__first_ready__lte": start_date_formatted, "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 = { filter_unmanaged_domains_end_date = {
"domain__permissions__isnull": True, "domain__permissions__isnull": True,
"domain__first_ready__lte": end_date_formatted, "domain__first_ready__lte": end_date_formatted,
} }
unmanaged_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_unmanaged_domains_start_date)
unmanaged_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_unmanaged_domains_end_date) unmanaged_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_unmanaged_domains_end_date)
# Ready and Deleted domains
filter_ready_domains_start_date = { filter_ready_domains_start_date = {
"domain__state__in": [Domain.State.READY], "domain__state__in": [Domain.State.READY],
"domain__first_ready__lte": start_date_formatted, "domain__first_ready__lte": start_date_formatted,
} }
filter_ready_domains_end_date = {
"domain__state__in": [Domain.State.READY],
"domain__first_ready__lte": end_date_formatted,
}
ready_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_ready_domains_start_date) ready_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_ready_domains_start_date)
ready_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_ready_domains_end_date)
filter_deleted_domains_start_date = { filter_deleted_domains_start_date = {
"domain__state__in": [Domain.State.DELETED], "domain__state__in": [Domain.State.DELETED],
"domain__deleted__lte": start_date_formatted, "domain__deleted__lte": start_date_formatted,
} }
deleted_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_deleted_domains_start_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 = { filter_deleted_domains_end_date = {
"domain__state__in": [Domain.State.DELETED], "domain__state__in": [Domain.State.DELETED],
"domain__deleted__lte": end_date_formatted, "domain__deleted__lte": end_date_formatted,
} }
deleted_domains_sliced_at_start_date = csv_export.get_sliced_domains(filter_deleted_domains_start_date)
deleted_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_deleted_domains_end_date) deleted_domains_sliced_at_end_date = csv_export.get_sliced_domains(filter_deleted_domains_end_date)
# Created and Submitted requests # Created and Submitted requests
filter_requests_start_date = { filter_requests_start_date = {
"created_at__lte": start_date_formatted, "created_at__lte": start_date_formatted,
} }
filter_requests_end_date = {
"created_at__lte": end_date_formatted,
}
requests_sliced_at_start_date = csv_export.get_sliced_requests(filter_requests_start_date) requests_sliced_at_start_date = csv_export.get_sliced_requests(filter_requests_start_date)
requests_sliced_at_end_date = csv_export.get_sliced_requests(filter_requests_end_date)
filter_submitted_requests_start_date = { filter_submitted_requests_start_date = {
"status": DomainApplication.ApplicationStatus.SUBMITTED, "status": DomainApplication.ApplicationStatus.SUBMITTED,
"submission_date__lte": start_date_formatted, "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 = { filter_submitted_requests_end_date = {
"status": DomainApplication.ApplicationStatus.SUBMITTED, "status": DomainApplication.ApplicationStatus.SUBMITTED,
"submission_date__lte": end_date_formatted, "submission_date__lte": end_date_formatted,
} }
submitted_requests_sliced_at_start_date = csv_export.get_sliced_requests(filter_submitted_requests_start_date)
submitted_requests_sliced_at_end_date = csv_export.get_sliced_requests(filter_submitted_requests_end_date) submitted_requests_sliced_at_end_date = csv_export.get_sliced_requests(filter_submitted_requests_end_date)
context = dict( context = dict(
@ -459,6 +453,7 @@ def analytics(request):
data=dict( data=dict(
user_count=models.User.objects.all().count(), user_count=models.User.objects.all().count(),
domain_count=models.Domain.objects.all().count(), domain_count=models.Domain.objects.all().count(),
ready_domain_count=models.Domain.objects.all().filter(state=models.Domain.State.READY).count(),
last_30_days_applications=last_30_days_applications.count(), last_30_days_applications=last_30_days_applications.count(),
last_30_days_approved_applications=last_30_days_approved_applications.count(), last_30_days_approved_applications=last_30_days_approved_applications.count(),
average_application_approval_time_last_30_days=avg_approval_time, average_application_approval_time_last_30_days=avg_approval_time,
@ -1096,7 +1091,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
search_help_text = "Search by domain or submitter." search_help_text = "Search by domain or submitter."
fieldsets = [ fieldsets = [
(None, {"fields": ["status", "rejection_reason", "investigator", "creator", "approved_domain", "notes"]}), (None, {"fields": ["status", "rejection_reason", "submission_date", "investigator", "creator", "approved_domain", "notes"]}),
( (
"Type of organization", "Type of organization",
{ {
@ -1448,7 +1443,7 @@ class DomainAdmin(ListHeaderAdmin):
search_fields = ["name"] search_fields = ["name"]
search_help_text = "Search by domain name." search_help_text = "Search by domain name."
change_form_template = "django/admin/domain_change_form.html" change_form_template = "django/admin/domain_change_form.html"
readonly_fields = ["state", "expiration_date", "first_ready", "deleted"] readonly_fields = ["state", "expiration_date", "deleted"]
# Table ordering # Table ordering
ordering = ["name"] ordering = ["name"]

View file

@ -319,6 +319,9 @@ input.admin-confirm-button {
.usa-icon { .usa-icon {
top: 2px; top: 2px;
} }
a.button:active, a.button:focus {
text-decoration: none;
}
} }
.module--custom { .module--custom {

View file

@ -1022,7 +1022,7 @@ class Domain(TimeStampedModel, DomainHelper):
first_ready = DateField( first_ready = DateField(
null=True, null=True,
editable=False, editable=True,
help_text="The last time this domain moved into the READY state", help_text="The last time this domain moved into the READY state",
) )

View file

@ -16,6 +16,7 @@
<ul> <ul>
<li>User Count: {{ data.user_count }}</li> <li>User Count: {{ data.user_count }}</li>
<li>Domain Count: {{ data.domain_count }}</li> <li>Domain Count: {{ data.domain_count }}</li>
<li>Domains in READY state: {{ data.ready_domain_count }}</li>
<li>Domain applications (last 30 days): {{ data.last_30_days_applications }}</li> <li>Domain applications (last 30 days): {{ data.last_30_days_applications }}</li>
<li>Approved applications (last 30 days): {{ data.last_30_days_approved_applications }}</li> <li>Approved applications (last 30 days): {{ data.last_30_days_approved_applications }}</li>
<li>Average approval time for applications (last 30 days): {{ data.average_application_approval_time_last_30_days }}</li> <li>Average approval time for applications (last 30 days): {{ data.average_application_approval_time_last_30_days }}</li>
@ -63,8 +64,6 @@
{% comment %} {% comment %}
Inputs of type date suck for accessibility. 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. 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" See the commit "Review for ticket #999"
{% endcomment %} {% endcomment %}
<div class="display-flex flex-align-baseline margin-top-1 margin-bottom-2"> <div class="display-flex flex-align-baseline margin-top-1 margin-bottom-2">
@ -107,7 +106,7 @@
</button> </button>
</li> </li>
<li class="usa-button-group__item"> <li class="usa-button-group__item">
<button class="button exportLink" data-export-url="{% url 'analytics' %}" type="button"> <button class="button exportLink usa-button--accent-cool" data-export-url="{% url 'analytics' %}" type="button">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24"> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24" height="24">
<use xlink:href="{%static 'img/sprite.svg'%}#assessment"></use> <use xlink:href="{%static 'img/sprite.svg'%}#assessment"></use>
</svg><span class="margin-left-05">Update charts</span> </svg><span class="margin-left-05">Update charts</span>
@ -116,18 +115,18 @@
</ul> </ul>
<div class="grid-row grid-gap-2 margin-y-2"> <div class="grid-row grid-gap-2 margin-y-2">
<div class="grid-col">
<canvas id="myChart" width="400" height="200"
data-list-one="{{data.unmanaged_domains_sliced_at_start_date}}"
data-list-two="{{data.unmanaged_domains_sliced_at_end_date}}"
></canvas>
</div>
<div class="grid-col"> <div class="grid-col">
<canvas id="myChart2" width="400" height="200" <canvas id="myChart2" width="400" height="200"
data-list-one="{{data.managed_domains_sliced_at_start_date}}" data-list-one="{{data.managed_domains_sliced_at_start_date}}"
data-list-two="{{data.managed_domains_sliced_at_end_date}}" data-list-two="{{data.managed_domains_sliced_at_end_date}}"
></canvas> ></canvas>
</div> </div>
<div class="grid-col">
<canvas id="myChart" width="400" height="200"
data-list-one="{{data.unmanaged_domains_sliced_at_start_date}}"
data-list-two="{{data.unmanaged_domains_sliced_at_end_date}}"
></canvas>
</div>
</div> </div>
<div class="grid-row grid-gap-2 margin-y-2"> <div class="grid-row grid-gap-2 margin-y-2">

View file

@ -71,5 +71,5 @@
<div class="module module--custom"> <div class="module module--custom">
<h2>Analytics</h2> <h2>Analytics</h2>
<a class="display-block padding-y-1 padding-x-2" href="{% url 'analytics' %}">Dashboard</a> <a class="display-block padding-y-1 padding-x-1" href="{% url 'analytics' %}">Dashboard</a>
</div> </div>

View file

@ -0,0 +1,232 @@
from django.test import TestCase
from django.contrib.auth import get_user_model
from registrar.models.domain_application import DomainApplication
from registrar.models.domain_information import DomainInformation
from registrar.models.domain import Domain
from registrar.models.user_domain_role import UserDomainRole
from registrar.models.public_contact import PublicContact
from registrar.models.user import User
from datetime import date, datetime, timedelta
from django.utils import timezone
from registrar.tests.common import MockEppLib
class MockDb(MockEppLib):
def setUp(self):
super().setUp()
username = "test_user"
first_name = "First"
last_name = "Last"
email = "info@example.com"
self.user = get_user_model().objects.create(
username=username, first_name=first_name, last_name=last_name, email=email
)
self.domain_1, _ = Domain.objects.get_or_create(
name="cdomain1.gov", state=Domain.State.READY, first_ready=timezone.now()
)
self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED)
self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD)
self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
self.domain_5, _ = Domain.objects.get_or_create(
name="bdomain5.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2023, 11, 1))
)
self.domain_6, _ = Domain.objects.get_or_create(
name="bdomain6.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(1980, 10, 16))
)
self.domain_7, _ = Domain.objects.get_or_create(
name="xdomain7.gov", state=Domain.State.DELETED, deleted=timezone.now()
)
self.domain_8, _ = Domain.objects.get_or_create(
name="sdomain8.gov", state=Domain.State.DELETED, deleted=timezone.now()
)
# We use timezone.make_aware to sync to server time a datetime object with the current date (using date.today())
# and a specific time (using datetime.min.time()).
# Deleted yesterday
self.domain_9, _ = Domain.objects.get_or_create(
name="zdomain9.gov",
state=Domain.State.DELETED,
deleted=timezone.make_aware(datetime.combine(date.today() - timedelta(days=1), datetime.min.time())),
)
# ready tomorrow
self.domain_10, _ = Domain.objects.get_or_create(
name="adomain10.gov",
state=Domain.State.READY,
first_ready=timezone.make_aware(datetime.combine(date.today() + timedelta(days=1), datetime.min.time())),
)
self.domain_information_1, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_1,
organization_type="federal",
federal_agency="World War I Centennial Commission",
federal_type="executive",
is_election_board=True
)
self.domain_information_2, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_2,
organization_type="interstate",
is_election_board=True
)
self.domain_information_3, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_3,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=True
)
self.domain_information_4, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_4,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=True
)
self.domain_information_5, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_5,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=False
)
self.domain_information_6, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_6,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=False
)
self.domain_information_7, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_7,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=False
)
self.domain_information_8, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_8,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=False
)
self.domain_information_9, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_9,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=False
)
self.domain_information_10, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_10,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
is_election_board=False
)
meoward_user = get_user_model().objects.create(
username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com"
)
lebowski_user = get_user_model().objects.create(
username="big_lebowski", first_name="big", last_name="lebowski", email="big_lebowski@dude.co"
)
# Test for more than 1 domain manager
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
)
_, created = UserDomainRole.objects.get_or_create(
user=self.user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
)
_, created = UserDomainRole.objects.get_or_create(
user=lebowski_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
)
# Test for just 1 domain manager
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
)
# self.domain_request_1, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# requested_domain=self.domain_1.name,
# organization_type="federal",
# federal_agency="World War I Centennial Commission",
# federal_type="executive",
# is_election_board=True
# )
# self.domain_request_2, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_2,
# organization_type="interstate",
# is_election_board=True
# )
# self.domain_request_3, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_3,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=True
# )
# self.domain_request_4, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_4,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=True
# )
# self.domain_request_5, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_5,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=False
# )
# self.domain_request_6, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_6,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=False
# )
# self.domain_request_7, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_7,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=False
# )
# self.domain_request_8, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_8,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=False
# )
# self.domain_information_9, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_9,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=False
# )
# self.domain_information_10, _ = DomainApplication.objects.get_or_create(
# creator=self.user,
# domain=self.domain_10,
# organization_type="federal",
# federal_agency="Armed Forces Retirement Home",
# is_election_board=False
# )
def tearDown(self):
PublicContact.objects.all().delete()
Domain.objects.all().delete()
DomainInformation.objects.all().delete()
User.objects.all().delete()
UserDomainRole.objects.all().delete()
super().tearDown()

View file

@ -3,7 +3,7 @@ from django.urls import reverse
from registrar.tests.common import create_superuser from registrar.tests.common import create_superuser
class TestViews(TestCase): class TestAdminViews(TestCase):
def setUp(self): def setUp(self):
self.client = Client(HTTP_HOST="localhost:8080") self.client = Client(HTTP_HOST="localhost:8080")
self.superuser = create_superuser() self.superuser = create_superuser()
@ -26,7 +26,7 @@ class TestViews(TestCase):
# Construct the URL for the export data view with start_date and end_date parameters: # Construct the URL for the export data view with start_date and end_date parameters:
# This stuff is currently done in JS # This stuff is currently done in JS
export_data_url = reverse("admin:admin_export_domain_growth") + f"?start_date={start_date}&end_date={end_date}" export_data_url = reverse("export_domains_growth") + f"?start_date={start_date}&end_date={end_date}"
# Make a GET request to the export data page # Make a GET request to the export data page
response = self.client.get(export_data_url) response = self.client.get(export_data_url)

View file

@ -8,9 +8,12 @@ from registrar.models.public_contact import PublicContact
from registrar.models.user import User from registrar.models.user import User
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from registrar.models.user_domain_role import UserDomainRole from registrar.models.user_domain_role import UserDomainRole
from registrar.tests.common import MockEppLib from registrar.tests.data.mocks import MockDb
from registrar.utility.csv_export import ( from registrar.utility.csv_export import (
write_csv, format_end_date,
format_start_date,
get_sliced_domains,
write_domains_csv,
get_default_start_date, get_default_start_date,
get_default_end_date, get_default_end_date,
) )
@ -231,136 +234,11 @@ class CsvReportsTest(TestCase):
self.assertEqual(expected_file_content, response.content) self.assertEqual(expected_file_content, response.content)
class ExportDataTest(MockEppLib): class ExportDataTest(MockDb):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
username = "test_user"
first_name = "First"
last_name = "Last"
email = "info@example.com"
self.user = get_user_model().objects.create(
username=username, first_name=first_name, last_name=last_name, email=email
)
self.domain_1, _ = Domain.objects.get_or_create(
name="cdomain1.gov", state=Domain.State.READY, first_ready=timezone.now()
)
self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED)
self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD)
self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
self.domain_5, _ = Domain.objects.get_or_create(
name="bdomain5.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2023, 11, 1))
)
self.domain_6, _ = Domain.objects.get_or_create(
name="bdomain6.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(1980, 10, 16))
)
self.domain_7, _ = Domain.objects.get_or_create(
name="xdomain7.gov", state=Domain.State.DELETED, deleted=timezone.now()
)
self.domain_8, _ = Domain.objects.get_or_create(
name="sdomain8.gov", state=Domain.State.DELETED, deleted=timezone.now()
)
# We use timezone.make_aware to sync to server time a datetime object with the current date (using date.today())
# and a specific time (using datetime.min.time()).
# Deleted yesterday
self.domain_9, _ = Domain.objects.get_or_create(
name="zdomain9.gov",
state=Domain.State.DELETED,
deleted=timezone.make_aware(datetime.combine(date.today() - timedelta(days=1), datetime.min.time())),
)
# ready tomorrow
self.domain_10, _ = Domain.objects.get_or_create(
name="adomain10.gov",
state=Domain.State.READY,
first_ready=timezone.make_aware(datetime.combine(date.today() + timedelta(days=1), datetime.min.time())),
)
self.domain_information_1, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_1,
organization_type="federal",
federal_agency="World War I Centennial Commission",
federal_type="executive",
)
self.domain_information_2, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_2,
organization_type="interstate",
)
self.domain_information_3, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_3,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
self.domain_information_4, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_4,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
self.domain_information_5, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_5,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
self.domain_information_6, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_6,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
self.domain_information_7, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_7,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
self.domain_information_8, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_8,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
self.domain_information_9, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_9,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
self.domain_information_10, _ = DomainInformation.objects.get_or_create(
creator=self.user,
domain=self.domain_10,
organization_type="federal",
federal_agency="Armed Forces Retirement Home",
)
meoward_user = get_user_model().objects.create(
username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com"
)
# Test for more than 1 domain manager
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
)
_, created = UserDomainRole.objects.get_or_create(
user=self.user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
)
# Test for just 1 domain manager
_, created = UserDomainRole.objects.get_or_create(
user=meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
)
def tearDown(self): def tearDown(self):
PublicContact.objects.all().delete()
Domain.objects.all().delete()
DomainInformation.objects.all().delete()
User.objects.all().delete()
UserDomainRole.objects.all().delete()
super().tearDown() super().tearDown()
def test_export_domains_to_writer_security_emails(self): def test_export_domains_to_writer_security_emails(self):
@ -403,7 +281,7 @@ class ExportDataTest(MockEppLib):
} }
self.maxDiff = None self.maxDiff = None
# Call the export functions # Call the export functions
write_csv( write_domains_csv(
writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True
) )
@ -427,7 +305,7 @@ class ExportDataTest(MockEppLib):
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip() expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)
def test_write_csv(self): def test_write_domains_csv(self):
"""Test that write_body returns the """Test that write_body returns the
existing domain, test that sort by domain name works, existing domain, test that sort by domain name works,
test that filter works""" test that filter works"""
@ -462,7 +340,7 @@ class ExportDataTest(MockEppLib):
], ],
} }
# Call the export functions # Call the export functions
write_csv( write_domains_csv(
writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True
) )
# Reset the CSV file's position to the beginning # Reset the CSV file's position to the beginning
@ -486,7 +364,7 @@ class ExportDataTest(MockEppLib):
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip() expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)
def test_write_body_additional(self): def test_write_domains_body_additional(self):
"""An additional test for filters and multi-column sort""" """An additional test for filters and multi-column sort"""
with less_console_noise(): with less_console_noise():
# Create a CSV file in memory # Create a CSV file in memory
@ -512,7 +390,7 @@ class ExportDataTest(MockEppLib):
], ],
} }
# Call the export functions # Call the export functions
write_csv( write_domains_csv(
writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True
) )
# Reset the CSV file's position to the beginning # Reset the CSV file's position to the beginning
@ -535,7 +413,7 @@ class ExportDataTest(MockEppLib):
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip() expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)
def test_write_body_with_date_filter_pulls_domains_in_range(self): def test_write_domains_body_with_date_filter_pulls_domains_in_range(self):
"""Test that domains that are """Test that domains that are
1. READY and their first_ready dates are in range 1. READY and their first_ready dates are in range
2. DELETED and their deleted dates are in range 2. DELETED and their deleted dates are in range
@ -546,7 +424,7 @@ class ExportDataTest(MockEppLib):
and would have been easy to set up, but expected_content would contain created_at dates and would have been easy to set up, but expected_content would contain created_at dates
which are hard to mock. which are hard to mock.
TODO: Simplify is created_at is not needed for the report.""" TODO: Simplify if created_at is not needed for the report."""
with less_console_noise(): with less_console_noise():
# Create a CSV file in memory # Create a CSV file in memory
csv_file = StringIO() csv_file = StringIO()
@ -591,7 +469,7 @@ class ExportDataTest(MockEppLib):
} }
# Call the export functions # Call the export functions
write_csv( write_domains_csv(
writer, writer,
columns, columns,
sort_fields, sort_fields,
@ -599,7 +477,7 @@ class ExportDataTest(MockEppLib):
get_domain_managers=False, get_domain_managers=False,
should_write_header=True, should_write_header=True,
) )
write_csv( write_domains_csv(
writer, writer,
columns, columns,
sort_fields_for_deleted_domains, sort_fields_for_deleted_domains,
@ -664,7 +542,7 @@ class ExportDataTest(MockEppLib):
} }
self.maxDiff = None self.maxDiff = None
# Call the export functions # Call the export functions
write_csv( write_domains_csv(
writer, columns, sort_fields, filter_condition, get_domain_managers=True, should_write_header=True writer, columns, sort_fields, filter_condition, get_domain_managers=True, should_write_header=True
) )
@ -677,11 +555,11 @@ class ExportDataTest(MockEppLib):
expected_content = ( expected_content = (
"Domain name,Status,Expiration date,Domain type,Agency," "Domain name,Status,Expiration date,Domain type,Agency,"
"Organization name,City,State,AO,AO email," "Organization name,City,State,AO,AO email,"
"Security contact email,Domain manager email 1,Domain manager email 2,\n" "Security contact email,Domain manager email 1,Domain manager email 2,Domain manager email 3\n"
"adomain10.gov,Ready,,Federal,Armed Forces Retirement Home,,,, , ,\n" "adomain10.gov,Ready,,Federal,Armed Forces Retirement Home,,,, , ,\n"
"adomain2.gov,Dns needed,,Interstate,,,,, , , ,meoward@rocks.com\n" "adomain2.gov,Dns needed,,Interstate,,,,, , , ,meoward@rocks.com\n"
"cdomain1.gov,Ready,,Federal - Executive,World War I Centennial Commission,,," "cdomain1.gov,Ready,,Federal - Executive,World War I Centennial Commission,,,"
", , , ,meoward@rocks.com,info@example.com\n" ", , , ,meoward@rocks.com,info@example.com,big_lebowski@dude.co\n"
"ddomain3.gov,On hold,,Federal,Armed Forces Retirement Home,,,, , , ,,\n" "ddomain3.gov,On hold,,Federal,Armed Forces Retirement Home,,,, , , ,,\n"
) )
# Normalize line endings and remove commas, # Normalize line endings and remove commas,
@ -690,6 +568,210 @@ class ExportDataTest(MockEppLib):
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip() expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content) self.assertEqual(csv_content, expected_content)
def test_export_data_managed_domains_to_csv(self):
""""""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
writer = csv.writer(csv_file)
end_date = timezone.make_aware(datetime.combine(date.today() + timedelta(days=2), datetime.min.time()))
start_date = timezone.make_aware(datetime.combine(date.today() - timedelta(days=2), datetime.min.time()))
# Define columns, sort fields, and filter condition
columns = [
"Domain name",
"Domain type",
]
sort_fields = [
"domain__name",
]
filter_managed_domains_start_date = {
"domain__permissions__isnull": False,
"domain__first_ready__lte": start_date,
}
managed_domains_sliced_at_start_date = get_sliced_domains(filter_managed_domains_start_date)
# Call the export functions
writer.writerow(["MANAGED DOMAINS COUNTS AT START DATE"])
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([])
filter_managed_domains_end_date = {
"domain__permissions__isnull": False,
"domain__first_ready__lte": 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(managed_domains_sliced_at_end_date)
writer.writerow([])
write_domains_csv(
writer,
columns,
sort_fields,
filter_managed_domains_end_date,
get_domain_managers=True,
should_write_header=True,
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
self.maxDiff=None
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = (
"MANAGED DOMAINS COUNTS AT START DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,School district,Election office\n"
"0,0,0,0,0,0,0,0,0,0\n"
"\n"
"MANAGED DOMAINS COUNTS AT END DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,School district,Election office\n"
"1,1,0,0,0,0,0,0,0,1\n"
"\n"
"Domain name,Domain type,Domain manager email 1,Domain manager email 2,Domain manager email 3\n"
"cdomain1.gov,Federal - Executive,meoward@rocks.com,info@example.com,big_lebowski@dude.co\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_export_data_unmanaged_domains_to_csv(self):
""""""
with less_console_noise():
# Create a CSV file in memory
csv_file = StringIO()
writer = csv.writer(csv_file)
end_date = timezone.make_aware(datetime.combine(date.today() + timedelta(days=2), datetime.min.time()))
start_date = timezone.make_aware(datetime.combine(date.today() - timedelta(days=2), datetime.min.time()))
# Define columns, sort fields, and filter condition
columns = [
"Domain name",
"Domain type",
]
sort_fields = [
"domain__name",
]
filter_unmanaged_domains_start_date = {
"domain__permissions__isnull": True,
"domain__first_ready__lte": start_date,
}
unmanaged_domains_sliced_at_start_date = get_sliced_domains(filter_unmanaged_domains_start_date)
# Call the export functions
writer.writerow(["UNMANAGED DOMAINS COUNTS AT START DATE"])
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([])
filter_unmanaged_domains_end_date = {
"domain__permissions__isnull": True,
"domain__first_ready__lte": end_date,
}
unmanaged_domains_sliced_at_end_date = get_sliced_domains(filter_unmanaged_domains_end_date)
writer.writerow(["UNMANAGED DOMAINS COUNTS AT END DATE"])
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_domains_csv(
writer,
columns,
sort_fields,
filter_unmanaged_domains_end_date,
get_domain_managers=False,
should_write_header=True,
)
# Reset the CSV file's position to the beginning
csv_file.seek(0)
# Read the content into a variable
csv_content = csv_file.read()
self.maxDiff=None
# We expect the READY domain names with the domain managers: Their counts, and listing at end_date.
expected_content = (
"UNMANAGED DOMAINS COUNTS AT START DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,School district,Election office\n"
"0,0,0,0,0,0,0,0,0,0\n"
"\n"
"UNMANAGED DOMAINS COUNTS AT END DATE\n"
"Total,Federal,Interstate,State or territory,Tribal,County,City,Special district,School district,Election office\n"
"1,1,0,0,0,0,0,0,0,0\n"
"\n"
"Domain name,Domain type\n"
"adomain10.gov,Federal\n"
)
# Normalize line endings and remove commas,
# spaces and leading/trailing whitespace
csv_content = csv_content.replace(",,", "").replace(",", "").replace(" ", "").replace("\r\n", "\n").strip()
expected_content = expected_content.replace(",,", "").replace(",", "").replace(" ", "").strip()
self.assertEqual(csv_content, expected_content)
def test_write_requests_body_with_date_filter_pulls_requests_in_range(self):
"""Test that requests that are
1. SUBMITTED and their submission_date are in range
are pulled when the growth report conditions are applied to export_requests_to_writed.
Test that requests are sorted by requested domain name.
"""
pass
class HelperFunctions(TestCase): class HelperFunctions(TestCase):
"""This asserts that 1=1. Its limited usefulness lies in making sure the helper methods stay healthy.""" """This asserts that 1=1. Its limited usefulness lies in making sure the helper methods stay healthy."""
@ -704,3 +786,11 @@ class HelperFunctions(TestCase):
expected_date = timezone.now() expected_date = timezone.now()
actual_date = get_default_end_date() actual_date = get_default_end_date()
self.assertEqual(actual_date.date(), expected_date.date()) self.assertEqual(actual_date.date(), expected_date.date())
def get_sliced_domains(self):
"""Should get fitered domains counts sliced by org type and election office."""
pass
def test_get_sliced_requests(self):
"""Should get fitered requests counts sliced by org type and election office."""
pass

View file

@ -25,9 +25,10 @@ def write_header(writer, columns):
def get_domain_infos(filter_condition, sort_fields): def get_domain_infos(filter_condition, sort_fields):
domain_infos = ( domain_infos = (
DomainInformation.objects.select_related("domain", "authorizing_official") DomainInformation.objects.prefetch_related("domain", "authorizing_official", "domain__permissions")
.filter(**filter_condition) .filter(**filter_condition)
.order_by(*sort_fields) .order_by(*sort_fields)
.distinct()
) )
# Do a mass concat of the first and last name fields for authorizing_official. # Do a mass concat of the first and last name fields for authorizing_official.
@ -44,7 +45,7 @@ def get_domain_infos(filter_condition, sort_fields):
return domain_infos_cleaned return domain_infos_cleaned
def parse_row(columns, domain_info: DomainInformation, security_emails_dict=None, get_domain_managers=False): def parse_domain_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""" """Given a set of columns, generate a new row from cleaned column data"""
# Domain should never be none when parsing this information # Domain should never be none when parsing this information
@ -136,7 +137,7 @@ def update_columns_with_domain_managers(columns, max_dm_count):
columns.append(f"Domain manager email {i}") columns.append(f"Domain manager email {i}")
def write_csv( def write_domains_csv(
writer, writer,
columns, columns,
sort_fields, sort_fields,
@ -145,8 +146,8 @@ def write_csv(
should_write_header=True, should_write_header=True,
): ):
""" """
Receives params from the parent methods and outputs a CSV with fltered and sorted domains. Receives params from the parent methods and outputs a CSV with filtered and sorted domains.
Works with write_header as longas the same writer object is passed. Works with write_header as long as the same writer object is passed.
get_domain_managers: Conditional bc we only use domain manager info for export_data_full_to_csv get_domain_managers: Conditional bc we only use domain manager info for export_data_full_to_csv
should_write_header: Conditional bc export_data_domain_growth_to_csv calls write_body twice should_write_header: Conditional bc export_data_domain_growth_to_csv calls write_body twice
""" """
@ -172,7 +173,7 @@ def write_csv(
rows = [] rows = []
for domain_info in page.object_list: for domain_info in page.object_list:
try: try:
row = parse_row(columns, domain_info, security_emails_dict, get_domain_managers) row = parse_domain_row(columns, domain_info, security_emails_dict, get_domain_managers)
rows.append(row) rows.append(row)
except ValueError: except ValueError:
# This should not happen. If it does, just skip this row. # This should not happen. If it does, just skip this row.
@ -188,7 +189,6 @@ def write_csv(
def get_requests(filter_condition, sort_fields): 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 return requests
@ -235,7 +235,8 @@ def write_requests_csv(
filter_condition, filter_condition,
should_write_header=True, should_write_header=True,
): ):
""" """ """Receives params from the parent methods and outputs a CSV with filtered and sorted requests.
Works with write_header as long as the same writer object is passed."""
all_requetsts = get_requests(filter_condition, sort_fields) all_requetsts = get_requests(filter_condition, sort_fields)
@ -295,7 +296,7 @@ def export_data_type_to_csv(csv_file):
Domain.State.ON_HOLD, Domain.State.ON_HOLD,
], ],
} }
write_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=True, should_write_header=True) write_domains_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=True, should_write_header=True)
def export_data_full_to_csv(csv_file): def export_data_full_to_csv(csv_file):
@ -326,7 +327,7 @@ def export_data_full_to_csv(csv_file):
Domain.State.ON_HOLD, Domain.State.ON_HOLD,
], ],
} }
write_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True) write_domains_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True)
def export_data_federal_to_csv(csv_file): def export_data_federal_to_csv(csv_file):
@ -358,7 +359,7 @@ def export_data_federal_to_csv(csv_file):
Domain.State.ON_HOLD, Domain.State.ON_HOLD,
], ],
} }
write_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True) write_domains_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True)
def get_default_start_date(): def get_default_start_date():
@ -426,8 +427,8 @@ def export_data_domain_growth_to_csv(csv_file, start_date, end_date):
"domain__deleted__gte": start_date_formatted, "domain__deleted__gte": start_date_formatted,
} }
write_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True) write_domains_csv(writer, columns, sort_fields, filter_condition, get_domain_managers=False, should_write_header=True)
write_csv( write_domains_csv(
writer, writer,
columns, columns,
sort_fields_for_deleted_domains, sort_fields_for_deleted_domains,
@ -440,19 +441,19 @@ def export_data_domain_growth_to_csv(csv_file, start_date, end_date):
def get_sliced_domains(filter_condition): 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 = DomainInformation.objects.all().filter(**filter_condition).distinct()
domains_count = domains.count() domains_count = domains.count()
federal = domains.filter(organization_type=DomainApplication.OrganizationChoices.FEDERAL).count() federal = domains.filter(organization_type=DomainApplication.OrganizationChoices.FEDERAL).distinct().count()
interstate = domains.filter(organization_type=DomainApplication.OrganizationChoices.INTERSTATE).count() interstate = domains.filter(organization_type=DomainApplication.OrganizationChoices.INTERSTATE).count()
state_or_territory = domains.filter( state_or_territory = domains.filter(
organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY
).count() ).distinct().count()
tribal = domains.filter(organization_type=DomainApplication.OrganizationChoices.TRIBAL).count() tribal = domains.filter(organization_type=DomainApplication.OrganizationChoices.TRIBAL).distinct().count()
county = domains.filter(organization_type=DomainApplication.OrganizationChoices.COUNTY).count() county = domains.filter(organization_type=DomainApplication.OrganizationChoices.COUNTY).distinct().count()
city = domains.filter(organization_type=DomainApplication.OrganizationChoices.CITY).count() city = domains.filter(organization_type=DomainApplication.OrganizationChoices.CITY).distinct().count()
special_district = domains.filter(organization_type=DomainApplication.OrganizationChoices.SPECIAL_DISTRICT).count() special_district = domains.filter(organization_type=DomainApplication.OrganizationChoices.SPECIAL_DISTRICT).distinct().count()
school_district = domains.filter(organization_type=DomainApplication.OrganizationChoices.SCHOOL_DISTRICT).count() school_district = domains.filter(organization_type=DomainApplication.OrganizationChoices.SCHOOL_DISTRICT).distinct().count()
election_board = domains.filter(is_election_board=True).count() election_board = domains.filter(is_election_board=True).distinct().count()
return [ return [
domains_count, domains_count,
@ -471,19 +472,19 @@ def get_sliced_domains(filter_condition):
def get_sliced_requests(filter_condition): 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 = DomainApplication.objects.all().filter(**filter_condition).distinct()
requests_count = requests.count() requests_count = requests.count()
federal = requests.filter(organization_type=DomainApplication.OrganizationChoices.FEDERAL).count() federal = requests.filter(organization_type=DomainApplication.OrganizationChoices.FEDERAL).distinct().count()
interstate = requests.filter(organization_type=DomainApplication.OrganizationChoices.INTERSTATE).count() interstate = requests.filter(organization_type=DomainApplication.OrganizationChoices.INTERSTATE).distinct().count()
state_or_territory = requests.filter( state_or_territory = requests.filter(
organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY organization_type=DomainApplication.OrganizationChoices.STATE_OR_TERRITORY
).count() ).distinct().count()
tribal = requests.filter(organization_type=DomainApplication.OrganizationChoices.TRIBAL).count() tribal = requests.filter(organization_type=DomainApplication.OrganizationChoices.TRIBAL).distinct().count()
county = requests.filter(organization_type=DomainApplication.OrganizationChoices.COUNTY).count() county = requests.filter(organization_type=DomainApplication.OrganizationChoices.COUNTY).distinct().count()
city = requests.filter(organization_type=DomainApplication.OrganizationChoices.CITY).count() city = requests.filter(organization_type=DomainApplication.OrganizationChoices.CITY).distinct().count()
special_district = requests.filter(organization_type=DomainApplication.OrganizationChoices.SPECIAL_DISTRICT).count() special_district = requests.filter(organization_type=DomainApplication.OrganizationChoices.SPECIAL_DISTRICT).distinct().count()
school_district = requests.filter(organization_type=DomainApplication.OrganizationChoices.SCHOOL_DISTRICT).count() school_district = requests.filter(organization_type=DomainApplication.OrganizationChoices.SCHOOL_DISTRICT).distinct().count()
election_board = requests.filter(is_election_board=True).count() election_board = requests.filter(is_election_board=True).distinct().count()
return [ return [
requests_count, requests_count,
@ -500,7 +501,8 @@ def get_sliced_requests(filter_condition):
def export_data_managed_domains_to_csv(csv_file, start_date, end_date): def export_data_managed_domains_to_csv(csv_file, start_date, end_date):
"""Get domains have domain managers for two different dates.""" """Get counts for domains that have domain managers for two different dates,
get list of domains at end_date."""
start_date_formatted = format_start_date(start_date) start_date_formatted = format_start_date(start_date)
end_date_formatted = format_end_date(end_date) end_date_formatted = format_end_date(end_date)
@ -512,14 +514,13 @@ def export_data_managed_domains_to_csv(csv_file, start_date, end_date):
sort_fields = [ sort_fields = [
"domain__name", "domain__name",
] ]
filter_managed_domains_start_date = { filter_managed_domains_start_date = {
"domain__permissions__isnull": False, "domain__permissions__isnull": False,
"domain__first_ready__lte": start_date_formatted, "domain__first_ready__lte": start_date_formatted,
} }
managed_domains_sliced_at_start_date = get_sliced_domains(filter_managed_domains_start_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(["MANAGED DOMAINS COUNTS AT START DATE"])
writer.writerow( writer.writerow(
[ [
"Total", "Total",
@ -537,16 +538,6 @@ def export_data_managed_domains_to_csv(csv_file, start_date, end_date):
writer.writerow(managed_domains_sliced_at_start_date) writer.writerow(managed_domains_sliced_at_start_date)
writer.writerow([]) writer.writerow([])
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 = { filter_managed_domains_end_date = {
"domain__permissions__isnull": False, "domain__permissions__isnull": False,
"domain__first_ready__lte": end_date_formatted, "domain__first_ready__lte": end_date_formatted,
@ -571,7 +562,7 @@ def export_data_managed_domains_to_csv(csv_file, start_date, end_date):
writer.writerow(managed_domains_sliced_at_end_date) writer.writerow(managed_domains_sliced_at_end_date)
writer.writerow([]) writer.writerow([])
write_csv( write_domains_csv(
writer, writer,
columns, columns,
sort_fields, sort_fields,
@ -582,7 +573,8 @@ def export_data_managed_domains_to_csv(csv_file, start_date, end_date):
def export_data_unmanaged_domains_to_csv(csv_file, start_date, end_date): 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 counts for domains that do not have domain managers for two different dates,
get list of domains at end_date."""
start_date_formatted = format_start_date(start_date) start_date_formatted = format_start_date(start_date)
end_date_formatted = format_end_date(end_date) end_date_formatted = format_end_date(end_date)
@ -619,16 +611,6 @@ def export_data_unmanaged_domains_to_csv(csv_file, start_date, end_date):
writer.writerow(unmanaged_domains_sliced_at_start_date) writer.writerow(unmanaged_domains_sliced_at_start_date)
writer.writerow([]) writer.writerow([])
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 = { filter_unmanaged_domains_end_date = {
"domain__permissions__isnull": True, "domain__permissions__isnull": True,
"domain__first_ready__lte": end_date_formatted, "domain__first_ready__lte": end_date_formatted,
@ -653,18 +635,23 @@ def export_data_unmanaged_domains_to_csv(csv_file, start_date, end_date):
writer.writerow(unmanaged_domains_sliced_at_end_date) writer.writerow(unmanaged_domains_sliced_at_end_date)
writer.writerow([]) writer.writerow([])
write_csv( write_domains_csv(
writer, writer,
columns, columns,
sort_fields, sort_fields,
filter_unmanaged_domains_end_date, filter_unmanaged_domains_end_date,
get_domain_managers=True, get_domain_managers=False,
should_write_header=True, should_write_header=True,
) )
def export_data_requests_growth_to_csv(csv_file, start_date, end_date): def export_data_requests_growth_to_csv(csv_file, start_date, end_date):
""" """ """
Growth report:
Receive start and end dates from the view, parse them.
Request from write_requests_body SUBMITTED requests that are created between
the start and end dates. Specify sort params.
"""
start_date_formatted = format_start_date(start_date) start_date_formatted = format_start_date(start_date)
end_date_formatted = format_end_date(end_date) end_date_formatted = format_end_date(end_date)
@ -676,7 +663,7 @@ def export_data_requests_growth_to_csv(csv_file, start_date, end_date):
"Submission date", "Submission date",
] ]
sort_fields = [ sort_fields = [
# "domain__name", "requested_domain__name",
] ]
filter_condition = { filter_condition = {
"status": DomainApplication.ApplicationStatus.SUBMITTED, "status": DomainApplication.ApplicationStatus.SUBMITTED,