diff --git a/amt/api/forms/measure.py b/amt/api/forms/measure.py index 8d9418f1..c4868627 100644 --- a/amt/api/forms/measure.py +++ b/amt/api/forms/measure.py @@ -1,10 +1,19 @@ from collections.abc import Sequence +from enum import StrEnum from gettext import NullTranslations from amt.models import User from amt.schema.webform import WebForm, WebFormField, WebFormFieldType, WebFormOption, WebFormTextCloneableField +class MeasureStatusOptions(StrEnum): + TODO = "to do" + IN_PROGRESS = "in progress" + IN_REVIEW = "in review" + DONE = "done" + NOT_IMPLEMENTED = "not implemented" + + async def get_measure_form( id: str, current_values: dict[str, str | list[str] | list[tuple[str, str]]], @@ -47,11 +56,11 @@ async def get_measure_form( name="measure_state", label=_("Status"), options=[ - WebFormOption(value="to do", display_value="to do"), - WebFormOption(value="in progress", display_value="in progress"), - WebFormOption(value="in review", display_value="in review"), - WebFormOption(value="done", display_value="done"), - WebFormOption(value="not implemented", display_value="not implemented"), + WebFormOption(value=MeasureStatusOptions.TODO, display_value="to do"), + WebFormOption(value=MeasureStatusOptions.IN_PROGRESS, display_value="in progress"), + WebFormOption(value=MeasureStatusOptions.IN_REVIEW, display_value="in review"), + WebFormOption(value=MeasureStatusOptions.DONE, display_value="done"), + WebFormOption(value=MeasureStatusOptions.NOT_IMPLEMENTED, display_value="not implemented"), ], default_value=current_values.get("measure_state"), group="1", diff --git a/amt/api/routes/algorithm.py b/amt/api/routes/algorithm.py index ee627d75..5d2d2c6e 100644 --- a/amt/api/routes/algorithm.py +++ b/amt/api/routes/algorithm.py @@ -1,13 +1,14 @@ import asyncio import datetime import logging +import urllib.parse from collections import defaultdict from collections.abc import Sequence from typing import Annotated, Any import yaml from fastapi import APIRouter, Depends, File, Form, Query, Request, Response, UploadFile -from fastapi.responses import FileResponse, HTMLResponse +from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse from ulid import ULID from amt.api.decorators import permission @@ -19,7 +20,7 @@ get_resolved_editables, save_editable, ) -from amt.api.forms.measure import get_measure_form +from amt.api.forms.measure import MeasureStatusOptions, get_measure_form from amt.api.navigation import ( BaseNavigationItem, Navigation, @@ -42,10 +43,10 @@ from amt.schema.task import MovedTask from amt.services.algorithms import AlgorithmsService from amt.services.instruments_and_requirements_state import InstrumentStateService, RequirementsStateService -from amt.services.measures import MeasuresService, create_measures_service +from amt.services.measures import measures_service from amt.services.object_storage import ObjectStorageService, create_object_storage_service from amt.services.organizations import OrganizationsService -from amt.services.requirements import RequirementsService, create_requirements_service +from amt.services.requirements import requirements_service from amt.services.tasks import TasksService router = APIRouter() @@ -63,7 +64,6 @@ async def get_instrument_state(system_card: SystemCard) -> dict[str, Any]: async def get_requirements_state(system_card: SystemCard) -> dict[str, Any]: - requirements_service = create_requirements_service() requirements = await requirements_service.fetch_requirements( [requirement.urn for requirement in system_card.requirements] ) @@ -411,8 +411,6 @@ async def get_system_card_requirements( organizations_repository: Annotated[OrganizationsRepository, Depends(OrganizationsRepository)], users_repository: Annotated[UsersRepository, Depends(UsersRepository)], algorithms_service: Annotated[AlgorithmsService, Depends(AlgorithmsService)], - requirements_service: Annotated[RequirementsService, Depends(create_requirements_service)], - measures_service: Annotated[MeasuresService, Depends(create_measures_service)], ) -> HTMLResponse: algorithm = await get_algorithm_or_error(algorithm_id, algorithms_service, request) instrument_state = await get_instrument_state(algorithm.system_card) @@ -497,7 +495,7 @@ async def get_measure_task_functions( measure_task_functions: dict[str, list[User]] = defaultdict(list) for measure_task in measure_tasks: - person_types = ["accountable_persons", "reviewer_persons", "responsible_persons"] + person_types = ["responsible_persons", "reviewer_persons", "accountable_persons"] for person_type in person_types: person_list = getattr(measure_task, person_type) if person_list: @@ -527,8 +525,6 @@ async def find_requirement_tasks_by_measure_urn(system_card: SystemCard, measure requirement_mapper[requirement_task.urn] = requirement_task requirement_tasks: list[RequirementTask] = [] - measures_service = create_measures_service() - requirements_service = create_requirements_service() measure = await measures_service.fetch_measures(measure_urn) for requirement_urn in measure[0].links: # TODO: This is because measure are linked to too many requirement not applicable in our use case @@ -561,9 +557,9 @@ async def get_measure( algorithm_id: int, measure_urn: str, algorithms_service: Annotated[AlgorithmsService, Depends(AlgorithmsService)], - measures_service: Annotated[MeasuresService, Depends(create_measures_service)], object_storage_service: Annotated[ObjectStorageService, Depends(create_object_storage_service)], search: str = Query(""), + requirement_urn: str = "", ) -> HTMLResponse: filters, _, _, sort_by = get_filters_and_sort_by(request) algorithm = await get_algorithm_or_error(algorithm_id, algorithms_service, request) @@ -598,7 +594,12 @@ async def get_measure( translations=get_current_translation(request), ) - context = {"measure": measure[0], "algorithm_id": algorithm_id, "form": measure_form} + context = { + "measure": measure[0], + "algorithm_id": algorithm_id, + "form": measure_form, + "requirement_urn": requirement_urn, + } return templates.TemplateResponse(request, "algorithms/details_measure_modal.html.j2", context) @@ -633,7 +634,6 @@ async def update_measure_value( organizations_repository: Annotated[OrganizationsRepository, Depends(OrganizationsRepository)], users_repository: Annotated[UsersRepository, Depends(UsersRepository)], algorithms_service: Annotated[AlgorithmsService, Depends(AlgorithmsService)], - requirements_service: Annotated[RequirementsService, Depends(create_requirements_service)], object_storage_service: Annotated[ObjectStorageService, Depends(create_object_storage_service)], measure_state: Annotated[str, Form()], measure_responsible: Annotated[str | None, Form()] = None, @@ -642,6 +642,7 @@ async def update_measure_value( measure_value: Annotated[str | None, Form()] = None, measure_links: Annotated[list[str] | None, Form()] = None, measure_files: Annotated[list[UploadFile] | None, File()] = None, + requirement_urn: str = "", ) -> HTMLResponse: filters, _, _, sort_by = get_filters_and_sort_by(request) algorithm = await get_algorithm_or_error(algorithm_id, algorithms_service, request) @@ -670,24 +671,51 @@ async def update_measure_value( requirement_urns = [requirement_task.urn for requirement_task in requirement_tasks] requirements = await requirements_service.fetch_requirements(requirement_urns) + state_order_list = set(MeasureStatusOptions) for requirement in requirements: - count_completed = 0 + state_count: dict[str, int] = {} for link_measure_urn in requirement.links: link_measure_task = find_measure_task(algorithm.system_card, link_measure_urn) - if link_measure_task: # noqa: SIM102 - if link_measure_task.state == "done": - count_completed += 1 + if link_measure_task: + state_count[link_measure_task.state] = state_count.get(link_measure_task.state, 0) + 1 requirement_task = find_requirement_task(algorithm.system_card, requirement.urn) - if count_completed == len(requirement.links): - requirement_task.state = "done" # pyright: ignore [reportOptionalMemberAccess] - elif count_completed == 0 and len(requirement.links) > 0: - requirement_task.state = "to do" # pyright: ignore [reportOptionalMemberAccess] - else: - requirement_task.state = "in progress" # pyright: ignore [reportOptionalMemberAccess] + full_match = False + for state in state_order_list: + # if all measures are in the same state, the requirement is set to that state + if requirement_task and state_count.get(state, 0) == len(requirement.links): + requirement_task.state = state + full_match = True + break + # a requirement is considered 'in progress' if any measure is of any state other than todo + if requirement_task and not full_match: + if len([key for key in state_count if key != MeasureStatusOptions.TODO]) > 0: + requirement_task.state = MeasureStatusOptions.IN_PROGRESS + else: + requirement_task.state = MeasureStatusOptions.TODO await algorithms_service.update(algorithm) - # TODO: FIX THIS!! The page now reloads at the top, which is annoying - return templates.Redirect(request, f"/algorithm/{algorithm_id}/details/system_card/compliance") + + # the redirect 'to same page' does not trigger a javascript reload, so we let us redirect by a different server URL + encoded_url = urllib.parse.quote_plus( + f"/algorithm/{algorithm_id}/details/system_card/compliance#{requirement_urn.replace(":","_")}" + ) + return templates.Redirect( + request, + f"/algorithm/{algorithm_id}/redirect?to={encoded_url}", + ) + + +@router.get("/{algorithm_id}/redirect") +@permission({AuthorizationResource.ALGORITHM: [AuthorizationVerb.READ]}) +async def redirect_to(request: Request, algorithm_id: str, to: str) -> RedirectResponse: + """ + Redirects to the requested URL. We only have and use this because HTMX and javascript redirects do + not work when redirecting to the same URL, even if query params are changed. + """ + return RedirectResponse( + status_code=302, + url=to, + ) @router.get("/{algorithm_id}/members") diff --git a/amt/api/routes/algorithms.py b/amt/api/routes/algorithms.py index e2d6e3de..22754d71 100644 --- a/amt/api/routes/algorithms.py +++ b/amt/api/routes/algorithms.py @@ -22,7 +22,6 @@ from amt.schema.algorithm import AlgorithmNew from amt.schema.webform import WebForm from amt.services.algorithms import AlgorithmsService, get_template_files -from amt.services.instruments import InstrumentsService, create_instrument_service from amt.services.organizations import OrganizationsService router = APIRouter() @@ -128,7 +127,6 @@ async def get_algorithms( @permission({AuthorizationResource.ALGORITHMS: [AuthorizationVerb.CREATE]}) async def get_new( request: Request, - instrument_service: Annotated[InstrumentsService, Depends(create_instrument_service)], organizations_service: Annotated[OrganizationsService, Depends(OrganizationsService)], organization_id: int = Query(None), ) -> HTMLResponse: @@ -151,10 +149,8 @@ async def get_new( template_files = get_template_files() - instruments = await instrument_service.fetch_instruments() - context: dict[str, Any] = { - "instruments": instruments, + "instruments": [], "ai_act_profile": ai_act_profile, "breadcrumbs": breadcrumbs, "sub_menu_items": {}, # sub_menu_items disabled for now, diff --git a/amt/api/routes/organizations.py b/amt/api/routes/organizations.py index 55418a26..3f8dbd34 100644 --- a/amt/api/routes/organizations.py +++ b/amt/api/routes/organizations.py @@ -112,6 +112,11 @@ async def root( search=search, sort=sort_by, filters=filters, user_id=user["sub"] if user else None ) + # we only can show organization you belong to, so the all organizations option is disabled + organization_filters = [ + f for f in get_localized_organization_filters(request) if f and f.value != OrganizationFilterOptions.ALL.value + ] + context: dict[str, Any] = { "breadcrumbs": breadcrumbs, "organizations": organizations, @@ -123,7 +128,7 @@ async def root( "organizations_length": len(organizations), "filters": localized_filters, "include_filters": False, - "organization_filters": get_localized_organization_filters(request), + "organization_filters": organization_filters, } if request.state.htmx: diff --git a/amt/cli/check_state.py b/amt/cli/check_state.py index 1415f510..3c7fb889 100644 --- a/amt/cli/check_state.py +++ b/amt/cli/check_state.py @@ -8,7 +8,7 @@ from amt.schema.instrument import Instrument from amt.schema.system_card import SystemCard -from amt.services.instruments import create_instrument_service +from amt.services.instruments import instruments_service from amt.services.instruments_and_requirements_state import all_lifecycles, get_all_next_tasks from amt.services.storage import StorageFactory @@ -30,7 +30,6 @@ def get_requested_instruments(all_instruments: list[Instrument], urns: list[str] def get_tasks_by_priority(urns: list[str], system_card_path: Path) -> None: try: system_card = get_system_card(system_card_path) - instruments_service = create_instrument_service() all_instruments = asyncio.run(instruments_service.fetch_instruments()) instruments = get_requested_instruments(all_instruments, urns) next_tasks = get_all_next_tasks(instruments, system_card) diff --git a/amt/locale/base.pot b/amt/locale/base.pot index bd333727..8ccc24ca 100644 --- a/amt/locale/base.pot +++ b/amt/locale/base.pot @@ -46,7 +46,7 @@ msgid "Role" msgstr "" #: amt/api/group_by_category.py:15 -#: amt/site/templates/algorithms/details_info.html.j2:96 +#: amt/site/templates/algorithms/details_info.html.j2:101 #: amt/site/templates/algorithms/new.html.j2:41 #: amt/site/templates/parts/algorithm_search.html.j2:48 #: amt/site/templates/parts/filter_list.html.j2:71 @@ -145,7 +145,7 @@ msgstr "" msgid "Model" msgstr "" -#: amt/api/navigation.py:62 amt/site/templates/algorithms/new.html.j2:170 +#: amt/api/navigation.py:62 msgid "Instruments" msgstr "" @@ -164,11 +164,7 @@ msgstr "" msgid "Compliance" msgstr "" -#: amt/api/organization_filter_options.py:17 -msgid "All organizations" -msgstr "" - -#: amt/api/organization_filter_options.py:17 +#: amt/api/organization_filter_options.py:16 msgid "My organizations" msgstr "" @@ -197,54 +193,54 @@ msgid "Select organization" msgstr "" #: amt/api/forms/algorithm.py:42 -#: amt/site/templates/algorithms/details_info.html.j2:74 +#: amt/site/templates/algorithms/details_info.html.j2:79 msgid "Organization" msgstr "" -#: amt/api/forms/measure.py:24 +#: amt/api/forms/measure.py:33 msgid "Responsible" msgstr "" -#: amt/api/forms/measure.py:32 +#: amt/api/forms/measure.py:41 msgid "Reviewer" msgstr "" -#: amt/api/forms/measure.py:40 +#: amt/api/forms/measure.py:49 msgid "Accountable" msgstr "" -#: amt/api/forms/measure.py:48 +#: amt/api/forms/measure.py:57 msgid "Status" msgstr "" -#: amt/api/forms/measure.py:63 +#: amt/api/forms/measure.py:72 msgid "Information on how this measure is implemented" msgstr "" -#: amt/api/forms/measure.py:70 +#: amt/api/forms/measure.py:79 msgid "" "Select one or more to upload. The files will be saved once you confirm " "changes by pressing the save button." msgstr "" -#: amt/api/forms/measure.py:75 +#: amt/api/forms/measure.py:84 msgid "Add files" msgstr "" -#: amt/api/forms/measure.py:76 +#: amt/api/forms/measure.py:85 msgid "No files selected." msgstr "" -#: amt/api/forms/measure.py:80 +#: amt/api/forms/measure.py:89 msgid "Add URI" msgstr "" -#: amt/api/forms/measure.py:83 +#: amt/api/forms/measure.py:92 msgid "Add links to documents" msgstr "" #: amt/api/forms/organization.py:23 -#: amt/site/templates/algorithms/details_info.html.j2:70 +#: amt/site/templates/algorithms/details_info.html.j2:75 #: amt/site/templates/auth/profile.html.j2:34 #: amt/site/templates/organizations/home.html.j2:13 #: amt/site/templates/organizations/parts/members_results.html.j2:118 @@ -381,11 +377,11 @@ msgstr "" msgid "Download as YAML" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:34 +#: amt/site/templates/algorithms/details_compliance.html.j2:36 msgid "measure executed" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:67 +#: amt/site/templates/algorithms/details_compliance.html.j2:69 #: amt/site/templates/macros/editable.html.j2:28 #: amt/site/templates/macros/editable.html.j2:33 msgid "Edit" @@ -396,37 +392,37 @@ msgid "Does the algorithm meet the requirements?" msgstr "" #: amt/site/templates/algorithms/details_info.html.j2:20 -#: amt/site/templates/algorithms/details_info.html.j2:46 +#: amt/site/templates/algorithms/details_info.html.j2:48 #: amt/site/templates/macros/tasks.html.j2:32 msgid "Done" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:42 +#: amt/site/templates/algorithms/details_info.html.j2:44 msgid "To do" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:44 +#: amt/site/templates/algorithms/details_info.html.j2:46 msgid "In progress" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:60 +#: amt/site/templates/algorithms/details_info.html.j2:65 msgid "Go to all requirements" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:80 +#: amt/site/templates/algorithms/details_info.html.j2:85 #: amt/site/templates/algorithms/details_measure_modal.html.j2:27 msgid "Description" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:86 +#: amt/site/templates/algorithms/details_info.html.j2:91 msgid "Repository" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:92 +#: amt/site/templates/algorithms/details_info.html.j2:97 msgid "Algorithm code" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:102 +#: amt/site/templates/algorithms/details_info.html.j2:107 #: amt/site/templates/organizations/parts/overview_results.html.j2:134 #: amt/site/templates/pages/assessment_card.html.j2:7 #: amt/site/templates/pages/model_card.html.j2:6 @@ -436,11 +432,11 @@ msgstr "" msgid "Last updated" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:106 +#: amt/site/templates/algorithms/details_info.html.j2:111 msgid "Labels" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:114 +#: amt/site/templates/algorithms/details_info.html.j2:119 msgid "References" msgstr "" @@ -509,21 +505,11 @@ msgstr "" msgid "Select Option" msgstr "" -#: amt/site/templates/algorithms/new.html.j2:172 -msgid "" -"Overview of instruments for the responsible development, deployment, " -"assessment, and monitoring of algorithms and AI-systems." -msgstr "" - -#: amt/site/templates/algorithms/new.html.j2:180 -msgid "Choose one or more instruments" -msgstr "" - -#: amt/site/templates/algorithms/new.html.j2:204 +#: amt/site/templates/algorithms/new.html.j2:176 msgid "Create Algorithm" msgstr "" -#: amt/site/templates/algorithms/new.html.j2:221 +#: amt/site/templates/algorithms/new.html.j2:193 msgid "Copy results and close" msgstr "" diff --git a/amt/locale/en_US/LC_MESSAGES/messages.mo b/amt/locale/en_US/LC_MESSAGES/messages.mo index 738f59d7..22706da2 100644 Binary files a/amt/locale/en_US/LC_MESSAGES/messages.mo and b/amt/locale/en_US/LC_MESSAGES/messages.mo differ diff --git a/amt/locale/en_US/LC_MESSAGES/messages.po b/amt/locale/en_US/LC_MESSAGES/messages.po index e56c0c42..69f89757 100644 --- a/amt/locale/en_US/LC_MESSAGES/messages.po +++ b/amt/locale/en_US/LC_MESSAGES/messages.po @@ -47,7 +47,7 @@ msgid "Role" msgstr "" #: amt/api/group_by_category.py:15 -#: amt/site/templates/algorithms/details_info.html.j2:96 +#: amt/site/templates/algorithms/details_info.html.j2:101 #: amt/site/templates/algorithms/new.html.j2:41 #: amt/site/templates/parts/algorithm_search.html.j2:48 #: amt/site/templates/parts/filter_list.html.j2:71 @@ -146,7 +146,7 @@ msgstr "" msgid "Model" msgstr "" -#: amt/api/navigation.py:62 amt/site/templates/algorithms/new.html.j2:170 +#: amt/api/navigation.py:62 msgid "Instruments" msgstr "" @@ -165,11 +165,7 @@ msgstr "" msgid "Compliance" msgstr "" -#: amt/api/organization_filter_options.py:17 -msgid "All organizations" -msgstr "" - -#: amt/api/organization_filter_options.py:17 +#: amt/api/organization_filter_options.py:16 msgid "My organizations" msgstr "" @@ -198,54 +194,54 @@ msgid "Select organization" msgstr "" #: amt/api/forms/algorithm.py:42 -#: amt/site/templates/algorithms/details_info.html.j2:74 +#: amt/site/templates/algorithms/details_info.html.j2:79 msgid "Organization" msgstr "" -#: amt/api/forms/measure.py:24 +#: amt/api/forms/measure.py:33 msgid "Responsible" msgstr "" -#: amt/api/forms/measure.py:32 +#: amt/api/forms/measure.py:41 msgid "Reviewer" msgstr "" -#: amt/api/forms/measure.py:40 +#: amt/api/forms/measure.py:49 msgid "Accountable" msgstr "" -#: amt/api/forms/measure.py:48 +#: amt/api/forms/measure.py:57 msgid "Status" msgstr "" -#: amt/api/forms/measure.py:63 +#: amt/api/forms/measure.py:72 msgid "Information on how this measure is implemented" msgstr "" -#: amt/api/forms/measure.py:70 +#: amt/api/forms/measure.py:79 msgid "" "Select one or more to upload. The files will be saved once you confirm " "changes by pressing the save button." msgstr "" -#: amt/api/forms/measure.py:75 +#: amt/api/forms/measure.py:84 msgid "Add files" msgstr "" -#: amt/api/forms/measure.py:76 +#: amt/api/forms/measure.py:85 msgid "No files selected." msgstr "" -#: amt/api/forms/measure.py:80 +#: amt/api/forms/measure.py:89 msgid "Add URI" msgstr "" -#: amt/api/forms/measure.py:83 +#: amt/api/forms/measure.py:92 msgid "Add links to documents" msgstr "" #: amt/api/forms/organization.py:23 -#: amt/site/templates/algorithms/details_info.html.j2:70 +#: amt/site/templates/algorithms/details_info.html.j2:75 #: amt/site/templates/auth/profile.html.j2:34 #: amt/site/templates/organizations/home.html.j2:13 #: amt/site/templates/organizations/parts/members_results.html.j2:118 @@ -382,11 +378,11 @@ msgstr "" msgid "Download as YAML" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:34 +#: amt/site/templates/algorithms/details_compliance.html.j2:36 msgid "measure executed" msgstr "" -#: amt/site/templates/algorithms/details_compliance.html.j2:67 +#: amt/site/templates/algorithms/details_compliance.html.j2:69 #: amt/site/templates/macros/editable.html.j2:28 #: amt/site/templates/macros/editable.html.j2:33 msgid "Edit" @@ -397,37 +393,37 @@ msgid "Does the algorithm meet the requirements?" msgstr "" #: amt/site/templates/algorithms/details_info.html.j2:20 -#: amt/site/templates/algorithms/details_info.html.j2:46 +#: amt/site/templates/algorithms/details_info.html.j2:48 #: amt/site/templates/macros/tasks.html.j2:32 msgid "Done" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:42 +#: amt/site/templates/algorithms/details_info.html.j2:44 msgid "To do" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:44 +#: amt/site/templates/algorithms/details_info.html.j2:46 msgid "In progress" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:60 +#: amt/site/templates/algorithms/details_info.html.j2:65 msgid "Go to all requirements" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:80 +#: amt/site/templates/algorithms/details_info.html.j2:85 #: amt/site/templates/algorithms/details_measure_modal.html.j2:27 msgid "Description" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:86 +#: amt/site/templates/algorithms/details_info.html.j2:91 msgid "Repository" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:92 +#: amt/site/templates/algorithms/details_info.html.j2:97 msgid "Algorithm code" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:102 +#: amt/site/templates/algorithms/details_info.html.j2:107 #: amt/site/templates/organizations/parts/overview_results.html.j2:134 #: amt/site/templates/pages/assessment_card.html.j2:7 #: amt/site/templates/pages/model_card.html.j2:6 @@ -437,11 +433,11 @@ msgstr "" msgid "Last updated" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:106 +#: amt/site/templates/algorithms/details_info.html.j2:111 msgid "Labels" msgstr "" -#: amt/site/templates/algorithms/details_info.html.j2:114 +#: amt/site/templates/algorithms/details_info.html.j2:119 msgid "References" msgstr "" @@ -510,21 +506,11 @@ msgstr "" msgid "Select Option" msgstr "" -#: amt/site/templates/algorithms/new.html.j2:172 -msgid "" -"Overview of instruments for the responsible development, deployment, " -"assessment, and monitoring of algorithms and AI-systems." -msgstr "" - -#: amt/site/templates/algorithms/new.html.j2:180 -msgid "Choose one or more instruments" -msgstr "" - -#: amt/site/templates/algorithms/new.html.j2:204 +#: amt/site/templates/algorithms/new.html.j2:176 msgid "Create Algorithm" msgstr "" -#: amt/site/templates/algorithms/new.html.j2:221 +#: amt/site/templates/algorithms/new.html.j2:193 msgid "Copy results and close" msgstr "" diff --git a/amt/locale/nl_NL/LC_MESSAGES/messages.mo b/amt/locale/nl_NL/LC_MESSAGES/messages.mo index 38eee241..d7cd0f6b 100644 Binary files a/amt/locale/nl_NL/LC_MESSAGES/messages.mo and b/amt/locale/nl_NL/LC_MESSAGES/messages.mo differ diff --git a/amt/locale/nl_NL/LC_MESSAGES/messages.po b/amt/locale/nl_NL/LC_MESSAGES/messages.po index a7a6defa..b72544c1 100644 --- a/amt/locale/nl_NL/LC_MESSAGES/messages.po +++ b/amt/locale/nl_NL/LC_MESSAGES/messages.po @@ -49,7 +49,7 @@ msgid "Role" msgstr "Rol" #: amt/api/group_by_category.py:15 -#: amt/site/templates/algorithms/details_info.html.j2:96 +#: amt/site/templates/algorithms/details_info.html.j2:101 #: amt/site/templates/algorithms/new.html.j2:41 #: amt/site/templates/parts/algorithm_search.html.j2:48 #: amt/site/templates/parts/filter_list.html.j2:71 @@ -148,7 +148,7 @@ msgstr "Data" msgid "Model" msgstr "Model" -#: amt/api/navigation.py:62 amt/site/templates/algorithms/new.html.j2:170 +#: amt/api/navigation.py:62 msgid "Instruments" msgstr "Instrumenten" @@ -167,11 +167,7 @@ msgstr "Leden" msgid "Compliance" msgstr "Voldoen aan wetten en regels" -#: amt/api/organization_filter_options.py:17 -msgid "All organizations" -msgstr "Alle organisaties" - -#: amt/api/organization_filter_options.py:17 +#: amt/api/organization_filter_options.py:16 msgid "My organizations" msgstr "Mijn organisaties" @@ -200,31 +196,31 @@ msgid "Select organization" msgstr "Selecteer organisatie" #: amt/api/forms/algorithm.py:42 -#: amt/site/templates/algorithms/details_info.html.j2:74 +#: amt/site/templates/algorithms/details_info.html.j2:79 msgid "Organization" msgstr "Organisatie" -#: amt/api/forms/measure.py:24 +#: amt/api/forms/measure.py:33 msgid "Responsible" msgstr "Uitvoerende" -#: amt/api/forms/measure.py:32 +#: amt/api/forms/measure.py:41 msgid "Reviewer" msgstr "Controlerende" -#: amt/api/forms/measure.py:40 +#: amt/api/forms/measure.py:49 msgid "Accountable" msgstr "Verantwoordelijke" -#: amt/api/forms/measure.py:48 +#: amt/api/forms/measure.py:57 msgid "Status" msgstr "Status" -#: amt/api/forms/measure.py:63 +#: amt/api/forms/measure.py:72 msgid "Information on how this measure is implemented" msgstr "Informatie over hoe deze maatregel is geïmplementeerd" -#: amt/api/forms/measure.py:70 +#: amt/api/forms/measure.py:79 msgid "" "Select one or more to upload. The files will be saved once you confirm " "changes by pressing the save button." @@ -232,24 +228,24 @@ msgstr "" "Selecteer één of meer bestanden om te uploaden. De bestanden zullen " "worden opgeslagen wanneer de Opslaan knop wordt ingedrukt." -#: amt/api/forms/measure.py:75 +#: amt/api/forms/measure.py:84 msgid "Add files" msgstr "Bestanden toevoegen" -#: amt/api/forms/measure.py:76 +#: amt/api/forms/measure.py:85 msgid "No files selected." msgstr "Geen bestanden geselecteerd." -#: amt/api/forms/measure.py:80 +#: amt/api/forms/measure.py:89 msgid "Add URI" msgstr "Voeg URI toe" -#: amt/api/forms/measure.py:83 +#: amt/api/forms/measure.py:92 msgid "Add links to documents" msgstr "Voeg link naar bestanden toe" #: amt/api/forms/organization.py:23 -#: amt/site/templates/algorithms/details_info.html.j2:70 +#: amt/site/templates/algorithms/details_info.html.j2:75 #: amt/site/templates/auth/profile.html.j2:34 #: amt/site/templates/organizations/home.html.j2:13 #: amt/site/templates/organizations/parts/members_results.html.j2:118 @@ -401,11 +397,11 @@ msgstr "Nee" msgid "Download as YAML" msgstr "Download naar YAML" -#: amt/site/templates/algorithms/details_compliance.html.j2:34 +#: amt/site/templates/algorithms/details_compliance.html.j2:36 msgid "measure executed" msgstr "maatregelen uitgevoerd" -#: amt/site/templates/algorithms/details_compliance.html.j2:67 +#: amt/site/templates/algorithms/details_compliance.html.j2:69 #: amt/site/templates/macros/editable.html.j2:28 #: amt/site/templates/macros/editable.html.j2:33 msgid "Edit" @@ -416,37 +412,37 @@ msgid "Does the algorithm meet the requirements?" msgstr "Voldoet het algoritme aan de vereisten?" #: amt/site/templates/algorithms/details_info.html.j2:20 -#: amt/site/templates/algorithms/details_info.html.j2:46 +#: amt/site/templates/algorithms/details_info.html.j2:48 #: amt/site/templates/macros/tasks.html.j2:32 msgid "Done" msgstr "Afgerond" -#: amt/site/templates/algorithms/details_info.html.j2:42 +#: amt/site/templates/algorithms/details_info.html.j2:44 msgid "To do" msgstr "Te doen" -#: amt/site/templates/algorithms/details_info.html.j2:44 +#: amt/site/templates/algorithms/details_info.html.j2:46 msgid "In progress" msgstr "Onderhanden" -#: amt/site/templates/algorithms/details_info.html.j2:60 +#: amt/site/templates/algorithms/details_info.html.j2:65 msgid "Go to all requirements" msgstr "Ga naar alle Vereisten" -#: amt/site/templates/algorithms/details_info.html.j2:80 +#: amt/site/templates/algorithms/details_info.html.j2:85 #: amt/site/templates/algorithms/details_measure_modal.html.j2:27 msgid "Description" msgstr "Omschrijving" -#: amt/site/templates/algorithms/details_info.html.j2:86 +#: amt/site/templates/algorithms/details_info.html.j2:91 msgid "Repository" msgstr "Repository" -#: amt/site/templates/algorithms/details_info.html.j2:92 +#: amt/site/templates/algorithms/details_info.html.j2:97 msgid "Algorithm code" msgstr "Algoritme code" -#: amt/site/templates/algorithms/details_info.html.j2:102 +#: amt/site/templates/algorithms/details_info.html.j2:107 #: amt/site/templates/organizations/parts/overview_results.html.j2:134 #: amt/site/templates/pages/assessment_card.html.j2:7 #: amt/site/templates/pages/model_card.html.j2:6 @@ -456,11 +452,11 @@ msgstr "Algoritme code" msgid "Last updated" msgstr "Laatst bijgewerkt" -#: amt/site/templates/algorithms/details_info.html.j2:106 +#: amt/site/templates/algorithms/details_info.html.j2:111 msgid "Labels" msgstr "Labels" -#: amt/site/templates/algorithms/details_info.html.j2:114 +#: amt/site/templates/algorithms/details_info.html.j2:119 msgid "References" msgstr "Referenties" @@ -532,23 +528,11 @@ msgstr "Vind uw AI Act profiel" msgid "Select Option" msgstr "Selecteer optie" -#: amt/site/templates/algorithms/new.html.j2:172 -msgid "" -"Overview of instruments for the responsible development, deployment, " -"assessment, and monitoring of algorithms and AI-systems." -msgstr "" -"Overzicht van aanbevolen instrument voor het verantwoord ontwikkelen, " -"gebruiken, beoordelen en monitoren van algoritmes en AI-systemen." - -#: amt/site/templates/algorithms/new.html.j2:180 -msgid "Choose one or more instruments" -msgstr "Kies één of meerdere instrumenten" - -#: amt/site/templates/algorithms/new.html.j2:204 +#: amt/site/templates/algorithms/new.html.j2:176 msgid "Create Algorithm" msgstr "Creëer algoritme" -#: amt/site/templates/algorithms/new.html.j2:221 +#: amt/site/templates/algorithms/new.html.j2:193 msgid "Copy results and close" msgstr "Resultaten overnemen en sluiten" diff --git a/amt/repositories/task_registry.py b/amt/repositories/task_registry.py index a972a6a3..0ec2f73c 100644 --- a/amt/repositories/task_registry.py +++ b/amt/repositories/task_registry.py @@ -67,3 +67,6 @@ async def _fetch_tasks_by_urns(self, task_type: TaskType, urns: Sequence[str]) - logger.warning("Cannot find all tasks") return tasks + + +task_registry_repository = TaskRegistryRepository(client=TaskRegistryAPIClient()) diff --git a/amt/services/algorithms.py b/amt/services/algorithms.py index 7df306c8..a2010efc 100644 --- a/amt/services/algorithms.py +++ b/amt/services/algorithms.py @@ -18,7 +18,6 @@ from amt.schema.algorithm import AlgorithmNew from amt.schema.instrument import InstrumentBase from amt.schema.system_card import AiActProfile, Owner, SystemCard -from amt.services.instruments import InstrumentsService, create_instrument_service from amt.services.task_registry import get_requirements_and_measures logger = logging.getLogger(__name__) @@ -30,11 +29,9 @@ class AlgorithmsService: def __init__( self, repository: Annotated[AlgorithmsRepository, Depends(AlgorithmsRepository)], - instrument_service: Annotated[InstrumentsService, Depends(create_instrument_service)], organizations_repository: Annotated[OrganizationsRepository, Depends(OrganizationsRepository)], ) -> None: self.repository = repository - self.instrument_service = instrument_service self.organizations_repository = organizations_repository async def get(self, algorithm_id: int) -> Algorithm: diff --git a/amt/services/instruments.py b/amt/services/instruments.py index 4ee19ffc..b90f83b9 100644 --- a/amt/services/instruments.py +++ b/amt/services/instruments.py @@ -1,8 +1,8 @@ import logging from collections.abc import Sequence -from amt.clients.clients import TaskType, task_registry_api_client -from amt.repositories.task_registry import TaskRegistryRepository +from amt.clients.clients import TaskType +from amt.repositories.task_registry import TaskRegistryRepository, task_registry_repository from amt.schema.instrument import Instrument logger = logging.getLogger(__name__) @@ -23,6 +23,4 @@ async def fetch_instruments(self, urns: str | Sequence[str] | None = None) -> li return [Instrument(**data) for data in task_data] -def create_instrument_service() -> InstrumentsService: - repository = TaskRegistryRepository(task_registry_api_client) - return InstrumentsService(repository) +instruments_service = InstrumentsService(task_registry_repository) diff --git a/amt/services/instruments_and_requirements_state.py b/amt/services/instruments_and_requirements_state.py index 5d50c99c..e34161e1 100644 --- a/amt/services/instruments_and_requirements_state.py +++ b/amt/services/instruments_and_requirements_state.py @@ -8,7 +8,7 @@ from amt.schema.instrument import Instrument, InstrumentTask from amt.schema.requirement import Requirement from amt.schema.system_card import SystemCard -from amt.services.instruments import create_instrument_service +from amt.services.instruments import instruments_service logger = logging.getLogger(__name__) @@ -108,8 +108,9 @@ def get_requirements_state(self, requirements: list[Requirement]) -> list[dict[s saved_requirements = self.system_card.requirements for requirement in saved_requirements: - urn = requirement.urn - self.requirements_state.append({"name": requirements_mapping[urn], "state": requirement.state}) + self.requirements_state.append( + {"name": requirements_mapping[requirement.urn], "state": requirement.state, "urn": requirement.urn} + ) return self.requirements_state def get_amount_total_requirements(self) -> int: @@ -134,7 +135,6 @@ async def get_state_per_instrument(self) -> list[dict[str, int]]: # Otherwise the instrument is completed as there are not any tasks left. urns = [instrument.urn for instrument in self.system_card.instruments] - instruments_service = create_instrument_service() instruments = await instruments_service.fetch_instruments(urns) # TODO: refactor this data structure in 3 lines below (also change in get_all_next_tasks + check_state.py) instruments_dict = {} diff --git a/amt/services/measures.py b/amt/services/measures.py index a26c0c2f..0f73f3ba 100644 --- a/amt/services/measures.py +++ b/amt/services/measures.py @@ -1,8 +1,8 @@ import logging from collections.abc import Sequence -from amt.clients.clients import TaskType, task_registry_api_client -from amt.repositories.task_registry import TaskRegistryRepository +from amt.clients.clients import TaskType +from amt.repositories.task_registry import TaskRegistryRepository, task_registry_repository from amt.schema.measure import Measure logger = logging.getLogger(__name__) @@ -23,6 +23,4 @@ async def fetch_measures(self, urns: str | Sequence[str] | None = None) -> list[ return [Measure(**data) for data in task_data] -def create_measures_service() -> MeasuresService: - repository = TaskRegistryRepository(task_registry_api_client) - return MeasuresService(repository) +measures_service = MeasuresService(task_registry_repository) diff --git a/amt/services/requirements.py b/amt/services/requirements.py index 675aa426..7549bc85 100644 --- a/amt/services/requirements.py +++ b/amt/services/requirements.py @@ -1,8 +1,8 @@ import logging from collections.abc import Sequence -from amt.clients.clients import TaskType, task_registry_api_client -from amt.repositories.task_registry import TaskRegistryRepository +from amt.clients.clients import TaskType +from amt.repositories.task_registry import TaskRegistryRepository, task_registry_repository from amt.schema.requirement import Requirement logger = logging.getLogger(__name__) @@ -23,6 +23,4 @@ async def fetch_requirements(self, urns: str | Sequence[str] | None = None) -> l return [Requirement(**data) for data in task_data] -def create_requirements_service() -> RequirementsService: - repository = TaskRegistryRepository(task_registry_api_client) - return RequirementsService(repository) +requirements_service = RequirementsService(task_registry_repository) diff --git a/amt/services/task_registry.py b/amt/services/task_registry.py index 71e16ee0..102ba4be 100644 --- a/amt/services/task_registry.py +++ b/amt/services/task_registry.py @@ -3,8 +3,8 @@ from amt.schema.measure import MeasureTask from amt.schema.requirement import Requirement, RequirementTask from amt.schema.system_card import AiActProfile -from amt.services.measures import create_measures_service -from amt.services.requirements import create_requirements_service +from amt.services.measures import measures_service +from amt.services.requirements import requirements_service logger = logging.getLogger(__name__) @@ -53,8 +53,6 @@ def is_requirement_applicable(requirement: Requirement, ai_act_profile: AiActPro async def get_requirements_and_measures( ai_act_profile: AiActProfile, ) -> tuple[list[RequirementTask], list[MeasureTask]]: - requirements_service = create_requirements_service() - measure_service = create_measures_service() all_requirements = await requirements_service.fetch_requirements() applicable_requirements: list[RequirementTask] = [] @@ -67,7 +65,7 @@ async def get_requirements_and_measures( for measure_urn in requirement.links: if measure_urn not in measure_urns: - measure = await measure_service.fetch_measures(measure_urn) + measure = await measures_service.fetch_measures(measure_urn) applicable_measures.append( MeasureTask(urn=measure_urn, state="to do", version=measure[0].schema_version) ) diff --git a/amt/site/static/ts/amt.ts b/amt/site/static/ts/amt.ts index c6fa62be..3ed28b8b 100644 --- a/amt/site/static/ts/amt.ts +++ b/amt/site/static/ts/amt.ts @@ -43,7 +43,7 @@ document.addEventListener("DOMContentLoaded", function () { new Sortable(column, { //NOSONAR group: "shared", // set both lists to same group animation: 150, - onEnd: function(evt) { + onEnd: function (evt) { if (evt.oldIndex !== evt.newIndex || evt.from !== evt.to) { const previousSiblingId = evt.item.previousElementSibling ? evt.item.previousElementSibling.getAttribute("data-id") @@ -427,4 +427,32 @@ export function getFiles(element: HTMLInputElement, target_id: string) { } } +function getAnchor() { + const currentUrl = document.URL, + urlParts = currentUrl.split("#"); + return urlParts.length > 1 ? urlParts[1] : null; +} + +function scrollElementIntoViewClickAndBlur(id: string | null) { + if (!id) { + return; + } + const element = document.getElementById(id); + if (element) { + element.scrollIntoView({ + behavior: "smooth", + block: "start", + inline: "start", + }); + element.click(); + element.blur(); + } +} + +export function showRequirementDetails() { + document.addEventListener("DOMContentLoaded", function () { + scrollElementIntoViewClickAndBlur(getAnchor()); + }); +} + // for debugging htmx use -> htmx.logAll(); diff --git a/amt/site/templates/algorithms/details_compliance.html.j2 b/amt/site/templates/algorithms/details_compliance.html.j2 index 6c869028..0ff30eb2 100644 --- a/amt/site/templates/algorithms/details_compliance.html.j2 +++ b/amt/site/templates/algorithms/details_compliance.html.j2 @@ -11,7 +11,9 @@ {% for (requirement, completed_measures_count, measures) in requirements_and_measures %}
- +

{% trans %}Edit{% endtrans %} - {{ macros.user_avatars(measure_task_functions[measure.urn]) }} +
{{ macros.user_avatars(measure_task_functions[measure.urn]) }}
@@ -74,4 +76,5 @@

{% endfor %} + {% endblock %} diff --git a/amt/site/templates/algorithms/details_info.html.j2 b/amt/site/templates/algorithms/details_info.html.j2 index 4ba713b0..471092bb 100644 --- a/amt/site/templates/algorithms/details_info.html.j2 +++ b/amt/site/templates/algorithms/details_info.html.j2 @@ -35,16 +35,21 @@ {% else %}
{% endif %} - {{ requirement.name }} + {{ requirement.name }}
- {% if requirement.state == "to do" %} - {% trans %}To do{% endtrans %} - {% elif requirement.state == "in progress" %} - {% trans %}In progress{% endtrans %} - {% else %} - {% trans %}Done{% endtrans %} - {% endif %} + + {% if requirement.state == "to do" or requirement.state == "" %} + {% trans %}To do{% endtrans %} + {% elif requirement.state == "in progress" %} + {% trans %}In progress{% endtrans %} + {% elif requirement.state == "done" %} + {% trans %}Done{% endtrans %} + {% else %} + {{ requirement.state }} + {% endif %} +
diff --git a/amt/site/templates/algorithms/details_measure_modal.html.j2 b/amt/site/templates/algorithms/details_measure_modal.html.j2 index 9635f392..7eaf354f 100644 --- a/amt/site/templates/algorithms/details_measure_modal.html.j2 +++ b/amt/site/templates/algorithms/details_measure_modal.html.j2 @@ -8,7 +8,7 @@