diff --git a/backend/core/views.py b/backend/core/views.py index 092155b6b..86e4f3fc9 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -1272,6 +1272,89 @@ def impact_graph(self, request): (viewable_controls_ids, _, _) = RoleAssignment.get_accessible_object_ids( Folder.get_root_folder(), request.user, AppliedControl ) + csf_functions_map = dict() + categories = [{"name": "--"}] + for i, option in enumerate(ReferenceControl.CSF_FUNCTION, 1): + csf_functions_map[option[0]] = i + categories.append({"name": option[1]}) + categories.append({"name": "requirements"}) # 7 + categories.append({"name": "scenarios"}) # 9 + categories.append({"name": "audits"}) # 8 + categories.append({"name": "risk assessments"}) + + nodes = list() + links = list() + indexes = dict() + idx_cnt = 0 + for ac in AppliedControl.objects.filter(id__in=viewable_controls_ids): + nodes.append( + { + "name": ac.name, + "value": ac.name, + "category": csf_functions_map.get(ac.csf_function, 0), + } + ) + indexes[ac.id] = idx_cnt + idx_cnt += 1 + # attached requirement_assessments + for req in RequirementAssessment.objects.filter(applied_controls__id=ac.id): + nodes.append( + { + "name": req.requirement.ref_id, + "value": req.requirement.description, + "category": 7, + "symbol": "triangle", + } + ) + indexes[req.id] = ( + idx_cnt # not good - even if the probability of collision is low + ) + idx_cnt += 1 + + audit = req.compliance_assessment + if indexes.get(audit.id) is None: + nodes.append( + { + "name": audit.name, + "value": audit.framework.name, + "category": 9, + "symbol": "rect", + } + ) + indexes[audit.id] = idx_cnt + idx_cnt += 1 + links.append({"source": indexes[audit.id], "target": indexes[req.id]}) + + links.append({"source": indexes[ac.id], "target": indexes[req.id]}) + for sc in RiskScenario.objects.filter(applied_controls__id=ac.id): + nodes.append( + { + "name": sc.ref_id, + "value": sc.name, + "category": 8, + "symbol": "diamond", + } + ) + indexes[sc.id] = idx_cnt + idx_cnt += 1 + + ra = sc.risk_assessment + if indexes.get(ra.id) is None: + nodes.append( + { + "name": ra.name, + "value": ra.name, + "category": 10, + "symbol": "rect", + } + ) + indexes[ra.id] = idx_cnt + idx_cnt += 1 + links.append({"source": indexes[ra.id], "target": indexes[sc.id]}) + + links.append({"source": indexes[ac.id], "target": indexes[sc.id]}) + + return Response({"nodes": nodes, "categories": categories, "links": links}) class PolicyViewSet(AppliedControlViewSet): diff --git a/frontend/src/lib/components/DataViz/GraphExplorer.svelte b/frontend/src/lib/components/DataViz/GraphExplorer.svelte index cfd9d8d59..16139da92 100644 --- a/frontend/src/lib/components/DataViz/GraphExplorer.svelte +++ b/frontend/src/lib/components/DataViz/GraphExplorer.svelte @@ -8,6 +8,7 @@ export let classesContainer = ''; export let title = ''; export let layout = 'force'; + export let initLayout = 'circular'; export let edgeLength = 50; export let name = 'graph'; @@ -81,7 +82,7 @@ gravity: 0.05, layoutAnimation: true, friction: 0.1, - initLayout: 'circular' + initLayout: initLayout }, labelLayout: { hideOverlap: true diff --git a/frontend/src/routes/(app)/(internal)/insights/controls-impact/+page.server.ts b/frontend/src/routes/(app)/(internal)/insights/controls-impact/+page.server.ts new file mode 100644 index 000000000..ac8154c51 --- /dev/null +++ b/frontend/src/routes/(app)/(internal)/insights/controls-impact/+page.server.ts @@ -0,0 +1,13 @@ + +import { BASE_API_URL } from '$lib/utils/constants'; + +import type { PageServerLoad } from './$types'; + +export const load: PageServerLoad = async ({ params, fetch }) => { + const endpoint = `${BASE_API_URL}/applied-controls/impact_graph/`; + + const res = await fetch(endpoint); + const data = await res.json(); + + return { data }; +}; diff --git a/frontend/src/routes/(app)/(internal)/insights/controls-impact/+page.svelte b/frontend/src/routes/(app)/(internal)/insights/controls-impact/+page.svelte index e69de29bb..f5cd108ef 100644 --- a/frontend/src/routes/(app)/(internal)/insights/controls-impact/+page.svelte +++ b/frontend/src/routes/(app)/(internal)/insights/controls-impact/+page.svelte @@ -0,0 +1,12 @@ + + +
+
+ +
+