Skip to content

Commit

Permalink
feat: add audit action plan
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohamed-Hacene committed May 15, 2024
1 parent 01dffac commit 0ee3a41
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 25 deletions.
16 changes: 10 additions & 6 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,23 +1196,27 @@ def action_plan(self, request, pk):
object_type=ComplianceAssessment,
)
if UUID(pk) in viewable_objects:
response = {"planned": dict(), "active": dict(), "inactive": dict(), "none": dict()}
response = {
"planned": list(),
"active": list(),
"inactive": list(),
"none": list(),
}
compliance_assessment_object = self.get_object()
requirement_assessments_objects = (
compliance_assessment_object.get_requirement_assessments()
)
applied_controls = AppliedControlReadSerializer(
AppliedControl.objects.filter(
requirement_assessments__in=requirement_assessments_objects
),
).distinct(),
many=True,
).data
for applied_control in applied_controls:
response[applied_control["status"].lower()].update(
applied_control
) if applied_control["status"] else response["none"].update(
applied_control["requirements_count"] = RequirementAssessment.objects.filter(compliance_assessment=compliance_assessment_object).filter(applied_controls=applied_control["id"]).count()
response[applied_control["status"].lower()].append(
applied_control
)
) if applied_control["status"] else response["none"].append(applied_control)
return Response(response)

