diff --git a/backend/core/models.py b/backend/core/models.py index b910eafb0..f239d5716 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -1451,7 +1451,25 @@ def get_selected_implementation_groups(self): ] def get_requirement_assessments(self): - return RequirementAssessment.objects.filter(compliance_assessment=self) + """ + Returns sorted assessable requirement assessments based on the selected implementation groups + """ + if not self.selected_implementation_groups: + return RequirementAssessment.objects.filter( + compliance_assessment=self, requirement__assessable=True + ).order_by("requirement__order_id") + selected_implementation_groups_set = set(self.selected_implementation_groups) + filtered_requirements = RequirementAssessment.objects.filter( + compliance_assessment=self, requirement__assessable=True + ).order_by("requirement__order_id") + requirement_assessments_list = [] + for requirement in filtered_requirements: + if selected_implementation_groups_set & set( + requirement.requirement.implementation_groups + ): + requirement_assessments_list.append(requirement) + + return requirement_assessments_list def get_requirements_status_count(self): requirements_status_count = [] @@ -1692,6 +1710,9 @@ class Status(models.TextChoices): def __str__(self) -> str: return self.requirement.display_short + def get_requirement_description(self) -> str: + return self.requirement.description + class Meta: verbose_name = _("Requirement assessment") verbose_name_plural = _("Requirement assessments") diff --git a/backend/core/serializers.py b/backend/core/serializers.py index f87aba05d..da403d712 100644 --- a/backend/core/serializers.py +++ b/backend/core/serializers.py @@ -485,6 +485,7 @@ class Meta: class RequirementAssessmentReadSerializer(BaseModelSerializer): name = serializers.CharField(source="__str__") + description = serializers.CharField(source="get_requirement_description") compliance_assessment = FieldsRelatedField() folder = FieldsRelatedField() diff --git a/backend/core/views.py b/backend/core/views.py index 6f6baee7d..718040679 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -1372,6 +1372,27 @@ def tree(self, request, pk): filter_graph_by_implementation_groups(tree, implementation_groups) ) + @action(detail=True, methods=["get"]) + def flash_mode(self, request, pk): + """Returns the list of requirement assessments for flash mode""" + requirement_assessments_objects = ( + self.get_object().get_requirement_assessments() + ) + requirements_objects = RequirementNode.objects.filter( + framework=self.get_object().framework + ) + requirement_assessments = RequirementAssessmentReadSerializer( + requirement_assessments_objects, many=True + ).data + requirements = RequirementNodeReadSerializer( + requirements_objects, many=True + ).data + flash_mode = { + "requirements": requirements, + "requirement_assessments": requirement_assessments, + } + return Response(flash_mode, status=status.HTTP_200_OK) + @action(detail=True) def export(self, request, pk): (object_ids_view, _, _) = RoleAssignment.get_accessible_object_ids( diff --git a/frontend/messages/de.json b/frontend/messages/de.json index b8aa43270..b213abc9f 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -529,6 +529,7 @@ "asZIP": "als ZIP", "incoming": "Eingehend", "outdated": "Veraltet", + "goBackToAudit": "Zurück zum Audit", "exportBackupDescription": "Dies wird die Datenbank serialisieren und ein Backup erstellen, einschließlich Benutzer und RBAC. Beweise und andere Dateien sind im Backup nicht enthalten.", "importBackupDescription": "Dies wird die Datenbank aus einem Backup deserialisieren und wiederherstellen. Dies wird alle vorhandenen Daten, einschließlich Benutzer und RBAC, überschreiben und kann nicht rückgängig gemacht werden.", "requirementAppliedControlHelpText": "Mit den ausgewählten Maßnahmen verknüpfte Nachweise werden automatisch der Anforderung zugeordnet.", diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 2184080e0..23b0480cc 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -529,6 +529,8 @@ "asZIP": "as ZIP", "incoming": "Incoming", "outdated": "Outdated", + "flashMode": "Flash mode", + "goBackToAudit": "Go back to the audit", "exportBackupDescription": "This will serialize and create a backup of the database, including users and RBAC. Evidences and other files are not included in the backup.", "importBackupDescription": "This will deserialize and restore the database from a backup. This will overwrite all existing data, including users and RBAC and cannot be undone.", "requirementAppliedControlHelpText": "Evidences linked to the selected measures will be automatically associated with the requirement.", diff --git a/frontend/messages/es.json b/frontend/messages/es.json index 794b9aab4..259c0e7fa 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -529,6 +529,7 @@ "asZIP": "como ZIP", "incoming": "Entrante", "outdated": "Desactualizado", + "goBackToAudit": "Volver a la auditoría", "exportBackupDescription": "Esto serializará y creará una copia de seguridad de la base de datos, incluidos los usuarios y RBAC. Las pruebas y otros archivos no se incluyen en la copia de seguridad.", "importBackupDescription": "Esto deserializará y restaurará la base de datos desde una copia de seguridad. Esto sobrescribirá todos los datos existentes, incluidos los usuarios y RBAC, y no se puede deshacer.", "requirementAppliedControlHelpText": "Las evidencias vinculadas a las medidas seleccionadas se asociarán automáticamente al requisito.", diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 258cccda2..febabc1a4 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -529,6 +529,7 @@ "asZIP": "en ZIP", "incoming": "En approche", "outdated": "Dépassé", + "goBackToAudit": "Retour à l'audit", "exportBackupDescription": "Cela va sérialiser et créer une sauvegarde de la base de données, y compris les utilisateurs et RBAC. Les preuves et autres fichiers ne sont pas inclus dans la sauvegarde.", "importBackupDescription": "Cela va désérialiser et restaurer la base de données à partir d'une sauvegarde. Cela va écraser toutes les données existantes, y compris les utilisateurs et RBAC. Cette action est irréversible.", "requirementAppliedControlHelpText": "Les preuves liées aux mesures sélectionnées seront automatiquement associées à l'exigence.", diff --git a/frontend/messages/it.json b/frontend/messages/it.json index d5ab8718e..988ae1bf1 100644 --- a/frontend/messages/it.json +++ b/frontend/messages/it.json @@ -529,6 +529,7 @@ "asZIP": "come ZIP", "incoming": "In arrivo", "outdated": "Obsoleto", + "goBackToAudit": "Torniamo all'audit", "exportBackupDescription": "Questo serializzerà e creerà un backup del database, inclusi utenti e RBAC. Le prove e altri file non sono inclusi nel backup.", "importBackupDescription": "Questo deserializzerà e ripristinerà il database da un backup. Questo sovrascriverà tutti i dati esistenti, inclusi utenti e RBAC, e non può essere annullato.", "requirementAppliedControlHelpText": "Le evidenze legate alle misure selezionate verranno automaticamente associate al requisito.", diff --git a/frontend/messages/nl.json b/frontend/messages/nl.json index 69299cc7b..cc06dd039 100644 --- a/frontend/messages/nl.json +++ b/frontend/messages/nl.json @@ -529,6 +529,7 @@ "asZIP": "als ZIP", "incoming": "Binnenkomend", "outdated": "Verouderd", + "goBackToAudit": "Ga terug naar de controle", "exportBackupDescription": "Dit zal de database serialiseren en een back-up maken, inclusief gebruikers en RBAC. Bewijzen en andere bestanden zijn niet inbegrepen in de back-up.", "importBackupDescription": "Dit zal de database deserialiseren en herstellen vanaf een back-up. Dit zal alle bestaande gegevens, inclusief gebruikers en RBAC, overschrijven en kan niet ongedaan worden gemaakt.", "requirementAppliedControlHelpText": "Bewijsstukken die verband houden met de geselecteerde maatregelen worden automatisch aan de eis gekoppeld.", diff --git a/frontend/messages/pt.json b/frontend/messages/pt.json index 5637fde19..d7d4af3cf 100644 --- a/frontend/messages/pt.json +++ b/frontend/messages/pt.json @@ -529,6 +529,7 @@ "asZIP": "em ZIP", "incoming": "aproximação", "outdated": "Desatualizado", + "goBackToAudit": "Volte para a auditoria", "exportBackupDescription": "Isso irá serializar e criar um backup do banco de dados, incluindo usuários e RBAC. Evidências e outros arquivos não estão incluídos no backup.", "importBackupDescription": "Isso irá desserializar e restaurar o banco de dados a partir de um backup. Isso substituirá todos os dados existentes, incluindo usuários e RBAC, e não poderá ser desfeito.", "requirementAppliedControlHelpText": "As evidências vinculadas às medidas selecionadas serão automaticamente associadas ao requisito.", diff --git a/frontend/src/lib/utils/locales.ts b/frontend/src/lib/utils/locales.ts index 44fa7bb80..02d649bc3 100644 --- a/frontend/src/lib/utils/locales.ts +++ b/frontend/src/lib/utils/locales.ts @@ -360,7 +360,8 @@ export function localItems(languageTag: string): LocalItems { remediationPlan: m.remediationPlan({ languageTag: languageTag }), incoming: m.incoming({ languageTag: languageTag }), today: m.today({ languageTag: languageTag }), - outdated: m.outdated({ languageTag: languageTag }) + outdated: m.outdated({ languageTag: languageTag }), + flashMode: m.flashMode({ languageTag: languageTag }) }; return LOCAL_ITEMS; } diff --git a/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/+page.svelte index 8ac67b039..398a1c22e 100644 --- a/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/+page.svelte @@ -198,6 +198,7 @@ data-popup="popupDownload" >

{m.complianceAssessment()}

+ ... {m.asZIP()}{m.actionPlan()} + {m.flashMode()} diff --git a/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/flash-mode/+page.server.ts b/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/flash-mode/+page.server.ts new file mode 100644 index 000000000..1fa2353c4 --- /dev/null +++ b/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/flash-mode/+page.server.ts @@ -0,0 +1,42 @@ +import { BASE_API_URL } from '$lib/utils/constants'; +import type { PageServerLoad } from './$types'; +import type { Actions } from '@sveltejs/kit'; + +export const load = (async ({ fetch, params }) => { + const URLModel = 'compliance-assessments'; + const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/`; + + const res = await fetch(endpoint); + const compliance_assessment = await res.json(); + + const flashMode = await fetch(`${endpoint}flash_mode/`).then((res) => res.json()); + + const requirement_assessments = flashMode.requirement_assessments; + const requirements = flashMode.requirements; + + return { + URLModel, + compliance_assessment, + requirement_assessments, + requirements + }; +}) satisfies PageServerLoad; + +export const actions: Actions = { + updateRequirementAssessment: async (event) => { + const formData = await event.request.formData(); + const values: { id: string; status: string } = { id: '', status: '' }; + for (const entry of formData.entries()) { + values[entry[0]] = entry[1]; + } + const URLModel = 'requirement-assessments'; + const endpoint = `${BASE_API_URL}/${URLModel}/${values.id}/`; + + const requestInitOptions: RequestInit = { + method: 'PATCH', + body: JSON.stringify(values) + }; + + await event.fetch(endpoint, requestInitOptions); + } +}; diff --git a/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/flash-mode/+page.svelte b/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/flash-mode/+page.svelte new file mode 100644 index 000000000..9b86345d1 --- /dev/null +++ b/frontend/src/routes/(app)/compliance-assessments/[id=uuid]/flash-mode/+page.svelte @@ -0,0 +1,138 @@ + + +
+
+ {#if data.requirement_assessments[currentIndex]} +
+
+ +
{currentIndex + 1}/{data.requirement_assessments.length}
+
+
+

{title}

+ {#if data.requirement_assessments[currentIndex].description} + {data.requirement_assessments[currentIndex].description} + {/if} +
+
+
+

{m.status()}

+
+
    + + {#each possible_options as option} +
  • +
    + + +
    +
  • + {/each} +
+
+
+
+
+
+ + +
+ {/if} +
+