mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-20 00:14:16 +02:00
Biz logic
This commit is contained in:
parent
65174dfcd9
commit
8b8c176b0c
6 changed files with 335 additions and 186 deletions
|
@ -1498,12 +1498,36 @@ class DomainsTable extends LoadTableBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showExportElement(element) {
|
||||||
|
console.log(`Showing element: ${element.id}`);
|
||||||
|
element.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideExportElement(element) {
|
||||||
|
console.log(`Hiding element: ${element.id}`);
|
||||||
|
element.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
class DomainRequestsTable extends LoadTableBase {
|
class DomainRequestsTable extends LoadTableBase {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('.domain-requests__table', '.domain-requests__table-wrapper', '#domain-requests__search-field', '#domain-requests__search-field-submit', '.domain-requests__reset-search', '.domain-requests__reset-filters', '.domain-requests__no-data', '.domain-requests__no-search-results');
|
super('.domain-requests__table', '.domain-requests__table-wrapper', '#domain-requests__search-field', '#domain-requests__search-field-submit', '.domain-requests__reset-search', '.domain-requests__reset-filters', '.domain-requests__no-data', '.domain-requests__no-search-results');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleExportButton(requests) {
|
||||||
|
console.log("Toggling Export Button Visibility");
|
||||||
|
const exportButton = document.getElementById('export-csv-button');
|
||||||
|
if (exportButton) {
|
||||||
|
console.log(`Current requests length: ${requests.length}`);
|
||||||
|
if (requests.length > 0) {
|
||||||
|
showExportElement(exportButton);
|
||||||
|
} else {
|
||||||
|
hideExportElement(exportButton);
|
||||||
|
}
|
||||||
|
console.log(exportButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads rows in the domains list, as well as updates pagination around the domains list
|
* Loads rows in the domains list, as well as updates pagination around the domains list
|
||||||
* based on the supplied attributes.
|
* based on the supplied attributes.
|
||||||
|
@ -1517,6 +1541,7 @@ class DomainRequestsTable extends LoadTableBase {
|
||||||
*/
|
*/
|
||||||
loadTable(page, sortBy = this.currentSortBy, order = this.currentOrder, scroll = this.scrollToTable, status = this.currentStatus, searchTerm = this.currentSearchTerm, portfolio = this.portfolioValue) {
|
loadTable(page, sortBy = this.currentSortBy, order = this.currentOrder, scroll = this.scrollToTable, status = this.currentStatus, searchTerm = this.currentSearchTerm, portfolio = this.portfolioValue) {
|
||||||
let baseUrl = document.getElementById("get_domain_requests_json_url");
|
let baseUrl = document.getElementById("get_domain_requests_json_url");
|
||||||
|
|
||||||
if (!baseUrl) {
|
if (!baseUrl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1548,6 +1573,9 @@ class DomainRequestsTable extends LoadTableBase {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call toggleExportButton to manage button visibility
|
||||||
|
this.toggleExportButton(data.domain_requests);
|
||||||
|
|
||||||
// handle the display of proper messaging in the event that no requests exist in the list or search returns no results
|
// handle the display of proper messaging in the event that no requests exist in the list or search returns no results
|
||||||
this.updateDisplay(data, this.tableWrapper, this.noTableWrapper, this.noSearchResultsWrapper, this.currentSearchTerm);
|
this.updateDisplay(data, this.tableWrapper, this.noTableWrapper, this.noSearchResultsWrapper, this.currentSearchTerm);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ from registrar.views.report_views import (
|
||||||
AnalyticsView,
|
AnalyticsView,
|
||||||
ExportDomainRequestDataFull,
|
ExportDomainRequestDataFull,
|
||||||
ExportDataTypeUser,
|
ExportDataTypeUser,
|
||||||
|
ExportDataTypeRequests,
|
||||||
)
|
)
|
||||||
|
|
||||||
from registrar.views.domain_request import Step
|
from registrar.views.domain_request import Step
|
||||||
|
@ -165,6 +166,11 @@ urlpatterns = [
|
||||||
ExportDataTypeUser.as_view(),
|
ExportDataTypeUser.as_view(),
|
||||||
name="export_data_type_user",
|
name="export_data_type_user",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"reports/export_data_type_requests/",
|
||||||
|
ExportDataTypeRequests.as_view(),
|
||||||
|
name="export_data_type_requests",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"domain-request/<id>/edit/",
|
"domain-request/<id>/edit/",
|
||||||
views.DomainRequestWizard.as_view(),
|
views.DomainRequestWizard.as_view(),
|
||||||
|
|
|
@ -229,6 +229,10 @@ class User(AbstractUser):
|
||||||
"""Determines if the current user can view all available domains in a given portfolio"""
|
"""Determines if the current user can view all available domains in a given portfolio"""
|
||||||
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)
|
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS)
|
||||||
|
|
||||||
|
def has_view_all_domain_requests_portfolio_permission(self, portfolio):
|
||||||
|
"""Determines if the current user can view all available domains in a given portfolio"""
|
||||||
|
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS)
|
||||||
|
|
||||||
def has_any_requests_portfolio_permission(self, portfolio):
|
def has_any_requests_portfolio_permission(self, portfolio):
|
||||||
# BEGIN
|
# BEGIN
|
||||||
# Note code below is to add organization_request feature
|
# Note code below is to add organization_request feature
|
||||||
|
@ -458,3 +462,12 @@ class User(AbstractUser):
|
||||||
return DomainInformation.objects.filter(portfolio=portfolio).values_list("domain_id", flat=True)
|
return DomainInformation.objects.filter(portfolio=portfolio).values_list("domain_id", flat=True)
|
||||||
else:
|
else:
|
||||||
return UserDomainRole.objects.filter(user=self).values_list("domain_id", flat=True)
|
return UserDomainRole.objects.filter(user=self).values_list("domain_id", flat=True)
|
||||||
|
|
||||||
|
def get_user_domain_request_ids(self, request):
|
||||||
|
"""Returns either the domain request ids associated with this user on UserDomainRole or Portfolio"""
|
||||||
|
portfolio = request.session.get("portfolio")
|
||||||
|
|
||||||
|
if self.is_org_user(request) and self.has_view_all_domain_requests_portfolio_permission(portfolio):
|
||||||
|
return DomainRequest.objects.filter(portfolio=portfolio).values_list("id", flat=True)
|
||||||
|
else:
|
||||||
|
return UserDomainRole.objects.filter(user=self).values_list("domain_request_id", flat=True)
|
||||||
|
|
|
@ -3,208 +3,200 @@
|
||||||
{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
|
{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
|
||||||
{% url 'get_domain_requests_json' as url %}
|
{% url 'get_domain_requests_json' as url %}
|
||||||
<span id="get_domain_requests_json_url" class="display-none">{{url}}</span>
|
<span id="get_domain_requests_json_url" class="display-none">{{url}}</span>
|
||||||
|
|
||||||
<section class="section-outlined domain-requests{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domain-requests">
|
<section class="section-outlined domain-requests{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domain-requests">
|
||||||
<div class="grid-row">
|
<div class="grid-row">
|
||||||
{% if not portfolio %}
|
{% if not portfolio %}
|
||||||
<div class="mobile:grid-col-12 desktop:grid-col-6">
|
<div class="mobile:grid-col-12 desktop:grid-col-6">
|
||||||
<h2 id="domain-requests-header" class="flex-6">Domain requests</h2>
|
<h2 id="domain-requests-header" class="flex-6">Domain requests</h2>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<!-- Embedding the portfolio value in a data attribute -->
|
<!-- Embedding the portfolio value in a data attribute -->
|
||||||
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="mobile:grid-col-12 desktop:grid-col-6">
|
|
||||||
<section aria-label="Domain requests search component" class="flex-6 margin-y-2">
|
<div class="mobile:grid-col-12 desktop:grid-col-6 display-flex flex-align-end flex-justify-end">
|
||||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
<section aria-label="Domain requests search component" class="flex-6 margin-y-2">
|
||||||
{% csrf_token %}
|
<form class="usa-search usa-search--small" method="POST" role="search">
|
||||||
<button class="usa-button usa-button--unstyled margin-right-2 domain-requests__reset-search display-none" type="button">
|
{% csrf_token %}
|
||||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
<button class="usa-button usa-button--unstyled margin-right-2 domain-requests__reset-search display-none" type="button">
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
|
||||||
</svg>
|
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
||||||
Reset
|
</svg>
|
||||||
</button>
|
Reset
|
||||||
{% if portfolio %}
|
</button>
|
||||||
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name or creator</label>
|
{% if portfolio %}
|
||||||
{% else %}
|
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name or creator</label>
|
||||||
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name</label>
|
{% else %}
|
||||||
{% endif %}
|
<label class="usa-sr-only" for="domain-requests__search-field">Search by domain name</label>
|
||||||
<input
|
{% endif %}
|
||||||
class="usa-input"
|
<input
|
||||||
id="domain-requests__search-field"
|
class="usa-input"
|
||||||
type="search"
|
id="domain-requests__search-field"
|
||||||
name="search"
|
type="search"
|
||||||
{% if portfolio %}
|
name="search"
|
||||||
placeholder="Search by domain name or creator"
|
{% if portfolio %}
|
||||||
{% else %}
|
placeholder="Search by domain name or creator"
|
||||||
placeholder="Search by domain name"
|
{% else %}
|
||||||
{% endif %}
|
placeholder="Search by domain name"
|
||||||
/>
|
{% endif %}
|
||||||
<button class="usa-button" type="submit" id="domain-requests__search-field-submit">
|
/>
|
||||||
<img
|
<button class="usa-button" type="submit" id="domain-requests__search-field-submit">
|
||||||
src="{% static 'img/usa-icons-bg/search--white.svg' %}"
|
<img
|
||||||
class="usa-search__submit-icon"
|
src="{% static 'img/usa-icons-bg/search--white.svg' %}"
|
||||||
alt="Search"
|
class="usa-search__submit-icon"
|
||||||
/>
|
alt="Search"
|
||||||
</button>
|
/>
|
||||||
</form>
|
</button>
|
||||||
</section>
|
</form>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
|
<section aria-label="Domain Requests report component" class="margin-top-205 margin-left-2">
|
||||||
|
<a id="export-csv-button" href="{% url 'export_data_type_requests' %}" class="usa-button usa-button--unstyled usa-button--with-icon usa-button--justify-right" role="button">
|
||||||
|
<svg class="usa-icon usa-icon--big" aria-hidden="true" focusable="false" role="img" width="24" height="24">
|
||||||
|
<use xlink:href="{% static 'img/sprite.svg' %}#file_download"></use>
|
||||||
|
</svg>
|
||||||
|
Export as CSV
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if portfolio %}
|
{% if portfolio %}
|
||||||
<div class="display-flex flex-align-center">
|
<div class="display-flex flex-align-center">
|
||||||
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
|
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
|
||||||
<div class="usa-accordion usa-accordion--select margin-right-2">
|
<div class="usa-accordion usa-accordion--select margin-right-2">
|
||||||
<div class="usa-accordion__heading">
|
<div class="usa-accordion__heading">
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
|
class="usa-button usa-button--small padding--8-8-9 usa-button--outline usa-button--filter usa-accordion__button"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-controls="filter-status"
|
||||||
|
>
|
||||||
|
<span class="filter-indicator text-bold display-none"></span> Status
|
||||||
|
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||||
|
<use xlink:href="/public/img/sprite.svg#expand_more"></use>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="filter-status" class="usa-accordion__content usa-prose shadow-1">
|
||||||
|
<h2>Status</h2>
|
||||||
|
<fieldset class="usa-fieldset margin-top-0">
|
||||||
|
<legend class="usa-legend">Select to apply <span class="sr-only">status</span> filter</legend>
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-started"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="started"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-started">Started</label>
|
||||||
|
</div>
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-submitted"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="submitted"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-submitted">Submitted</label>
|
||||||
|
</div>
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-in-review"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="in review"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-in-review">In review</label>
|
||||||
|
</div>
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-action-needed"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="action needed"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-action-needed">Action needed</label>
|
||||||
|
</div>
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-rejected"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="rejected"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-rejected">Rejected</label>
|
||||||
|
</div>
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-withdrawn"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="withdrawn"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-withdrawn">Withdrawn</label>
|
||||||
|
</div>
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-ineligible"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="ineligible"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-ineligible">Ineligible</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="usa-button usa-button--small padding--8-8-9 usa-button--outline usa-button--filter usa-accordion__button"
|
class="usa-button usa-button--small padding--8-12-9-12 usa-button--outline usa-button--filter domain-requests__reset-filters display-none"
|
||||||
aria-expanded="false"
|
>
|
||||||
aria-controls="filter-status"
|
Clear filters
|
||||||
>
|
<svg class="usa-icon top-1px" aria-hidden="true" focusable="false" role="img" width="24">
|
||||||
<span class="filter-indicator text-bold display-none"></span> Status
|
<use xlink:href="/public/img/sprite.svg#close"></use>
|
||||||
<svg class="usa-icon top-2px" aria-hidden="true" focusable="false" role="img" width="24">
|
|
||||||
<use xlink:href="/public/img/sprite.svg#expand_more"></use>
|
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
<div id="filter-status" class="usa-accordion__content usa-prose shadow-1">
|
|
||||||
<h2>Status</h2>
|
|
||||||
<fieldset class="usa-fieldset margin-top-0">
|
|
||||||
<legend class="usa-legend">Select to apply <span class="sr-only">status</span> filter</legend>
|
|
||||||
<div class="usa-checkbox">
|
|
||||||
<input
|
|
||||||
class="usa-checkbox__input"
|
|
||||||
id="filter-status-started"
|
|
||||||
type="checkbox"
|
|
||||||
name="filter-status"
|
|
||||||
value="started"
|
|
||||||
/>
|
|
||||||
<label class="usa-checkbox__label" for="filter-status-started"
|
|
||||||
>Started</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="usa-checkbox">
|
|
||||||
<input
|
|
||||||
class="usa-checkbox__input"
|
|
||||||
id="filter-status-submitted"
|
|
||||||
type="checkbox"
|
|
||||||
name="filter-status"
|
|
||||||
value="submitted"
|
|
||||||
/>
|
|
||||||
<label class="usa-checkbox__label" for="filter-status-submitted"
|
|
||||||
>Submitted</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="usa-checkbox">
|
|
||||||
<input
|
|
||||||
class="usa-checkbox__input"
|
|
||||||
id="filter-status-in-review"
|
|
||||||
type="checkbox"
|
|
||||||
name="filter-status"
|
|
||||||
value="in review"
|
|
||||||
/>
|
|
||||||
<label class="usa-checkbox__label" for="filter-status-in-review"
|
|
||||||
>In review</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="usa-checkbox">
|
|
||||||
<input
|
|
||||||
class="usa-checkbox__input"
|
|
||||||
id="filter-status-action-needed"
|
|
||||||
type="checkbox"
|
|
||||||
name="filter-status"
|
|
||||||
value="action needed"
|
|
||||||
/>
|
|
||||||
<label class="usa-checkbox__label" for="filter-status-action-needed"
|
|
||||||
>Action needed</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="usa-checkbox">
|
|
||||||
<input
|
|
||||||
class="usa-checkbox__input"
|
|
||||||
id="filter-status-rejected"
|
|
||||||
type="checkbox"
|
|
||||||
name="filter-status"
|
|
||||||
value="rejected"
|
|
||||||
/>
|
|
||||||
<label class="usa-checkbox__label" for="filter-status-rejected"
|
|
||||||
>Rejected</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="usa-checkbox">
|
|
||||||
<input
|
|
||||||
class="usa-checkbox__input"
|
|
||||||
id="filter-status-withdrawn"
|
|
||||||
type="checkbox"
|
|
||||||
name="filter-status"
|
|
||||||
value="withdrawn"
|
|
||||||
/>
|
|
||||||
<label class="usa-checkbox__label" for="filter-status-withdrawn"
|
|
||||||
>Withdrawn</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="usa-checkbox">
|
|
||||||
<input
|
|
||||||
class="usa-checkbox__input"
|
|
||||||
id="filter-status-ineligible"
|
|
||||||
type="checkbox"
|
|
||||||
name="filter-status"
|
|
||||||
value="ineligible"
|
|
||||||
/>
|
|
||||||
<label class="usa-checkbox__label" for="filter-status-ineligible"
|
|
||||||
>Ineligible</label
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="usa-button usa-button--small padding--8-12-9-12 usa-button--outline usa-button--filter domain-requests__reset-filters display-none"
|
|
||||||
>
|
|
||||||
Clear filters
|
|
||||||
<svg class="usa-icon top-1px" aria-hidden="true" focusable="false" role="img" width="24">
|
|
||||||
<use xlink:href="/public/img/sprite.svg#close"></use>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="domain-requests__table-wrapper display-none usa-table-container--scrollable margin-top-0" tabindex="0">
|
<div class="domain-requests__table-wrapper display-none usa-table-container--scrollable margin-top-0" tabindex="0">
|
||||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked domain-requests__table">
|
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked domain-requests__table">
|
||||||
<caption class="sr-only">Your domain requests</caption>
|
<caption class="sr-only">Your domain requests</caption>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th data-sortable="requested_domain__name" scope="col" role="columnheader">Domain name</th>
|
<th data-sortable="requested_domain__name" scope="col" role="columnheader">Domain name</th>
|
||||||
<th data-sortable="last_submitted_date" scope="col" role="columnheader">Submitted</th>
|
<th data-sortable="last_submitted_date" scope="col" role="columnheader">Submitted</th>
|
||||||
{% if portfolio %}
|
{% if portfolio %}
|
||||||
<th data-sortable="creator" scope="col" role="columnheader">Created by</th>
|
<th data-sortable="creator" scope="col" role="columnheader">Created by</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<th data-sortable="status" scope="col" role="columnheader">Status</th>
|
<th data-sortable="status" scope="col" role="columnheader">Status</th>
|
||||||
<th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th>
|
<th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th>
|
||||||
<!-- AJAX will conditionally add a th for delete actions -->
|
<!-- AJAX will conditionally add a th for delete actions -->
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="domain-requests-tbody">
|
<tbody id="domain-requests-tbody">
|
||||||
<!-- AJAX will populate this tbody -->
|
<!-- AJAX will populate this tbody -->
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div
|
<div class="usa-sr-only usa-table__announcement-region" aria-live="polite"></div>
|
||||||
class="usa-sr-only usa-table__announcement-region"
|
|
||||||
aria-live="polite"
|
|
||||||
></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="domain-requests__no-data display-none">
|
<div class="domain-requests__no-data display-none">
|
||||||
<p>You haven't requested any domains.</p>
|
<p>You haven't requested any domains.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="domain-requests__no-search-results display-none">
|
<div class="domain-requests__no-search-results display-none">
|
||||||
<p>No results found</p>
|
<p>No results found</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domain-requests-pagination">
|
|
||||||
<span class="usa-pagination__counter text-base-dark padding-left-2 margin-bottom-1">
|
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domain
|
||||||
<!-- Count will be dynamically populated by JS -->
|
|
||||||
</span>
|
|
||||||
<ul class="usa-pagination__list">
|
|
||||||
<!-- Pagination links will be dynamically populated by JS -->
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
|
@ -583,6 +583,105 @@ class DomainDataTypeUser(DomainDataType):
|
||||||
return Q(domain__id__in=request.user.get_user_domain_ids(request))
|
return Q(domain__id__in=request.user.get_user_domain_ids(request))
|
||||||
|
|
||||||
|
|
||||||
|
class DomainRequestsDataType:
|
||||||
|
"""
|
||||||
|
The DomainRequestsDataType report, but filtered based on the current request user
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_filter_conditions(cls, request=None):
|
||||||
|
if request is None or not hasattr(request, "user") or not request.user.is_authenticated:
|
||||||
|
return Q(id__in=[])
|
||||||
|
|
||||||
|
request_ids = request.user.get_user_domain_request_ids(request)
|
||||||
|
return Q(id__in=request_ids)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_queryset(cls, request):
|
||||||
|
return DomainRequest.objects.filter(cls.get_filter_conditions(request))
|
||||||
|
|
||||||
|
def safe_get(attribute, default="N/A"):
|
||||||
|
# Return the attribute value or default if not present
|
||||||
|
return attribute if attribute is not None else default
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_data_to_csv(cls, response, request=None):
|
||||||
|
import csv
|
||||||
|
|
||||||
|
writer = csv.writer(response)
|
||||||
|
|
||||||
|
# CSV headers
|
||||||
|
writer.writerow(
|
||||||
|
[
|
||||||
|
"Domain request",
|
||||||
|
"Region",
|
||||||
|
"Status",
|
||||||
|
"Election office",
|
||||||
|
"Federal type",
|
||||||
|
"Domain type",
|
||||||
|
"Request additional details",
|
||||||
|
"Creator approved domains count",
|
||||||
|
"Creator active requests count",
|
||||||
|
"Alternative domains",
|
||||||
|
"Other contacts",
|
||||||
|
"Current websites",
|
||||||
|
"Federal agency",
|
||||||
|
"SO first name",
|
||||||
|
"SO last name",
|
||||||
|
"SO email",
|
||||||
|
"SO title/role",
|
||||||
|
"Creator first name",
|
||||||
|
"Creator last name",
|
||||||
|
"Creator email",
|
||||||
|
"Organization name",
|
||||||
|
"City",
|
||||||
|
"State/territory",
|
||||||
|
"Request purpose",
|
||||||
|
"CISA regional representative",
|
||||||
|
"Last submitted date",
|
||||||
|
"First submitted date",
|
||||||
|
"Last status update",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
queryset = cls.get_queryset(request)
|
||||||
|
for request in queryset:
|
||||||
|
writer.writerow(
|
||||||
|
[
|
||||||
|
request.requested_domain,
|
||||||
|
cls.safe_get(getattr(request, "region_field", None)),
|
||||||
|
request.status,
|
||||||
|
cls.safe_get(getattr(request, "election_office", None)),
|
||||||
|
request.federal_type,
|
||||||
|
cls.safe_get(getattr(request, "domain_type", None)),
|
||||||
|
cls.safe_get(getattr(request, "additional_details", None)),
|
||||||
|
cls.safe_get(getattr(request, "creator_approved_domains_count", None)),
|
||||||
|
cls.safe_get(getattr(request, "creator_active_requests_count", None)),
|
||||||
|
cls.safe_get(getattr(request, "all_alternative_domains", None)),
|
||||||
|
cls.safe_get(getattr(request, "all_other_contacts", None)),
|
||||||
|
cls.safe_get(getattr(request, "all_current_websites", None)),
|
||||||
|
cls.safe_get(getattr(request, "federal_agency", None)),
|
||||||
|
cls.safe_get(getattr(request.senior_official, "first_name", None)),
|
||||||
|
cls.safe_get(getattr(request.senior_official, "last_name", None)),
|
||||||
|
cls.safe_get(getattr(request.senior_official, "email", None)),
|
||||||
|
cls.safe_get(getattr(request.senior_official, "title", None)),
|
||||||
|
cls.safe_get(getattr(request.creator, "first_name", None)),
|
||||||
|
cls.safe_get(getattr(request.creator, "last_name", None)),
|
||||||
|
cls.safe_get(getattr(request.creator, "email", None)),
|
||||||
|
cls.safe_get(getattr(request, "organization_name", None)),
|
||||||
|
cls.safe_get(getattr(request, "city", None)),
|
||||||
|
cls.safe_get(getattr(request, "state_territory", None)),
|
||||||
|
cls.safe_get(getattr(request, "purpose", None)),
|
||||||
|
cls.safe_get(getattr(request, "cisa_representative_email", None)),
|
||||||
|
cls.safe_get(getattr(request, "last_submitted_date", None)),
|
||||||
|
cls.safe_get(getattr(request, "first_submitted_date", None)),
|
||||||
|
cls.safe_get(getattr(request, "last_status_update", None)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class DomainDataFull(DomainExport):
|
class DomainDataFull(DomainExport):
|
||||||
"""
|
"""
|
||||||
Shows security contacts, filtered by state
|
Shows security contacts, filtered by state
|
||||||
|
|
|
@ -169,6 +169,17 @@ class ExportDataTypeUser(View):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
class ExportDataTypeRequests(View):
|
||||||
|
"""Returns a domain requests report for a given user on the request"""
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
response = HttpResponse(content_type="text/csv")
|
||||||
|
response["Content-Disposition"] = 'attachment; filename="domain-requests.csv"'
|
||||||
|
csv_export.DomainRequestsDataType.export_data_to_csv(response, request=request)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class ExportDataFull(View):
|
class ExportDataFull(View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
# Smaller export based on 1
|
# Smaller export based on 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue