diff --git a/backend/core/models.py b/backend/core/models.py index 903841bd7..188719ea2 100644 --- a/backend/core/models.py +++ b/backend/core/models.py @@ -2778,48 +2778,71 @@ def compute_requirement_assessments_results( ) -> list["RequirementAssessment"]: requirement_assessments: list[RequirementAssessment] = [] result_order = ( + RequirementAssessment.Result.NOT_ASSESSED, + RequirementAssessment.Result.NOT_APPLICABLE, RequirementAssessment.Result.NON_COMPLIANT, RequirementAssessment.Result.PARTIALLY_COMPLIANT, RequirementAssessment.Result.COMPLIANT, ) + + def assign_attributes(target, attributes): + """ + Helper function to assign attributes to a target object. + Only assigns if the attribute is not None. + """ + keys = ["result", "status", "score", "is_scored", "observation"] + for key, value in zip(keys, attributes): + if value is not None: + setattr(target, key, value) + for requirement_assessment in self.requirement_assessments.all(): mappings = mapping_set.mappings.filter( target_requirement=requirement_assessment.requirement ) inferences = [] refs = [] + + # Filter for full coverage relationships if applicable if mappings.filter( relationship__in=RequirementMapping.FULL_COVERAGE_RELATIONSHIPS ).exists(): mappings = mappings.filter( relationship__in=RequirementMapping.FULL_COVERAGE_RELATIONSHIPS ) + for mapping in mappings: source_requirement_assessment = RequirementAssessment.objects.get( compliance_assessment=source_assessment, requirement=mapping.source_requirement, ) - inferred_result, inferred_status = requirement_assessment.infer_result( + inferred_result = requirement_assessment.infer_result( mapping=mapping, source_requirement_assessment=source_requirement_assessment, ) - if inferred_result in result_order: - inferences.append((inferred_result, inferred_status)) + if inferred_result.get("result") in result_order: + inferences.append( + ( + inferred_result.get("result"), + inferred_result.get("status"), + inferred_result.get("score"), + inferred_result.get("is_scored"), + inferred_result.get("observation"), + ) + ) refs.append(source_requirement_assessment) + if inferences: if len(inferences) == 1: - requirement_assessment.result = inferences[0][0] - if inferences[0][1]: - requirement_assessment.status = inferences[0][1] + selected_inference = inferences[0] ref = refs[0] else: - lowest_result = min( + selected_inference = min( inferences, key=lambda x: result_order.index(x[0]) ) - requirement_assessment.result = lowest_result[0] - if lowest_result[1]: - requirement_assessment.status = lowest_result[1] - ref = refs[inferences.index(lowest_result)] + ref = refs[inferences.index(selected_inference)] + + assign_attributes(requirement_assessment, selected_inference) + requirement_assessment.mapping_inference = { "result": requirement_assessment.result, "source_requirement_assessment": { @@ -2829,7 +2852,9 @@ def compute_requirement_assessments_results( }, # "mappings": [mapping.id for mapping in mappings], } + requirement_assessments.append(requirement_assessment) + return requirement_assessments def progress(self) -> int: @@ -2927,24 +2952,39 @@ def get_requirement_description(self) -> str: def infer_result( self, mapping: RequirementMapping, source_requirement_assessment: Self - ) -> str | None: + ) -> dict | None: if mapping.coverage == RequirementMapping.Coverage.FULL: - return ( - source_requirement_assessment.result, - source_requirement_assessment.status, - ) + if ( + source_requirement_assessment.compliance_assessment.min_score + == self.compliance_assessment.min_score + and source_requirement_assessment.compliance_assessment.max_score + == self.compliance_assessment.max_score + ): + return { + "result": source_requirement_assessment.result, + "status": source_requirement_assessment.status, + "score": source_requirement_assessment.score, + "is_scored": source_requirement_assessment.is_scored, + "observation": source_requirement_assessment.observation, + } + else: + return { + "result": source_requirement_assessment.result, + "status": source_requirement_assessment.status, + "observation": source_requirement_assessment.observation, + } if mapping.coverage == RequirementMapping.Coverage.PARTIAL: if source_requirement_assessment.result in ( RequirementAssessment.Result.COMPLIANT, RequirementAssessment.Result.PARTIALLY_COMPLIANT, ): - return (RequirementAssessment.Result.PARTIALLY_COMPLIANT, None) + return {"result": RequirementAssessment.Result.PARTIALLY_COMPLIANT} if ( source_requirement_assessment.result == RequirementAssessment.Result.NON_COMPLIANT ): - return (RequirementAssessment.Result.NON_COMPLIANT, None) - return (None, None) + return {"result": RequirementAssessment.Result.NON_COMPLIANT} + return {} def create_applied_controls_from_suggestions(self) -> list[AppliedControl]: applied_controls: list[AppliedControl] = [] diff --git a/frontend/messages/ar.json b/frontend/messages/ar.json index c472d7904..ded4c205e 100644 --- a/frontend/messages/ar.json +++ b/frontend/messages/ar.json @@ -857,5 +857,7 @@ "tags": "العلامات", "addTag": "إضافة علامة", "tagsHelpText": "تُستخدم العلامات لتصنيف العناصر وتصفيتها. يمكنك إضافة علامات في قسم \"إضافي\"", - "forgotPassword": "هل نسيت كلمة السر" + "forgotPassword": "هل نسيت كلمة السر", + "scoreSemiColon": "نتيجة:", + "mappingInferenceHelpText": "هذه المتغيرات ثابتة ولن تتغير اعتمادًا على المصدر." } diff --git a/frontend/messages/de.json b/frontend/messages/de.json index 732a26fb8..479b62086 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -856,5 +856,7 @@ "tags": "Schlagwörter", "addTag": "Tag hinzufügen", "tagsHelpText": "Tags werden zum Kategorisieren und Filtern der Elemente verwendet. Sie können Tags im Abschnitt Extra hinzufügen", - "forgotPassword": "Passwort vergessen" + "forgotPassword": "Passwort vergessen", + "scoreSemiColon": "Punktzahl:", + "mappingInferenceHelpText": "Diese Variablen sind fest und ändern sich je nach Quelle nicht." } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index b8cf7b63c..42a0fa288 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -887,6 +887,8 @@ "youCanSetPasswordHere": "You can set your password here", "forgotPassword": "Forgot password", "ssoSettingsUpdated": "SSO settings updated", + "scoreSemiColon": "Score:", + "mappingInferenceHelpText": "These variables are fixed and will not change depending on the source.", "priority": "Priority", "p1": "P1", "p2": "P2", diff --git a/frontend/messages/es.json b/frontend/messages/es.json index aaae117b0..e54e82223 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -856,5 +856,7 @@ "tags": "Etiquetas", "addTag": "Agregar etiqueta", "tagsHelpText": "Las etiquetas se utilizan para categorizar y filtrar los elementos. Puedes agregar etiquetas en la sección Extra", - "forgotPassword": "Has olvidado tu contraseña" + "forgotPassword": "Has olvidado tu contraseña", + "scoreSemiColon": "Puntaje:", + "mappingInferenceHelpText": "Estas variables son fijas y no cambiarán dependiendo de la fuente." } diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 0afbc8a92..dbe72b3a1 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -864,5 +864,7 @@ "existingMeasures": "Mesures existantes", "youCanSetPasswordHere": "Vous pouvez définir votre mot de passe ici", "forgotPassword": "Mot de passe oublié", - "ssoSettingsUpdated": "Paramètres SSO mis à jour" + "ssoSettingsUpdated": "Paramètres SSO mis à jour", + "scoreSemiColon": "Score:", + "mappingInferenceHelpText": "Ces variables sont fixes et ne changeront pas en fonction de la source." } diff --git a/frontend/messages/hi.json b/frontend/messages/hi.json index a5b4032ca..e3c563e07 100644 --- a/frontend/messages/hi.json +++ b/frontend/messages/hi.json @@ -856,5 +856,7 @@ "tags": "टैग", "addTag": "टैग जोड़ें", "tagsHelpText": "टैग का उपयोग आइटम को वर्गीकृत और फ़िल्टर करने के लिए किया जाता है। आप अतिरिक्त अनुभाग में टैग जोड़ सकते हैं", - "forgotPassword": "पासवर्ड भूल गए" + "forgotPassword": "पासवर्ड भूल गए", + "scoreSemiColon": "अंक:", + "mappingInferenceHelpText": "ये चर निश्चित हैं और स्रोत के आधार पर परिवर्तित नहीं होंगे।" } diff --git a/frontend/messages/it.json b/frontend/messages/it.json index 1dc157b7f..e05d289d8 100644 --- a/frontend/messages/it.json +++ b/frontend/messages/it.json @@ -856,5 +856,7 @@ "tags": "Etichette", "addTag": "Aggiungi tag", "tagsHelpText": "I tag vengono utilizzati per categorizzare e filtrare gli elementi. Puoi aggiungere tag nella sezione Extra", - "forgotPassword": "Ha dimenticato la password" + "forgotPassword": "Ha dimenticato la password", + "scoreSemiColon": "Punto:", + "mappingInferenceHelpText": "Queste variabili sono fisse e non cambiano a seconda della fonte." } diff --git a/frontend/messages/nl.json b/frontend/messages/nl.json index b2fb980fa..0329a0525 100644 --- a/frontend/messages/nl.json +++ b/frontend/messages/nl.json @@ -856,5 +856,7 @@ "tags": "Labels", "addTag": "Tag toevoegen", "tagsHelpText": "Tags worden gebruikt om de items te categoriseren en te filteren. U kunt tags toevoegen in de sectie Extra", - "forgotPassword": "Wachtwoord vergeten" + "forgotPassword": "Wachtwoord vergeten", + "scoreSemiColon": "Punt:", + "mappingInferenceHelpText": "Deze variabelen zijn vast en veranderen niet, afhankelijk van de bron." } diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json index 75a2cf2eb..4532052f4 100644 --- a/frontend/messages/pl.json +++ b/frontend/messages/pl.json @@ -856,5 +856,7 @@ "tags": "Tagi", "addTag": "Dodaj tag", "tagsHelpText": "Tagi służą do kategoryzowania i filtrowania elementów. Możesz dodać tagi w sekcji Extra", - "forgotPassword": "Zapomniałem hasła" + "forgotPassword": "Zapomniałem hasła", + "scoreSemiColon": "Wynik:", + "mappingInferenceHelpText": "Te zmienne są stałe i nie zmieniają się w zależności od źródła." } diff --git a/frontend/messages/pt.json b/frontend/messages/pt.json index c08b43cab..edc81af78 100644 --- a/frontend/messages/pt.json +++ b/frontend/messages/pt.json @@ -856,5 +856,7 @@ "tags": "Etiquetas", "addTag": "Adicionar etiqueta", "tagsHelpText": "As tags são usadas para categorizar e filtrar os itens. Você pode adicionar tags na seção Extra", - "forgotPassword": "Esqueceu sua senha" + "forgotPassword": "Esqueceu sua senha", + "scoreSemiColon": "Pontuação:", + "mappingInferenceHelpText": "Essas variáveis são fixas e não mudarão dependendo da fonte." } diff --git a/frontend/messages/ro.json b/frontend/messages/ro.json index 5fc13d0ff..14c6f3d23 100644 --- a/frontend/messages/ro.json +++ b/frontend/messages/ro.json @@ -856,5 +856,7 @@ "tags": "Etichete", "addTag": "Adăugați etichetă", "tagsHelpText": "Etichetele sunt folosite pentru a clasifica și filtra articolele. Puteți adăuga etichete în secțiunea Extra", - "forgotPassword": "Aţi uitat parola" + "forgotPassword": "Aţi uitat parola", + "scoreSemiColon": "Scor:", + "mappingInferenceHelpText": "Aceste variabile sunt fixe și nu se vor modifica în funcție de sursă." } diff --git a/frontend/messages/sv.json b/frontend/messages/sv.json index 5bf052cb7..69ae0d8d8 100644 --- a/frontend/messages/sv.json +++ b/frontend/messages/sv.json @@ -860,5 +860,7 @@ "existingMeasures": "Befintliga kontroller", "youCanSetPasswordHere": "Du kan ställa in ditt lösenord här", "forgotPassword": "Glömt lösenord", - "ssoSettingsUpdated": "SSO-inställningar uppdaterade" + "ssoSettingsUpdated": "SSO-inställningar uppdaterade", + "scoreSemiColon": "Göra:", + "mappingInferenceHelpText": "Dessa variabler är fasta och kommer inte att ändras beroende på källan." } diff --git a/frontend/messages/ur.json b/frontend/messages/ur.json index 27feffa59..7024a3a67 100644 --- a/frontend/messages/ur.json +++ b/frontend/messages/ur.json @@ -856,5 +856,7 @@ "tags": "ٹیگز", "addTag": "ٹیگ شامل کریں۔", "tagsHelpText": "ٹیگز اشیاء کی درجہ بندی اور فلٹر کرنے کے لیے استعمال ہوتے ہیں۔ آپ اضافی سیکشن میں ٹیگ شامل کر سکتے ہیں۔", - "forgotPassword": "پاس ورڈ بھول گئے۔" + "forgotPassword": "پاس ورڈ بھول گئے۔", + "scoreSemiColon": "سکور:", + "mappingInferenceHelpText": "یہ متغیرات طے شدہ ہیں اور ماخذ کے لحاظ سے تبدیل نہیں ہوں گے۔" } diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte index 7f757b20c..33163029d 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/+page.svelte @@ -192,6 +192,9 @@ {m.mappingInference()}
+ {m.mappingInferenceHelpText()}@@ -205,7 +208,7 @@
{m.coverageColon()} - + {safeTranslate( toCamelCase(mappingInference.sourceRequirementAssessment.coverage) )} diff --git a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte index c9da5a0f9..8b247aecc 100644 --- a/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte +++ b/frontend/src/routes/(app)/(third-party)/requirement-assessments/[id=uuid]/edit/+page.svelte @@ -327,6 +327,9 @@ {m.mappingInference()}
+ {m.mappingInferenceHelpText()}@@ -340,7 +343,7 @@
{m.coverageColon()} - + {safeTranslate(mappingInference.sourceRequirementAssessment.coverage)}