def perform_create(self, serializer):
Expand Down
5 changes: 4 additions & 1 deletion frontend/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -516,5 +516,8 @@
"implementationGroupsDefinition": "Implementation groups definition",
"threatRadarChart": "Threat radar",
"noThreatsMapped": "No threats mapped. Consider attaching threats to your risk scenarios for a better overview.",
"actionPlan": "Action plan"
"actionPlan": "Action plan",
"noStatus": "No status",
"actionPlanHelpText": "Separated by status and sorted by eta",
"requirementsCount": "Requirements count"
}
4 changes: 2 additions & 2 deletions frontend/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@
"currentRisk": "Risque courant",
"residualRisk": "Risque résiduel",
"planned": "Planifié",
"active": "Active",
"inactive": "Inactive",
"active": "Actif",
"inactive": "Inactif",
"watchlist": "Liste de surveillance",
"watchlistDescription": "Objets expirés ou expirant bientôt",
"measuresToReview": "Mesures à revoir",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/lib/utils/locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ export function localItems(languageTag: string): LocalItems {
languageTag: languageTag
}),
actionPlan: m.actionPlan({ languageTag: languageTag }),
requirementsCount: m.requirementsCount({ languageTag: languageTag }),
};
return LOCAL_ITEMS;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import type { PageServerLoad } from './$types';
export const load = (async ({ fetch, params }) => {
const URLModel = 'compliance-assessments';
const endpoint = `${BASE_API_URL}/${URLModel}/${params.id}/`;
const actionPlanEndpoint = `${BASE_API_URL}/${URLModel}/${params.id}/action_plan/`;

const res = await fetch(endpoint);
const actionPlanRes = await fetch(actionPlanEndpoint);
const compliance_assessment = await res.json();
return { URLModel, compliance_assessment };
const actionPlan = await actionPlanRes.json();
return { URLModel, compliance_assessment, actionPlan };
}) satisfies PageServerLoad;
Original file line number Diff line number Diff line change
@@ -1,24 +1,67 @@
<script lang="ts">
import * as m from '$paraglide/messages.js';
import type { TableSource } from '$lib/components/ModelTable/types';
import { Tab, TabGroup, tableSourceMapper } from '@skeletonlabs/skeleton';
import ModelTable from '$lib/components/ModelTable/ModelTable.svelte';
export let data;
const measureStatusColorMap = (treatment: string) => {
const map: Record<string, string> = {
open: 'bg-orange-200',
'in progress': 'bg-blue-200',
'on hold': 'bg-red-200',
done: 'bg-success-200'
};
if (treatment !== null) {
return map[treatment.toLowerCase()];
} else {
return 'bg-gray-200';
}
let tabSet = 0;
const plannedAppliedControls: TableSource = {
head: {
name: 'name',
category: 'category',
eta: 'eta',
expiry_date: 'expiryDate',
effort: 'effort',
requirements_count: 'requirementsCount'
},
body: tableSourceMapper(data.actionPlan.planned, ['name', 'category', 'eta', 'expiry_date', 'efforts', 'requirements_count']),
meta: data.actionPlan.planned,
};
const activeAppliedControls: TableSource = {
head: {
name: 'name',
category: 'category',
eta: 'eta',
expiry_date: 'expiryDate',
effort: 'effort',
requirements_count: 'requirementsCount'
},
body: tableSourceMapper(data.actionPlan.active, ['name', 'category', 'eta', 'expiry_date', 'efforts', 'requirements_count']),
meta: data.actionPlan.active,
};
const inactiveAppliedControls: TableSource = {
head: {
name: 'name',
category: 'category',
eta: 'eta',
expiry_date: 'expiryDate',
effort: 'effort',
requirements_count: 'requirementsCount'
},
body: tableSourceMapper(data.actionPlan.inactive, ['name', 'category', 'eta', 'expiry_date', 'efforts', 'requirements_count']),
meta: data.actionPlan.inactive,
};
const noneAppliedControls: TableSource = {
head: {
name: 'name',
category: 'category',
eta: 'eta',
expiry_date: 'expiryDate',
effort: 'effort',
requirements_count: 'requirementsCount'
},
body: tableSourceMapper(data.actionPlan.none, ['name', 'category', 'eta', 'expiry_date', 'efforts', 'requirements_count']),
meta: data.actionPlan.none,
};
</script>

<div class="bg-white p-2 m-2 shadow rounded-lg space-x-2 flex flex-row justify-center">
<div class="bg-white p-2 shadow rounded-lg space-x-2 flex flex-row justify-center mb-2">
<p class="font-semibold text-lg">
{m.project()}:
<a
Expand All @@ -36,5 +79,68 @@
>
</p>
</div>

<p class="p-2 m-2 text-lg font-semibold">{m.associatedRiskScenarios()}:</p>
<div class="flex flex-col space-y-4 bg-white p-4 shadow rounded-lg space-x-2">
<div>
<p class="text-xl font-extrabold">{m.associatedAppliedControls()}</p>
<p class="text-sm text-gray-500">
{m.actionPlanHelpText()}
</p>
</div>
<div class="">
<TabGroup>
<Tab bind:group={tabSet} name="planned" value={0}
>{m.planned()}</Tab
>
<Tab bind:group={tabSet} name="active" value={1}
>{m.active()}</Tab
>
<Tab bind:group={tabSet} name="inactive" value={2}
>{m.inactive()}</Tab
>
<Tab bind:group={tabSet} name="noStatus" value={3}
>{m.noStatus()}</Tab
>
<svelte:fragment slot="panel">
<div class="p-2">
{#if tabSet === 0}
<ModelTable
URLModel="applied-controls"
source={plannedAppliedControls}
search={true}
rowsPerPage={true}
orderBy={{ identifier: 'eta', direction: 'desc' }}
/>
{/if}
{#if tabSet === 1}
<ModelTable
URLModel="applied-controls"
source={activeAppliedControls}
search={true}
rowsPerPage={true}
orderBy={{ identifier: 'eta', direction: 'desc' }}
/>
{/if}
{#if tabSet === 2}
<ModelTable
URLModel="applied-controls"
source={inactiveAppliedControls}
search={true}
rowsPerPage={true}
orderBy={{ identifier: 'eta', direction: 'desc' }}
/>
{/if}
{#if tabSet === 3}
<ModelTable
URLModel="applied-controls"
source={noneAppliedControls}
search={true}
rowsPerPage={true}
orderBy={{ identifier: 'eta', direction: 'desc' }}
/>
{/if}
</div>
</svelte:fragment>
</TabGroup>
</div>

</div>

0 comments on commit 0ee3a41

Please sign in to comment.