From 2de9e3f260397da283308cff069c0a728d64a945 Mon Sep 17 00:00:00 2001 From: robbertuittenbroek Date: Tue, 25 Feb 2025 10:16:55 +0100 Subject: [PATCH 1/2] Make AI Act profile editable and use a new version of the beslishulp --- amt/api/deps.py | 11 +++ amt/api/editable.py | 53 ++++++++++++- amt/api/editable_classes.py | 5 ++ amt/api/editable_value_providers.py | 31 ++++++++ amt/locale/base.pot | 59 +++++++------- amt/locale/en_US/LC_MESSAGES/messages.mo | Bin 1409 -> 1409 bytes amt/locale/en_US/LC_MESSAGES/messages.po | 59 +++++++------- amt/locale/nl_NL/LC_MESSAGES/messages.mo | Bin 14039 -> 13968 bytes amt/locale/nl_NL/LC_MESSAGES/messages.po | 59 +++++++------- amt/schema/ai_act_profile.py | 20 +---- amt/schema/webform.py | 3 + amt/services/task_registry.py | 17 ++--- amt/site/static/beslishulp.html | 11 +++ amt/site/static/scss/layout.scss | 52 ++++++++----- amt/site/static/ts/amt.ts | 72 +++++++++++++++++- .../templates/algorithms/details_base.html.j2 | 49 ++++++------ .../algorithms/details_compliance.html.j2 | 9 +-- .../algorithms/details_measure_modal.html.j2 | 6 +- amt/site/templates/algorithms/new.html.j2 | 19 +---- .../templates/layouts/base.html.j2.webpack | 10 +++ amt/site/templates/macros/editable.html.j2 | 42 ++++++++-- amt/site/templates/macros/form_macros.html.j2 | 63 ++++++++------- amt/site/templates/macros/tasks.html.j2 | 2 +- .../templates/organizations/members.html.j2 | 54 ++++++------- .../parts/add_members_modal.html.j2 | 3 + .../parts/members_results.html.j2 | 2 +- amt/site/templates/parts/tasks_board.html.j2 | 7 -- tests/schema/test_schema_ai_act_profile.py | 17 +---- tests/services/test_task_registry_service.py | 2 +- 29 files changed, 445 insertions(+), 292 deletions(-) create mode 100644 amt/api/editable_value_providers.py create mode 100644 amt/site/static/beslishulp.html diff --git a/amt/api/deps.py b/amt/api/deps.py index 9d627f6c..be07ee5d 100644 --- a/amt/api/deps.py +++ b/amt/api/deps.py @@ -152,6 +152,15 @@ def hasattr_jinja(obj: object, attributes: str) -> bool: return True +def equal_or_includes(my_value: str, check_against_value: str | list[str] | tuple[str]) -> bool: + """Test if my_value equals or exists in check_against_value""" + if isinstance(check_against_value, list | tuple): + return my_value in check_against_value + elif isinstance(check_against_value, str): + return my_value == check_against_value + return False + + templates = LocaleJinja2Templates( directory="amt/site/templates/", context_processors=[custom_context_processor], undefined=get_undefined_behaviour() ) @@ -172,5 +181,7 @@ def hasattr_jinja(obj: object, attributes: str) -> bool: templates.env.globals.update(is_parent_editable=is_parent_editable) # pyright: ignore [reportUnknownMemberType] templates.env.globals.update(resolve_resource_list_path=resolve_resource_list_path) # pyright: ignore [reportUnknownMemberType] templates.env.globals.update(get_localized_value=get_localized_value) # pyright: ignore [reportUnknownMemberType] +# env tests allows for usage in templates like: if value is test_name(other_value) templates.env.tests["permission"] = permission # pyright: ignore [reportUnknownMemberType] +templates.env.tests["equal_or_includes"] = equal_or_includes # pyright: ignore [reportUnknownMemberType] templates.env.add_extension("jinja2_base64_filters.Base64Filters") # pyright: ignore [reportUnknownMemberType] diff --git a/amt/api/editable.py b/amt/api/editable.py index af08af2c..396136d6 100644 --- a/amt/api/editable.py +++ b/amt/api/editable.py @@ -13,6 +13,7 @@ replace_wildcard_with_digits_in_brackets, ) from amt.api.editable_validators import EditableValidatorMinMaxLength, EditableValidatorSlug +from amt.api.editable_value_providers import AIActValuesProvider from amt.api.lifecycles import get_localized_lifecycles from amt.api.routes.shared import nested_value from amt.api.utils import SafeDict @@ -270,6 +271,48 @@ class Editables: validator=EditableValidatorSlug(), ) + ALGORITHM_EDITABLE_AIACT = Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile", + implementation_type=WebFormFieldImplementationType.PARENT, + children=[ + Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile/role", + implementation_type=WebFormFieldImplementationType.MULTIPLE_CHECKBOX_AI_ACT, + values_provider=AIActValuesProvider(type="role"), + ), + Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile/type", + implementation_type=WebFormFieldImplementationType.SELECT_AI_ACT, + values_provider=AIActValuesProvider(type="type"), + ), + Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile/open_source", + implementation_type=WebFormFieldImplementationType.SELECT_AI_ACT, + values_provider=AIActValuesProvider(type="open_source"), + ), + Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile/risk_group", + implementation_type=WebFormFieldImplementationType.SELECT_AI_ACT, + values_provider=AIActValuesProvider(type="risk_group"), + ), + Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile/conformity_assessment_body", + implementation_type=WebFormFieldImplementationType.SELECT_AI_ACT, + values_provider=AIActValuesProvider(type="conformity_assessment_body"), + ), + Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile/systemic_risk", + implementation_type=WebFormFieldImplementationType.SELECT_AI_ACT, + values_provider=AIActValuesProvider(type="systemic_risk"), + ), + Editable( + full_resource_path="algorithm/{algorithm_id}/system_card/ai_act_profile/transparency_obligations", + implementation_type=WebFormFieldImplementationType.SELECT_AI_ACT, + values_provider=AIActValuesProvider(type="transparency_obligations"), + ), + ], + ) + # TODO: rethink if this is a wise solution.. we do this to keep all elements in 1 class and still # be able to execute other code (like making relationships) def __iter__(self) -> typing.Generator[tuple[str, Any], Any, Any]: @@ -375,8 +418,13 @@ async def enrich_editable( # noqa: C901 ) # TODO: can we move this to the editable object instead of here? + # TODO: consider if values_providers could solve & replace the specific conditions below if edit_mode == EditModes.EDIT: - if editable.implementation_type == WebFormFieldImplementationType.SELECT_MY_ORGANIZATIONS: + if editable.values_provider: + if request is None: + raise ValueError("Request is required when resolving a 'editable values provider'") + editable.form_options = await editable.values_provider.get_values(request) + elif editable.implementation_type == WebFormFieldImplementationType.SELECT_MY_ORGANIZATIONS: if organizations_service is None: raise ValueError("Organization service is required when resolving an organization") my_organizations = await organizations_service.get_organizations_for_user(user_id=user_id) @@ -427,6 +475,7 @@ def resolve_editable_path( full_resource_path=full_resource_path, relative_resource_path=relative_resource_path, implementation_type=editable.implementation_type, + values_provider=editable.values_provider, couples=couples, children=children, converter=editable.converter, @@ -556,8 +605,6 @@ def is_parent_editable(editables: dict[str, ResolvedEditable], full_resource_pat full_resource_path = replace_digits_in_brackets(full_resource_path) editable = editables.get(full_resource_path) if editable is None: - print(full_resource_path + " : " + "false, no match") return False result = editable.implementation_type == WebFormFieldImplementationType.PARENT - print(full_resource_path + " : " + str(result)) return result diff --git a/amt/api/editable_classes.py b/amt/api/editable_classes.py index 65789fcc..f76ebef7 100644 --- a/amt/api/editable_classes.py +++ b/amt/api/editable_classes.py @@ -7,6 +7,7 @@ ) from amt.api.editable_enforcers import EditableEnforcer from amt.api.editable_validators import EditableValidator +from amt.api.editable_value_providers import EditableValuesProvider from amt.models.base import Base from amt.schema.webform import WebFormFieldImplementationTypeFields, WebFormOption @@ -35,6 +36,7 @@ def __init__( self, full_resource_path: str, implementation_type: WebFormFieldImplementationTypeFields, + values_provider: EditableValuesProvider | None = None, couples: set[EditableType] | None = None, children: list[EditableType] | None = None, converter: EditableConverter | None = None, @@ -45,6 +47,7 @@ def __init__( ) -> None: self.full_resource_path = full_resource_path self.implementation_type = implementation_type + self.values_provider = values_provider self.couples = set[EditableType]() if couples is None else couples self.children = list[EditableType]() if children is None else children self.converter = converter @@ -84,6 +87,7 @@ def __init__( # fields copied from the Editable class full_resource_path: str, implementation_type: WebFormFieldImplementationTypeFields, + values_provider: EditableValuesProvider | None = None, couples: set[ResolvedEditableType] | None = None, children: list[ResolvedEditableType] | None = None, converter: EditableConverter | None = None, @@ -96,6 +100,7 @@ def __init__( ) -> None: self.full_resource_path = full_resource_path self.implementation_type = implementation_type + self.values_provider = values_provider self.couples = set[ResolvedEditableType]() if couples is None else couples self.children = list[ResolvedEditableType]() if children is None else children self.converter = converter diff --git a/amt/api/editable_value_providers.py b/amt/api/editable_value_providers.py new file mode 100644 index 00000000..b7f1f19b --- /dev/null +++ b/amt/api/editable_value_providers.py @@ -0,0 +1,31 @@ +from abc import ABC, abstractmethod + +from starlette.requests import Request + +from amt.api.ai_act_profile import SelectAiProfileItem, get_ai_act_profile_selector +from amt.schema.webform import WebFormOption + + +class EditableValuesProvider(ABC): + @abstractmethod + async def get_values(self, request: Request) -> list[WebFormOption]: + pass + + +class AIActValuesProvider(EditableValuesProvider): + def __init__(self, type: str) -> None: + self.type = type + + async def get_values(self, request: Request) -> list[WebFormOption]: + profile = get_ai_act_profile_selector(request) + target_ai_act_profile: SelectAiProfileItem | None = next( + ( + ai_act_profile + for ai_act_profile in (profile.dropdown_select or []) + (profile.multiple_select or []) + if ai_act_profile.target_name == self.type + ), + None, + ) + if target_ai_act_profile is not None and target_ai_act_profile.options is not None: + return [WebFormOption(value=option, display_value=option) for option in target_ai_act_profile.options] + return [] diff --git a/amt/locale/base.pot b/amt/locale/base.pot index c594a191..30067beb 100644 --- a/amt/locale/base.pot +++ b/amt/locale/base.pot @@ -299,7 +299,7 @@ msgstr "" #: amt/api/forms/organization.py:40 #: amt/site/templates/organizations/parts/add_members_modal.html.j2:4 -#: amt/site/templates/organizations/parts/add_members_modal.html.j2:23 +#: amt/site/templates/organizations/parts/add_members_modal.html.j2:26 msgid "Add members" msgstr "" @@ -380,43 +380,43 @@ msgstr "" msgid "Something went wrong storing your file. PLease try again later." msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:12 +#: amt/site/templates/algorithms/details_base.html.j2:13 msgid "Delete algoritmic system" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:19 +#: amt/site/templates/algorithms/details_base.html.j2:17 msgid "Are you sure you want to delete your algoritmic system " msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:22 +#: amt/site/templates/algorithms/details_base.html.j2:20 msgid "Data will be stored for at least 45 days before permanent deletion." msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:28 +#: amt/site/templates/algorithms/details_base.html.j2:26 #: amt/site/templates/algorithms/new.html.j2:153 -#: amt/site/templates/macros/form_macros.html.j2:170 -#: amt/site/templates/organizations/members.html.j2:33 +#: amt/site/templates/macros/form_macros.html.j2:168 +#: amt/site/templates/organizations/members.html.j2:34 msgid "Yes" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:31 +#: amt/site/templates/algorithms/details_base.html.j2:29 #: amt/site/templates/algorithms/new.html.j2:163 -#: amt/site/templates/macros/form_macros.html.j2:175 -#: amt/site/templates/organizations/members.html.j2:36 +#: amt/site/templates/macros/form_macros.html.j2:173 +#: amt/site/templates/organizations/members.html.j2:37 msgid "No" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:50 +#: amt/site/templates/algorithms/details_base.html.j2:49 msgid "Download as YAML" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:36 +#: amt/site/templates/algorithms/details_compliance.html.j2:29 msgid "measure executed" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:69 -#: amt/site/templates/macros/editable.html.j2:68 -#: amt/site/templates/macros/editable.html.j2:73 +#: amt/site/templates/algorithms/details_compliance.html.j2:62 +#: amt/site/templates/macros/editable.html.j2:77 +#: amt/site/templates/macros/editable.html.j2:82 #: amt/site/templates/macros/tasks.html.j2:98 msgid "Edit" msgstr "" @@ -430,7 +430,7 @@ msgid "Go to all requirements" msgstr "" #: amt/site/templates/algorithms/details_info.html.j2:85 -#: amt/site/templates/algorithms/details_measure_modal.html.j2:27 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:25 msgid "Description" msgstr "" @@ -460,18 +460,18 @@ msgstr "" msgid "To be implemented" msgstr "" -#: amt/site/templates/algorithms/details_measure_modal.html.j2:37 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:35 msgid "Read more on the algoritmekader" msgstr "" -#: amt/site/templates/algorithms/details_measure_modal.html.j2:64 -#: amt/site/templates/macros/editable.html.j2:175 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:62 +#: amt/site/templates/macros/editable.html.j2:205 msgid "Save" msgstr "" -#: amt/site/templates/algorithms/details_measure_modal.html.j2:68 -#: amt/site/templates/macros/editable.html.j2:180 -#: amt/site/templates/organizations/parts/add_members_modal.html.j2:26 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:66 +#: amt/site/templates/macros/editable.html.j2:210 +#: amt/site/templates/organizations/parts/add_members_modal.html.j2:29 msgid "Cancel" msgstr "" @@ -514,6 +514,7 @@ msgid "" msgstr "" #: amt/site/templates/algorithms/new.html.j2:76 +#: amt/site/templates/macros/editable.html.j2:9 msgid "Find your AI Act profile" msgstr "" @@ -526,10 +527,6 @@ msgstr "" msgid "Add algorithm" msgstr "" -#: amt/site/templates/algorithms/new.html.j2:193 -msgid "Copy results and close" -msgstr "" - #: amt/site/templates/auth/profile.html.j2:10 msgid "of" msgstr "" @@ -642,11 +639,11 @@ msgstr "" msgid "Delete member" msgstr "" -#: amt/site/templates/macros/form_macros.html.j2:152 +#: amt/site/templates/macros/form_macros.html.j2:153 msgid "Delete file" msgstr "" -#: amt/site/templates/macros/form_macros.html.j2:159 +#: amt/site/templates/macros/form_macros.html.j2:157 msgid "Are you sure you want to delete" msgstr "" @@ -670,7 +667,7 @@ msgstr "" msgid "Modified at" msgstr "" -#: amt/site/templates/organizations/members.html.j2:24 +#: amt/site/templates/organizations/members.html.j2:25 #: amt/site/templates/organizations/parts/add_members_modal.html.j2:8 msgid "Close" msgstr "" @@ -822,11 +819,11 @@ msgstr "" msgid "This page is yet to be build." msgstr "" -#: amt/site/templates/parts/algorithm_search.html.j2:62 +#: amt/site/templates/parts/algorithm_search.html.j2:64 msgid "Category" msgstr "" -#: amt/site/templates/parts/algorithm_search.html.j2:83 +#: amt/site/templates/parts/algorithm_search.html.j2:85 msgid "Group by" msgstr "" diff --git a/amt/locale/en_US/LC_MESSAGES/messages.mo b/amt/locale/en_US/LC_MESSAGES/messages.mo index 057963849e1fa264d79bcd56727d55ff58b4f180..bc5b1686afab88ba097fdee876bf7f3b632d15fc 100644 GIT binary patch delta 21 ccmZqVZsgvujfum^T*1K7%EWl{KBmJ=07Op)Q~&?~ delta 21 ccmZqVZsgvujfum+Ou^8?%EWy0KBmJ=07OCtRR910 diff --git a/amt/locale/en_US/LC_MESSAGES/messages.po b/amt/locale/en_US/LC_MESSAGES/messages.po index 2355b7d0..3cf9da0f 100644 --- a/amt/locale/en_US/LC_MESSAGES/messages.po +++ b/amt/locale/en_US/LC_MESSAGES/messages.po @@ -300,7 +300,7 @@ msgstr "" #: amt/api/forms/organization.py:40 #: amt/site/templates/organizations/parts/add_members_modal.html.j2:4 -#: amt/site/templates/organizations/parts/add_members_modal.html.j2:23 +#: amt/site/templates/organizations/parts/add_members_modal.html.j2:26 msgid "Add members" msgstr "" @@ -381,43 +381,43 @@ msgstr "" msgid "Something went wrong storing your file. PLease try again later." msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:12 +#: amt/site/templates/algorithms/details_base.html.j2:13 msgid "Delete algoritmic system" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:19 +#: amt/site/templates/algorithms/details_base.html.j2:17 msgid "Are you sure you want to delete your algoritmic system " msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:22 +#: amt/site/templates/algorithms/details_base.html.j2:20 msgid "Data will be stored for at least 45 days before permanent deletion." msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:28 +#: amt/site/templates/algorithms/details_base.html.j2:26 #: amt/site/templates/algorithms/new.html.j2:153 -#: amt/site/templates/macros/form_macros.html.j2:170 -#: amt/site/templates/organizations/members.html.j2:33 +#: amt/site/templates/macros/form_macros.html.j2:168 +#: amt/site/templates/organizations/members.html.j2:34 msgid "Yes" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:31 +#: amt/site/templates/algorithms/details_base.html.j2:29 #: amt/site/templates/algorithms/new.html.j2:163 -#: amt/site/templates/macros/form_macros.html.j2:175 -#: amt/site/templates/organizations/members.html.j2:36 +#: amt/site/templates/macros/form_macros.html.j2:173 +#: amt/site/templates/organizations/members.html.j2:37 msgid "No" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:50 +#: amt/site/templates/algorithms/details_base.html.j2:49 msgid "Download as YAML" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:36 +#: amt/site/templates/algorithms/details_compliance.html.j2:29 msgid "measure executed" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:69 -#: amt/site/templates/macros/editable.html.j2:68 -#: amt/site/templates/macros/editable.html.j2:73 +#: amt/site/templates/algorithms/details_compliance.html.j2:62 +#: amt/site/templates/macros/editable.html.j2:77 +#: amt/site/templates/macros/editable.html.j2:82 #: amt/site/templates/macros/tasks.html.j2:98 msgid "Edit" msgstr "" @@ -431,7 +431,7 @@ msgid "Go to all requirements" msgstr "" #: amt/site/templates/algorithms/details_info.html.j2:85 -#: amt/site/templates/algorithms/details_measure_modal.html.j2:27 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:25 msgid "Description" msgstr "" @@ -461,18 +461,18 @@ msgstr "" msgid "To be implemented" msgstr "" -#: amt/site/templates/algorithms/details_measure_modal.html.j2:37 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:35 msgid "Read more on the algoritmekader" msgstr "" -#: amt/site/templates/algorithms/details_measure_modal.html.j2:64 -#: amt/site/templates/macros/editable.html.j2:175 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:62 +#: amt/site/templates/macros/editable.html.j2:205 msgid "Save" msgstr "" -#: amt/site/templates/algorithms/details_measure_modal.html.j2:68 -#: amt/site/templates/macros/editable.html.j2:180 -#: amt/site/templates/organizations/parts/add_members_modal.html.j2:26 +#: amt/site/templates/algorithms/details_measure_modal.html.j2:66 +#: amt/site/templates/macros/editable.html.j2:210 +#: amt/site/templates/organizations/parts/add_members_modal.html.j2:29 msgid "Cancel" msgstr "" @@ -515,6 +515,7 @@ msgid "" msgstr "" #: amt/site/templates/algorithms/new.html.j2:76 +#: amt/site/templates/macros/editable.html.j2:9 msgid "Find your AI Act profile" msgstr "" @@ -527,10 +528,6 @@ msgstr "" msgid "Add algorithm" msgstr "" -#: amt/site/templates/algorithms/new.html.j2:193 -msgid "Copy results and close" -msgstr "" - #: amt/site/templates/auth/profile.html.j2:10 msgid "of" msgstr "" @@ -643,11 +640,11 @@ msgstr "" msgid "Delete member" msgstr "" -#: amt/site/templates/macros/form_macros.html.j2:152 +#: amt/site/templates/macros/form_macros.html.j2:153 msgid "Delete file" msgstr "" -#: amt/site/templates/macros/form_macros.html.j2:159 +#: amt/site/templates/macros/form_macros.html.j2:157 msgid "Are you sure you want to delete" msgstr "" @@ -671,7 +668,7 @@ msgstr "" msgid "Modified at" msgstr "" -#: amt/site/templates/organizations/members.html.j2:24 +#: amt/site/templates/organizations/members.html.j2:25 #: amt/site/templates/organizations/parts/add_members_modal.html.j2:8 msgid "Close" msgstr "" @@ -823,11 +820,11 @@ msgstr "" msgid "This page is yet to be build." msgstr "" -#: amt/site/templates/parts/algorithm_search.html.j2:62 +#: amt/site/templates/parts/algorithm_search.html.j2:64 msgid "Category" msgstr "" -#: amt/site/templates/parts/algorithm_search.html.j2:83 +#: amt/site/templates/parts/algorithm_search.html.j2:85 msgid "Group by" msgstr "" diff --git a/amt/locale/nl_NL/LC_MESSAGES/messages.mo b/amt/locale/nl_NL/LC_MESSAGES/messages.mo index 587320ec853769604de88a23a3d603da62c8ff46..9cf970833dfd7a9cc25e7b1592536cf78c6f668f 100644 GIT binary patch delta 3033 zcmYM$4NR3)9LMn^@+OxT<@!<@-iux!!x!*^h{agf)C9~dE0XDq&~S;_Y?zfk1;Q|+ECL?X~i0vnVV+xYDVRzO>3Hce?Di6!Tp?b?s@s2|2fYyPFKBN z<$RkQ*<|=Rz~3GGosClM|DRTmF*Q`%@KGESZ%ibv!P{{iF2Jq06fa{UE=VwDEG|Qa zn#~xE+b{;(kzdEW$LC-g_TwNtj>EAR0GScaw8h>ElyeRvu3(97z6EJm8la@52xVJz08Ht-gb z1hWH^aW87VPf`84QAzH_IMz3(DX8Q3sGa|%FEF0jiYpB2?hPwU6<3#G& z=*PvV{;!~pY%}U|?m=C`Ym+82Q&(4W&T~rJ_1yqINdf)~BLUS7_U3 zp)xQBwQ#Am0=4i;RK{vi8*4#+O*3Z zREld+3$&sFdV=yX^6l=D12C{kcIBHzAbrUM%&11>G zcC^oSJY+kZMYaE7+i##UG&0~$l!hF$$wLR{;UetCT+B*yU5dJ-tvCsHVF8{(1?){{ zv?9w$cW-AtYT?GoTUG?^+?2AVL4Z=){n z35-VPCkl$RAER)rj_#Ju0=esK^^o{ol3iVSJhT0o3!H zKgpOGI2Sdp2_L{_d<1_*9a&+P`~SksEY5#24XbI;4&O)Zpc8jt7i!`sv)y<08cd_U z75CynREDaUWG-$&_5T@%;C0lyK9Q@hqnv=+SO}A`C`kU3C@iEwm!t|6VKw@2E$WQh zFdsWm6a0x0cohTqk8Styh-khHR0i^JHr|6?+<$7>3z!yMERJb_wZ u3F=*5i8{*~Y-~@>4flD{qQWUd;*(-0=lQepr{x60+5S_};q;6Tll}wsb1SF- delta 3103 zcmYM$3rv<(9LMpa+(ALPXqv&30_ByfU@$d<$V=feOuUTp0%dYBK+DWtXO4!KmLrmM z(kdO87!pL~rc*4+SuSUWYl5SpES6b~HB-vIKc2J2Uih4Io^$!1|2faAZxwYEdAdTq zs|`P2@H>v*9v{{I|7i#^W;@lR_%tRCGsYV$aSXnR%dj2`&}+Cc!T17Bz)i?da{ztu zF#6#!g_n6_06;pVzf$H}+>Ik%`M;-ksJci*&R?G@iM%JMgtj5W>4~fyV+x8ArfLBmE>cx1xftn|h zb5cN4QRA~v8!N>bSQ$qCm6A3ZlJFdA!Mmslyu;m{grQO!g$isUs^3#M4Rg`KH&7d@ zM;%=Y>XQC|x{SY}0t$_AGch@W{40P&8gypqsDq zvF=1Iycd0bUgTLUp{2LotXvZNXq9n`Rg4(!GZ|(?)dg1ah3F z3$=mYZT%lq20})={o_y@orwxG3nS69h=LY=(RSE|8n_d+vm@44)B>kb6P&mHgqrv| zYKJ$i{Wy5pkXQz{tH4LIZEYk5*XjMAPJ!b#?_)B4ikheo7vN2tjp^i56K+P`m2LO{ zwxR;+LLF5fjvqoMPz#6M>t<>aj;Fo=Yp@t2_5S-Tjyi0| z>$ZbewA&tmnjjjLp*g6D9!HMZ*&#yq@*({V|R>u%H~J%e%hEzZP&7)B}Lv{-k- zY}D<178THX)R}ouDXc_gqT04MSX)sUJcSDM8+7m*l5OKlTKVoVv8cBo9ktFp4+U=u zOVPnRT!m`a!at(|{1vrh^Pp=eD&^6rehKJ@>8L<6a0xC%^{YdsFfHhhS1<`Z*C^-~ zkD2JcUQQ1fg@?R*C+z#0tH zsU4(145ks4iOZ;goya{i;gjvk`5u+AOQ;O=+UNbK z4BWv0-5T#HZiKsZ%*x-P#-qTHtw{h8t1u^&wOst+w8V%G5<1h65OY0Sr>0VaUZck*I(&QGw*3 zF5xoNe6OJTSD;4|?6D8_qXyRDaQqAv`B$jRbr!Yb3#d!^BdUL&ZNH6MsozDt)?4F@ zc@*D4jk|)`*o})ZKA!yR%qrsD|2XW%MCvC|JNyH+gMaZ5dMCIOe}wwi>lDV|Rs0xz z7_1Dn;6m&~1u~VZJ`yuf;})ZivOJ0WYiE@-gyJ3y!Gowv(t--`1S*AJqRzMnAHo6D z_!M4+p*RbpaSp1z05xARDg&?KJbWF)@Vg8OyM~Yw3|2?3{wUmn>hLz|2 str | None: - if v is None: - return None - - if isinstance(v, str): - return v - - if len(v) <= 2: - if len(v) == 0: - return None - elif len(v) == 1: - return v[0] - else: - return f"{v[0]} + {v[1]}" - else: - raise ValueError("There can only be two roles") diff --git a/amt/schema/webform.py b/amt/schema/webform.py index fa9f73cc..b391bb2e 100644 --- a/amt/schema/webform.py +++ b/amt/schema/webform.py @@ -4,6 +4,7 @@ class WebFormFieldType(Enum): + CHECKBOX_MULTIPLE = "checkbox_multiple" HIDDEN = "hidden" TEXT = "text" TEXT_CLONEABLE = "text_cloneable" @@ -24,11 +25,13 @@ class WebFormFieldImplementationTypeFields: class WebFormFieldImplementationType: + MULTIPLE_CHECKBOX_AI_ACT = WebFormFieldImplementationTypeFields("checkbox", WebFormFieldType.CHECKBOX_MULTIPLE) TEXT = WebFormFieldImplementationTypeFields("text", WebFormFieldType.TEXT) PARENT = WebFormFieldImplementationTypeFields("parent", None) TEXTAREA = WebFormFieldImplementationTypeFields("textarea", WebFormFieldType.TEXTAREA) SELECT_MY_ORGANIZATIONS = WebFormFieldImplementationTypeFields("select_my_organizations", WebFormFieldType.SELECT) SELECT_LIFECYCLE = WebFormFieldImplementationTypeFields("select_lifecycle", WebFormFieldType.SELECT) + SELECT_AI_ACT = WebFormFieldImplementationTypeFields("select", WebFormFieldType.SELECT) DATE = WebFormFieldImplementationTypeFields("date", WebFormFieldType.DATE) diff --git a/amt/services/task_registry.py b/amt/services/task_registry.py index e629ac07..6077df5e 100644 --- a/amt/services/task_registry.py +++ b/amt/services/task_registry.py @@ -1,4 +1,5 @@ import logging +from typing import Any from amt.schema.measure import MeasureTask from amt.schema.requirement import Requirement, RequirementTask @@ -79,14 +80,12 @@ async def get_requirements_and_measures( return applicable_requirements, applicable_measures -def _parse_attribute_values(attr: str, ai_act_profile: AiActProfile) -> set[str]: +def _parse_attribute_values(attr: str, ai_act_profile: AiActProfile) -> set[str] | set[Any]: """ - Helper function needed in `is_requirement_applicable`, handling special case for 'role' - and 'publication_category'. + Helper function needed in `is_requirement_applicable`, handling special case for 'publication_category'. """ - if attr == "role": - return {s.strip() for s in getattr(ai_act_profile, attr, "").split("+")} - if attr == "risk_group": - return {getattr(ai_act_profile, "risk_group", "")} - - return {getattr(ai_act_profile, attr, "")} + value = getattr(ai_act_profile, attr, "") + if isinstance(value, str): + return {value} + else: + return set(value) diff --git a/amt/site/static/beslishulp.html b/amt/site/static/beslishulp.html new file mode 100644 index 00000000..f71eb60b --- /dev/null +++ b/amt/site/static/beslishulp.html @@ -0,0 +1,11 @@ + + +
+ + + diff --git a/amt/site/static/scss/layout.scss b/amt/site/static/scss/layout.scss index 8418f3b5..dbde0426 100644 --- a/amt/site/static/scss/layout.scss +++ b/amt/site/static/scss/layout.scss @@ -201,25 +201,49 @@ main { right: 0; } -.minbzk-modal > .modal-content { +.model-content-auto-size { + width: auto !important; + height: auto !important; +} + +.modal-content { overflow: auto; + position: relative; + width: 100%; + height: 100%; +} + +.modal-content-close { + position: absolute; + right: 30px; + top: 10px; + z-index: 1000; + cursor: pointer; + font-size: 40px; +} + +.modal-content-container { + overflow: hidden; + position: relative; /* Position visible dialog near the top of the window */ - margin-top: 10vh; + margin-top: 5vh; + transition: + height 0.3s ease, + width 0.3s ease; /* Sizing for visible dialog */ width: 80%; height: 80%; - max-width: var(--rvo-layout-max-width-lg); /* Display properties for visible dialog */ border: solid 1px #999; - border-radius: var(--rvo-border-radius-xl); + border-radius: 1em; /* Change to appropriate color from NLDS. */ - box-shadow: 0 0 var(--rvo-space-lg) 0 rgba(0 0 0 / 30%); - background-color: var(--rvo-color-wit); - padding: var(--rvo-space-lg); + box-shadow: 0 0 1em 0 rgba(0 0 0 / 30%); + background-color: #fff; + padding: 1em; /* Animate when opening */ animation-name: zoom-in; @@ -227,20 +251,6 @@ main { animation-timing-function: ease; } -.minbzk-modal.closing { - /* Animate when closing */ - animation-name: fade-out; - animation-duration: 150ms; - animation-timing-function: ease; -} - -.minbzk-modal.closing > .modal-content { - /* Animate when closing */ - animation-name: zoom-out; - animation-duration: 150ms; - animation-timing-function: ease; -} - .display-none { display: none !important; } diff --git a/amt/site/static/ts/amt.ts b/amt/site/static/ts/amt.ts index bdd34eaa..de60d950 100644 --- a/amt/site/static/ts/amt.ts +++ b/amt/site/static/ts/amt.ts @@ -172,6 +172,17 @@ export function openModal(id: string) { } } +export function openDynamicModal(modalId: string) { + if (modalId == "ai-act-support-tool") { + document.getElementById("dynamic-modal-content")!.innerHTML = ``; + } else { + console.error("Unkown dynamic modalId: " + modalId); + } + openModal("modal"); +} + export function openConfirmModal( title: string, content: string, @@ -212,7 +223,6 @@ class AiActProfile { } export function closeModal(id: string) { - // Do not show modal. const el: Element | null = document.getElementById(id); if (el != null) { el.classList.add("display-none"); @@ -523,4 +533,64 @@ export function resetSearchInput() { inputElement.value = ""; } +export function updateInlineEditorAIActProfile() { + const jsonObject = JSON.parse( + sessionStorage.getItem("labelsbysubcategory") as string, + ); + const groupToFieldName = new Map() + .set("Rol", "role") + .set("Soort toepassing", "type") + .set("Risicogroep", "risk_group") + .set("Conformiteitsbeoordelingsinstantie", "conformity_assessment_body") + .set("Systeemrisico", "systemic_risk") + .set("Transparantieverplichting", "transparency_obligations") + .set("Open source", "open_source"); + for (const [groupName, formFieldName] of groupToFieldName) { + const groupValues = jsonObject[groupName]; + const elements = document.querySelectorAll( + '[name="' + formFieldName + '"]', + ); + if (elements && elements.length > 1) { + elements.forEach((element) => { + if ( + element.getAttribute("type") === "radio" || + element.getAttribute("type") === "checkbox" + ) { + const labelValue = element.getAttribute("value"); + if (groupValues.includes(labelValue)) { + element.setAttribute("checked", "checked"); + } else { + element.removeAttribute("checked"); + } + } + }); + } else if (elements) { + elements[0].querySelectorAll("option").forEach((optionElement) => { + const labelValue = optionElement.getAttribute("value"); + if (groupValues.includes(labelValue)) { + optionElement.setAttribute("selected", "selected"); + } else { + optionElement.removeAttribute("selected"); + } + }); + } else { + console.error(`Element with name ${formFieldName} not found.`); + } + } +} + +window.addEventListener("message", (event) => { + if (event.data.event === "beslishulp-done") { + console.log("Received beslishulp-done:", event.data.value); + if (window.location.pathname == "/algorithms/new") { + closeModalSave("modal"); + } else if (/\/algorithm\/\d+\/details/.test(window.location.pathname)) { + updateInlineEditorAIActProfile(); + closeAndResetDynamicModal("modal"); + } else { + console.error("No handle found for location " + window.location.pathname); + } + } +}); + // for debugging htmx use -> htmx.logAll(); diff --git a/amt/site/templates/algorithms/details_base.html.j2 b/amt/site/templates/algorithms/details_base.html.j2 index 7f381f08..e353c40f 100644 --- a/amt/site/templates/algorithms/details_base.html.j2 +++ b/amt/site/templates/algorithms/details_base.html.j2 @@ -4,32 +4,31 @@ {% block content %}