From 818a8eb7b0ebb6ebf3ceab0ac8b91ea03a4f9861 Mon Sep 17 00:00:00 2001 From: Rieven Date: Tue, 7 Jan 2025 14:03:03 +0100 Subject: [PATCH] file changes --- rocky/crisis_room/templates/crisis_room.html | 19 ++++ .../templates/crisis_room_findings.html | 97 +++++++---------- .../templates/crisis_room_header.html | 2 +- rocky/crisis_room/urls.py | 5 +- rocky/crisis_room/views.py | 103 +----------------- rocky/tests/test_crisis_room.py | 69 ------------ rocky/tests/test_dashboard.py | 2 +- 7 files changed, 65 insertions(+), 232 deletions(-) create mode 100644 rocky/crisis_room/templates/crisis_room.html delete mode 100644 rocky/tests/test_crisis_room.py diff --git a/rocky/crisis_room/templates/crisis_room.html b/rocky/crisis_room/templates/crisis_room.html new file mode 100644 index 00000000000..c3efc21f121 --- /dev/null +++ b/rocky/crisis_room/templates/crisis_room.html @@ -0,0 +1,19 @@ +{% extends "layouts/base.html" %} + +{% load i18n %} +{% load static %} + +{% block content %} + {% include "header.html" %} + +
+
+
+

{% translate "Crisis Room" %}

+

{% translate "Crisis Room overview for all organizations" %}

+
+ {% include "crisis_room_findings.html" %} + +
+
+{% endblock content %} diff --git a/rocky/crisis_room/templates/crisis_room_findings.html b/rocky/crisis_room/templates/crisis_room_findings.html index 46f4e46627f..f990e1146ae 100644 --- a/rocky/crisis_room/templates/crisis_room_findings.html +++ b/rocky/crisis_room/templates/crisis_room_findings.html @@ -1,66 +1,51 @@ -{% extends "layouts/base.html" %} - {% load i18n %} -{% load static %} - -{% block content %} - {% include "header.html" %} -
+{% if organizations_dashboards %} + {% if organizations_findings_summary.total_finding_types != 0 %}
-

{% translate "Crisis Room" %}

-

{% translate "Crisis Room overview for all organizations" %}

-
- {% if organizations_dashboards %} - {% if organizations_findings_summary.total_finding_types != 0 %} -
-
-
-

{% translate "Findings overview" %}

-

- {% blocktranslate %} - This overview shows the total number of findings per - severity that have been identified for all organizations. - {% endblocktranslate %} -

-
- {% include "partials/report_severity_totals_table.html" with data=organizations_findings_summary %} - -
-
-
-
-
-

{% translate "Findings per organization" %}

-

- {% blocktranslate %} - This table shows the findings that have been identiefied for each organization, - sorted by the finding types and grouped by organizations. - {% endblocktranslate %} -

-
- {% include "findings_report/report.html" with is_dashboard_findings="yes" %} +
+

{% translate "Findings overview" %}

+

+ {% blocktranslate %} + This overview shows the total number of findings per + severity that have been identified for all organizations. + {% endblocktranslate %} +

+
+ {% include "partials/report_severity_totals_table.html" with data=organizations_findings_summary %} -
-
- {% else %} -

{% translate "Findings overview" %}

+ +
+
+
+
+

{% translate "Findings per organization" %}

{% blocktranslate %} - No findings have been identified yet. As soon as they have been - identified, they will be shown on this page. + This table shows the findings that have been identiefied for each organization, + sorted by the finding types and grouped by organizations. {% endblocktranslate %}

- {% endif %} - {% else %} -

- {% blocktranslate %} - There are no organizations yet. After creating an organization, - the identified findings with severity 'critical' and 'high' will be shown here. - {% endblocktranslate %} -

- {% endif %} +
+ {% include "findings_report/report.html" with is_dashboard_findings="yes" %} + +
-
-{% endblock content %} + {% else %} +

{% translate "Findings overview" %}

+

+ {% blocktranslate %} + No findings have been identified yet. As soon as they have been + identified, they will be shown on this page. + {% endblocktranslate %} +

+ {% endif %} +{% else %} +

+ {% blocktranslate %} + There are no organizations yet. After creating an organization, + the identified findings with severity 'critical' and 'high' will be shown here. + {% endblocktranslate %} +

