diff --git a/backend/ebios_rm/serializers.py b/backend/ebios_rm/serializers.py
index fceac647ed..2845e70987 100644
--- a/backend/ebios_rm/serializers.py
+++ b/backend/ebios_rm/serializers.py
@@ -90,7 +90,7 @@ class RoToReadSerializer(BaseModelSerializer):
str = serializers.CharField(source="__str__")
ebios_rm_study = FieldsRelatedField()
folder = FieldsRelatedField()
- feared_events = FieldsRelatedField(many=True)
+ feared_events = FieldsRelatedField(["folder", "id"], many=True)
class Meta:
model = RoTo
diff --git a/backend/ebios_rm/views.py b/backend/ebios_rm/views.py
index 1ca61f709b..37f8e205fd 100644
--- a/backend/ebios_rm/views.py
+++ b/backend/ebios_rm/views.py
@@ -43,6 +43,10 @@ class FearedEventViewSet(BaseModelViewSet):
class RoToViewSet(BaseModelViewSet):
model = RoTo
+ filterset_fields = [
+ "ebios_rm_study",
+ ]
+
@action(detail=False, name="Get risk origin choices", url_path="risk-origin")
def risk_origin(self, request):
return Response(dict(RoTo.RiskOrigin.choices))
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index ca6cab15a9..c7ef5027ae 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -384,7 +384,7 @@
"exportDatabase": "Export database",
"upload": "Upload",
"add": "Add",
- "undefined": "--",
+ "undefined": "undefined",
"production": "Production",
"design": "Design",
"development": "Development",
@@ -929,5 +929,12 @@
"impacts": "Impacts",
"ebiosRmStudies": "Ebios RM studies",
"bringTheEvidences": "Bring the evidences",
- "bringTheEvidencesHelpText": "If disabled, the object will be duplicated without its evidences"
+ "bringTheEvidencesHelpText": "If disabled, the object will be duplicated without its evidences",
+ "fearedEvents": "Feared events",
+ "riskOrigin": "Risk origin",
+ "targetObjective": "Target objective",
+ "motivation": "Motivation",
+ "resources": "Resources",
+ "pertinence": "Pertinence",
+ "isSelected": "Is selected"
}
diff --git a/frontend/src/lib/components/Forms/ModelForm.svelte b/frontend/src/lib/components/Forms/ModelForm.svelte
index 7f30a2a719..3866c9d84a 100644
--- a/frontend/src/lib/components/Forms/ModelForm.svelte
+++ b/frontend/src/lib/components/Forms/ModelForm.svelte
@@ -28,6 +28,7 @@
import GeneralSettingsForm from './ModelForm/GeneralSettingForm.svelte';
import EbiosRmForm from './ModelForm/EbiosRmForm.svelte';
import FearedEventForm from './ModelForm/FearedEventForm.svelte';
+ import RoToForm from './ModelForm/RoToForm.svelte';
import AutocompleteSelect from './AutocompleteSelect.svelte';
@@ -261,6 +262,8 @@
{:else if URLModel === 'feared-events'}
+ {:else if URLModel === 'ro-to'}
+
{/if}
{#if closeModal}
diff --git a/frontend/src/lib/components/Forms/ModelForm/RoToForm.svelte b/frontend/src/lib/components/Forms/ModelForm/RoToForm.svelte
new file mode 100644
index 0000000000..cdbc7544ed
--- /dev/null
+++ b/frontend/src/lib/components/Forms/ModelForm/RoToForm.svelte
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts
index 0c9c667f46..f915a17c9e 100644
--- a/frontend/src/lib/utils/crud.ts
+++ b/frontend/src/lib/utils/crud.ts
@@ -621,6 +621,24 @@ export const URL_MODEL_MAP: ModelMap = {
{ field: 'assets', urlModel: 'assets' },
{ field: 'qualifications', urlModel: 'qualifications' }
]
+ },
+ 'ro-to': {
+ endpointUrl: 'ebios-rm/ro-to',
+ name: 'roto',
+ localName: 'roto',
+ localNamePlural: 'roto',
+ verboseName: 'Ro to',
+ verboseNamePlural: 'Ro to',
+ foreignKeyFields: [
+ { field: 'ebios_rm_study', urlModel: 'ebios-rm' },
+ { field: 'feared_events', urlModel: 'feared-events' }
+ ],
+ selectFields: [
+ { field: 'risk-origin' },
+ { field: 'motivation' },
+ { field: 'resources' },
+ { field: 'pertinence' }
+ ]
}
};
diff --git a/frontend/src/lib/utils/schemas.ts b/frontend/src/lib/utils/schemas.ts
index f862915fda..0b793b4246 100644
--- a/frontend/src/lib/utils/schemas.ts
+++ b/frontend/src/lib/utils/schemas.ts
@@ -413,6 +413,19 @@ export const fearedEventsSchema = z.object({
qualifications: z.string().optional().array().optional()
});
+export const roToSchema = z.object({
+ ebios_rm_study: z.string(),
+ feared_events: z.string().uuid().array(),
+ risk_origin: z.string(),
+ target_objective: z.string(),
+ motivation: z.string().optional(),
+ ressources: z.string().optional(),
+ pertinence: z.string().optional(),
+ activity: z.number().optional().default(0),
+ is_selected: z.boolean().optional().default(false),
+ justification: z.string().optional()
+});
+
const SCHEMA_MAP: Record
= {
folders: FolderSchema,
projects: ProjectSchema,
@@ -439,7 +452,8 @@ const SCHEMA_MAP: Record = {
vulnerabilities: vulnerabilitySchema,
'filtering-labels': FilteringLabelSchema,
'ebios-rm': ebiosRMSchema,
- 'feared-events': fearedEventsSchema
+ 'feared-events': fearedEventsSchema,
+ 'ro-to': roToSchema
};
export const modelSchema = (model: string) => {
diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts
index c4623fa3b6..082feae357 100644
--- a/frontend/src/lib/utils/table.ts
+++ b/frontend/src/lib/utils/table.ts
@@ -535,5 +535,23 @@ export const listViewFields: ListViewFieldsConfig = {
'feared-events': {
head: ['selected', 'assets', 'fearedEvent', 'qualifications', 'gravity'],
body: ['is_selected', 'assets', 'description', 'qualifications', 'gravity']
+ },
+ 'ro-to': {
+ head: [
+ 'isSelected',
+ 'riskOrigin',
+ 'targetObjective',
+ 'motivation',
+ 'fearedEvents',
+ 'pertinence'
+ ],
+ body: [
+ 'is_selected',
+ 'risk_origin',
+ 'target_objective',
+ 'motivation',
+ 'feared_events',
+ 'pertinence'
+ ]
}
};
diff --git a/frontend/src/lib/utils/types.ts b/frontend/src/lib/utils/types.ts
index 14c7c0356d..0e1dde4503 100644
--- a/frontend/src/lib/utils/types.ts
+++ b/frontend/src/lib/utils/types.ts
@@ -52,7 +52,8 @@ export const URL_MODEL = [
'representatives',
'vulnerabilities',
'filtering-labels',
- 'feared-events'
+ 'feared-events',
+ 'ro-to'
// 'ebios-rm',
] as const;
diff --git a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+page.server.ts b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+page.server.ts
index 61f4258526..169ddf8075 100644
--- a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+page.server.ts
+++ b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+page.server.ts
@@ -27,7 +27,10 @@ export const load: PageServerLoad = async ({ params, fetch }) => {
for (const keyField of foreignKeyFields) {
const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
- const url = `${BASE_API_URL}/${keyField.urlModel}/${queryParams}`;
+ const keyModel = getModelInfo(keyField.urlModel);
+ const url = keyModel.endpointUrl
+ ? `${BASE_API_URL}/${keyModel.endpointUrl}/${queryParams}`
+ : `${BASE_API_URL}/${keyField.urlModel}/${queryParams}`;
const response = await fetch(url);
if (response.ok) {
foreignKeys[keyField.field] = await response.json().then((data) => data.results);
@@ -42,7 +45,9 @@ export const load: PageServerLoad = async ({ params, fetch }) => {
for (const selectField of selectFields) {
if (selectField.detail) continue;
- const url = `${BASE_API_URL}/${params.model}/${selectField.field}/`;
+ const url = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${selectField.field}/`
+ : `${BASE_API_URL}/${params.model}/${selectField.field}/`;
const response = await fetch(url);
if (response.ok) {
selectOptions[selectField.field] = await response.json().then((data) =>
diff --git a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/+page.server.ts b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/+page.server.ts
index 699a98520f..a1d7091951 100644
--- a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/+page.server.ts
+++ b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/+page.server.ts
@@ -26,8 +26,11 @@ export const load: PageServerLoad = async (event) => {
if (modelInfo.foreignKeyFields) {
await Promise.all(
modelInfo.foreignKeyFields.map(async (keyField) => {
+ const keyModel = getModelInfo(keyField.urlModel);
const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
- const url = `${BASE_API_URL}/${keyField.urlModel}/${queryParams}`;
+ const url = keyModel.endpointUrl
+ ? `${BASE_API_URL}/${keyModel.endpointUrl}/${queryParams}`
+ : `${BASE_API_URL}/${keyField.urlModel}/${queryParams}`;
const response = await event.fetch(url);
if (response.ok) {
foreignKeys[keyField.field] = await response.json().then((data) => data.results);
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte
index af9816e3d5..dc1a7deb46 100644
--- a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte
@@ -34,9 +34,21 @@
}
],
ws2: [
- { title: safeTranslate(m.ebiosWs2_1()), status: 'to_do', href: '#' },
- { title: safeTranslate(m.ebiosWs2_2()), status: 'to_do', href: '#' },
- { title: safeTranslate(m.ebiosWs2_3()), status: 'to_do', href: '#' }
+ {
+ title: safeTranslate(m.ebiosWs2_1()),
+ status: 'to_do',
+ href: `${$page.url.pathname}/workshop-two/ro-to?next=${$page.url.pathname}`
+ },
+ {
+ title: safeTranslate(m.ebiosWs2_2()),
+ status: 'to_do',
+ href: `${$page.url.pathname}/workshop-two/ro-to?next=${$page.url.pathname}`
+ },
+ {
+ title: safeTranslate(m.ebiosWs2_3()),
+ status: 'to_do',
+ href: `${$page.url.pathname}/workshop-two/ro-to?next=${$page.url.pathname}`
+ }
],
ws3: [
{ title: safeTranslate(m.ebiosWs3_1()), status: 'to_do', href: '#' },
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-two/ro-to/+page.server.ts b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-two/ro-to/+page.server.ts
new file mode 100644
index 0000000000..ba85292593
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-two/ro-to/+page.server.ts
@@ -0,0 +1,107 @@
+import { defaultDeleteFormAction, defaultWriteFormAction } from '$lib/utils/actions';
+import { BASE_API_URL } from '$lib/utils/constants';
+import {
+ getModelInfo,
+ urlParamModelForeignKeyFields,
+ urlParamModelSelectFields
+} from '$lib/utils/crud';
+import { modelSchema } from '$lib/utils/schemas';
+import type { ModelInfo, urlModel } from '$lib/utils/types';
+import { type Actions } from '@sveltejs/kit';
+import { superValidate } from 'sveltekit-superforms';
+import { zod } from 'sveltekit-superforms/adapters';
+import { z } from 'zod';
+import type { PageServerLoad } from './$types';
+import { listViewFields } from '$lib/utils/table';
+import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton';
+
+export const load: PageServerLoad = async ({ params, fetch }) => {
+ const schema = z.object({ id: z.string().uuid() });
+ const deleteForm = await superValidate(zod(schema));
+ const URLModel = 'ro-to';
+ const createSchema = modelSchema(URLModel);
+ const initialData = {
+ ebios_rm_study: params.id
+ };
+ const createForm = await superValidate(initialData, zod(createSchema), { errors: false });
+ const model: ModelInfo = getModelInfo(URLModel);
+ const foreignKeyFields = urlParamModelForeignKeyFields(URLModel);
+ const selectFields = urlParamModelSelectFields(URLModel);
+
+ const foreignKeys: Record = {};
+
+ for (const keyField of foreignKeyFields) {
+ const keyModel = getModelInfo(keyField.urlModel);
+ const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
+ const url = keyModel.endpointUrl
+ ? `${BASE_API_URL}/${keyModel.endpointUrl}/${queryParams}`
+ : `${BASE_API_URL}/${keyModel.urlModel}/${queryParams}`;
+ const response = await fetch(url);
+ if (response.ok) {
+ foreignKeys[keyField.field] = await response.json().then((data) => data.results);
+ } else {
+ console.error(`Failed to fetch data for ${keyField.field}: ${response.statusText}`);
+ }
+ }
+
+ model['foreignKeys'] = foreignKeys;
+
+ const selectOptions: Record = {};
+
+ for (const selectField of selectFields) {
+ if (selectField.detail) continue;
+ const url = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${selectField.field}/`
+ : `${BASE_API_URL}/${model.urlModel}/${selectField.field}/`;
+ const response = await fetch(url);
+ if (response.ok) {
+ selectOptions[selectField.field] = await response.json().then((data) =>
+ Object.entries(data).map(([key, value]) => ({
+ label: value,
+ value: key
+ }))
+ );
+ } else {
+ console.error(`Failed to fetch data for ${selectField.field}: ${response.statusText}`);
+ }
+ }
+
+ model['selectOptions'] = selectOptions;
+
+ const endpoint = `${BASE_API_URL}/${model.endpointUrl}?ebios_rm_study=${params.id}`;
+ const res = await fetch(endpoint);
+ const data = await res.json().then((res) => res.results);
+
+ const bodyData = tableSourceMapper(data, listViewFields[URLModel as urlModel].body);
+
+ const headData: Record = listViewFields[URLModel as urlModel].body.reduce(
+ (obj, key, index) => {
+ obj[key] = listViewFields[URLModel as urlModel].head[index];
+ return obj;
+ },
+ {}
+ );
+
+ const table: TableSource = {
+ head: headData,
+ body: bodyData,
+ meta: data // metaData
+ };
+
+ return { createForm, deleteForm, model, URLModel, table };
+};
+
+export const actions: Actions = {
+ create: async (event) => {
+ // const redirectToWrittenObject = Boolean(event.params.model === 'entity-assessments');
+ return defaultWriteFormAction({
+ event,
+ urlModel: 'ro-to',
+ action: 'create'
+ // redirectToWrittenObject: redirectToWrittenObject
+ });
+ },
+ delete: async (event) => {
+ return defaultDeleteFormAction({ event, urlModel: 'ro-to' });
+ }
+};
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-two/ro-to/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-two/ro-to/+page.svelte
new file mode 100644
index 0000000000..08ebedae85
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-two/ro-to/+page.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+