{% translate "Crisis Room" %}
+{% translate "Crisis Room overview for all organizations" %}
+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 overview for all organizations" %} {% translate "Crisis Room overview for all organizations" %}
- {% blocktranslate %}
- This overview shows the total number of findings per
- severity that have been identified for all organizations.
- {% endblocktranslate %}
-
- {% blocktranslate %}
- This table shows the findings that have been identiefied for each organization,
- sorted by the finding types and grouped by organizations.
- {% endblocktranslate %}
-
+ {% blocktranslate %}
+ This overview shows the total number of findings per
+ severity that have been identified for all organizations.
+ {% endblocktranslate %}
+
{% 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 %}
- {% blocktranslate %}
- There are no organizations yet. After creating an organization,
- the identified findings with severity 'critical' and 'high' will be shown here.
- {% endblocktranslate %}
- {% translate "Crisis Room" %}
+ {% translate "Crisis Room" %}
- {% translate "Findings overview" %}
- {% translate "Findings per organization" %}
- {% translate "Findings overview" %}
+ {% 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. + {% 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, "