+{% endif %} diff --git a/rocky/crisis_room/templates/crisis_room_header.html b/rocky/crisis_room/templates/crisis_room_header.html index 5ccd0d8c44d..07b20b58295 100644 --- a/rocky/crisis_room/templates/crisis_room_header.html +++ b/rocky/crisis_room/templates/crisis_room_header.html @@ -4,7 +4,7 @@ diff --git a/rocky/crisis_room/urls.py b/rocky/crisis_room/urls.py index 34ff77e40d3..46eeeda4f63 100644 --- a/rocky/crisis_room/urls.py +++ b/rocky/crisis_room/urls.py @@ -3,7 +3,4 @@ from . import views # Crisis room overview urls -urlpatterns = [ - path("", views.CrisisRoomAllOrganizations.as_view(), name="crisis_room"), - path("findings", views.CrisisRoomFindings.as_view(), name="crisis_room_findings"), -] +urlpatterns = [path("", views.CrisisRoom.as_view(), name="crisis_room")] diff --git a/rocky/crisis_room/views.py b/rocky/crisis_room/views.py index 1dba5f92222..f74153e87cd 100644 --- a/rocky/crisis_room/views.py +++ b/rocky/crisis_room/views.py @@ -1,122 +1,23 @@ -from dataclasses import dataclass from datetime import datetime, timezone from typing import Any import structlog -from account.models import KATUser from django.conf import settings -from django.contrib import messages -from django.http import HttpResponse from django.http.request import HttpRequest -from django.shortcuts import redirect -from django.urls.base import reverse -from django.utils.translation import gettext_lazy as _ from django.views.generic import TemplateView from pydantic import TypeAdapter from reports.report_types.findings_report.report import SEVERITY_OPTIONS -from tools.forms.base import ObservedAtForm from tools.models import Organization, OrganizationMember -from tools.view_helpers import BreadcrumbsMixin from crisis_room.management.commands.dashboard import FINDINGS_DASHBOARD_NAME from crisis_room.models import DashboardData -from octopoes.connector import ConnectorException from octopoes.connector.octopoes import OctopoesAPIConnector from octopoes.models import Reference -from octopoes.models.ooi.findings import RiskLevelSeverity from rocky.bytes_client import BytesClient, get_bytes_client -from rocky.views.mixins import ConnectorFormMixin, ObservedAtMixin logger = structlog.get_logger(__name__) -# dataclass to store finding type counts -@dataclass -class OrganizationFindingCountPerSeverity: - name: str - code: str - finding_count_per_severity: dict[str, int] - - @property - def total(self) -> int: - return sum(self.finding_count_per_severity.values()) - - @property - def total_critical(self) -> int: - try: - return self.finding_count_per_severity[RiskLevelSeverity.CRITICAL.value] - except KeyError: - return 0 - - -class CrisisRoomView(BreadcrumbsMixin, ConnectorFormMixin, ObservedAtMixin, TemplateView): - template_name = "crisis_room/crisis_room.html" - connector_form_class = ObservedAtForm - breadcrumbs = [{"url": "", "text": "Crisis Room"}] - - def sort_by_total( - self, finding_counts: list[OrganizationFindingCountPerSeverity] - ) -> list[OrganizationFindingCountPerSeverity]: - is_desc = self.request.GET.get("sort_total_by", "desc") != "asc" - return sorted(finding_counts, key=lambda x: x.total, reverse=is_desc) - - def sort_by_severity( - self, finding_counts: list[OrganizationFindingCountPerSeverity] - ) -> list[OrganizationFindingCountPerSeverity]: - is_desc = self.request.GET.get("sort_critical_by", "desc") != "asc" - return sorted(finding_counts, key=lambda x: x.total_critical, reverse=is_desc) - - def get_finding_type_severity_count(self, organization: Organization) -> dict[str, int]: - try: - api_connector = OctopoesAPIConnector( - settings.OCTOPOES_API, organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT - ) - return api_connector.count_findings_by_severity(valid_time=self.observed_at) - except ConnectorException: - messages.add_message( - self.request, - messages.ERROR, - _("Failed to get list of findings for organization {}, check server logs for more details.").format( - organization.code - ), - ) - logger.exception("Failed to get list of findings for organization %s", organization.code) - return {} - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - - user: KATUser = self.request.user - - # query each organization's finding type count - org_finding_counts_per_severity = [ - OrganizationFindingCountPerSeverity( - name=org.name, code=org.code, finding_count_per_severity=self.get_finding_type_severity_count(org) - ) - for org in user.organizations - ] - - context["breadcrumb_list"] = [{"url": reverse("crisis_room"), "text": "CRISIS ROOM"}] - context["organizations"] = user.organizations - - context["org_finding_counts_per_severity"] = self.sort_by_total(org_finding_counts_per_severity) - context["org_finding_counts_per_severity_critical"] = self.sort_by_severity(org_finding_counts_per_severity) - - context["observed_at_form"] = self.get_connector_form() - context["observed_at"] = self.observed_at.date() - - return context - - -class CrisisRoomAllOrganizations(TemplateView): - """ - Crisis Room langding page. - """ - - def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse: - return redirect(reverse("crisis_room_findings")) - - class DashboardService: observed_at = datetime.now(timezone.utc) # we can later set any observed_at @@ -218,8 +119,8 @@ def get_organizations_findings_summary( return summary -class CrisisRoomFindings(TemplateView): - template_name = "crisis_room_findings.html" +class CrisisRoom(TemplateView): + template_name = "crisis_room.html" def setup(self, request: HttpRequest, *args: Any, **kwargs: Any) -> None: super().setup(request, *args, **kwargs) diff --git a/rocky/tests/test_crisis_room.py b/rocky/tests/test_crisis_room.py deleted file mode 100644 index 94e9b8f577a..00000000000 --- a/rocky/tests/test_crisis_room.py +++ /dev/null @@ -1,69 +0,0 @@ -from crisis_room.views import CrisisRoomView, OrganizationFindingCountPerSeverity -from django.urls import resolve, reverse -from pytest_django.asserts import assertContains - -from octopoes.connector import ConnectorException -from tests.conftest import setup_request - - -def test_crisis_room(rf, client_member, mock_crisis_room_octopoes): - request = setup_request(rf.get("crisis_room"), client_member.user) - request.resolver_match = resolve(reverse("crisis_room")) - - mock_crisis_room_octopoes().count_findings_by_severity.return_value = {"medium": 1, "critical": 0} - - response = CrisisRoomView.as_view()(request) - - assert response.status_code == 200 - - assertContains(response, '1', html=True) - assertContains(response, "
0
", html=True) - - assert mock_crisis_room_octopoes().count_findings_by_severity.call_count == 1 - - -def test_crisis_room_observed_at(rf, client_member, mock_crisis_room_octopoes): - request = setup_request(rf.get("crisis_room", {"observed_at": "2021-01-01"}), client_member.user) - request.resolver_match = resolve(reverse("crisis_room")) - response = CrisisRoomView.as_view()(request) - assert response.status_code == 200 - assertContains(response, "Jan 01, 2021") # Next to title crisis room - assertContains(response, "2021-01-01") # Date Widget - - -def test_crisis_room_observed_at_bad_format(rf, client_member, mock_crisis_room_octopoes): - request = setup_request(rf.get("crisis_room", {"observed_at": "2021-bad-format"}), client_member.user) - request.resolver_match = resolve(reverse("crisis_room")) - response = CrisisRoomView.as_view()(request) - assert response.status_code == 200 - assertContains(response, "Can not parse date, falling back to show current date.") - assertContains(response, "Enter a valid date.") - - -def test_org_finding_count_total(): - assert OrganizationFindingCountPerSeverity("dev", "_dev", {"medium": 1, "low": 2}).total == 3 - - -def test_crisis_room_error(rf, client_user_two_organizations, mock_crisis_room_octopoes): - request = setup_request(rf.get("crisis_room"), client_user_two_organizations) - request.resolver_match = resolve(reverse("crisis_room")) - - mock_crisis_room_octopoes().count_findings_by_severity.side_effect = [ - {"medium": 1, "critical": 0}, - ConnectorException("error"), - ] - - response = CrisisRoomView.as_view()(request) - - assert response.status_code == 200 - - assertContains(response, '1', html=True) - assertContains(response, "
0
", html=True) - - messages = list(request._messages) - assert ( - messages[0].message - == "Failed to get list of findings for organization org_b, check server logs for more details." - ) - - assert mock_crisis_room_octopoes().count_findings_by_severity.call_count == 2 diff --git a/rocky/tests/test_dashboard.py b/rocky/tests/test_dashboard.py index da3b2a00a1c..ce9b68d204b 100644 --- a/rocky/tests/test_dashboard.py +++ b/rocky/tests/test_dashboard.py @@ -13,7 +13,7 @@ def test_crisis_room_findings_dashboard(rf, mocker, client_member, findings_dash ) summary(findings_dashboard_mock_data) - request = setup_request(rf.get("crisis_room_findings"), client_member.user) + request = setup_request(rf.get("crisis_room"), client_member.user) response = CrisisRoomFindings.as_view()(request) assert response.status_code == 200