diff --git a/amt/core/exception_handlers.py b/amt/core/exception_handlers.py index 1806eb23..e8eac482 100644 --- a/amt/core/exception_handlers.py +++ b/amt/core/exception_handlers.py @@ -1,5 +1,8 @@ import logging +from gettext import gettext as _ +from typing import Any +from babel.support import NullTranslations from fastapi import Request, status from fastapi.exceptions import RequestValidationError from fastapi.responses import HTMLResponse @@ -13,6 +16,21 @@ logger = logging.getLogger(__name__) +CUSTOM_MESSAGES = { + "string_too_short": _("String should have at least {min_length} characters"), + "missing": _("Field required"), +} + + +def translate_pydantic_exception(err: dict[str, Any], translations: NullTranslations) -> str: + message: str | None = CUSTOM_MESSAGES.get(err["type"], None) + + if message: + custom_message = translations.gettext(message) + return custom_message.format(**err["ctx"]) if "ctx" in err else custom_message + + return err["msg"] + async def general_exception_handler(request: Request, exc: Exception) -> HTMLResponse: exception_name = exc.__class__.__name__ @@ -27,8 +45,10 @@ async def general_exception_handler(request: Request, exc: Exception) -> HTMLRes elif isinstance(exc, StarletteHTTPException): message = AMTNotFound().getmessage(translations) if exc.status_code == status.HTTP_404_NOT_FOUND else exc.detail elif isinstance(exc, RequestValidationError): - messages: list[str] = [f"{error['loc'][-1]}: {error['msg']}" for error in exc.errors()] - message = "\n".join(messages) + # i assume only pydantic errors get into this section + message = exc.errors() + for err in message: + err["msg"] = translate_pydantic_exception(err, translations) status_code = status.HTTP_500_INTERNAL_SERVER_ERROR if isinstance(exc, StarletteHTTPException): @@ -42,6 +62,7 @@ async def general_exception_handler(request: Request, exc: Exception) -> HTMLRes if request.state.htmx else f"errors/{exception_name}_{status_code}.html.j2" ) + fallback_template_name = "errors/_Exception.html.j2" if request.state.htmx else "errors/Exception.html.j2" response: HTMLResponse | None = None diff --git a/amt/locale/base.pot b/amt/locale/base.pot index 72295496..96ee46b5 100644 --- a/amt/locale/base.pot +++ b/amt/locale/base.pot @@ -133,7 +133,7 @@ msgstr "" msgid "Model" msgstr "" -#: amt/api/navigation.py:59 amt/site/templates/projects/new.html.j2:145 +#: amt/api/navigation.py:59 amt/site/templates/projects/new.html.j2:147 msgid "Instruments" msgstr "" @@ -161,6 +161,14 @@ msgstr "" msgid "Exception of application" msgstr "" +#: amt/core/exception_handlers.py:20 +msgid "String should have at least {min_length} characters" +msgstr "" + +#: amt/core/exception_handlers.py:21 +msgid "Field required" +msgstr "" + #: amt/core/exceptions.py:20 msgid "" "An error occurred while configuring the options for '{field}'. Please " @@ -263,6 +271,14 @@ msgstr "" msgid "An error occurred. Please try again later" msgstr "" +#: amt/site/templates/errors/_RequestValidationError_400.html.j2:3 +msgid "There are some errors" +msgstr "" + +#: amt/site/templates/errors/_RequestValidationError_400.html.j2:5 +msgid "There is one error:" +msgstr "" + #: amt/site/templates/layouts/base.html.j2:11 msgid "Algorithmic Management Toolkit (AMT)" msgstr "" @@ -415,7 +431,7 @@ msgstr "" #: amt/site/templates/parts/filter_list.html.j2:99 #: amt/site/templates/projects/details_info.html.j2:24 -#: amt/site/templates/projects/new.html.j2:37 +#: amt/site/templates/projects/new.html.j2:38 msgid "Lifecycle" msgstr "" @@ -601,27 +617,27 @@ msgstr "" msgid "Algorithm System name" msgstr "" -#: amt/site/templates/projects/new.html.j2:26 +#: amt/site/templates/projects/new.html.j2:27 msgid "Name of the algorithm system" msgstr "" -#: amt/site/templates/projects/new.html.j2:39 +#: amt/site/templates/projects/new.html.j2:41 msgid "Select the lifecycle your algorithm system is currently in." msgstr "" -#: amt/site/templates/projects/new.html.j2:40 +#: amt/site/templates/projects/new.html.j2:42 msgid "For more information on lifecycle, read the" msgstr "" -#: amt/site/templates/projects/new.html.j2:43 +#: amt/site/templates/projects/new.html.j2:45 msgid "Algorithm Framework" msgstr "" -#: amt/site/templates/projects/new.html.j2:61 +#: amt/site/templates/projects/new.html.j2:63 msgid "AI Act Profile" msgstr "" -#: amt/site/templates/projects/new.html.j2:63 +#: amt/site/templates/projects/new.html.j2:65 msgid "" "The AI Act profile provides insight into, among other things, the type of" " AI system and the associated obligations from the European AI Act. If " @@ -630,33 +646,33 @@ msgid "" "tree." msgstr "" -#: amt/site/templates/projects/new.html.j2:70 +#: amt/site/templates/projects/new.html.j2:72 msgid "Find your AI Act profile" msgstr "" -#: amt/site/templates/projects/new.html.j2:128 +#: amt/site/templates/projects/new.html.j2:130 msgid "Yes" msgstr "" -#: amt/site/templates/projects/new.html.j2:138 +#: amt/site/templates/projects/new.html.j2:140 msgid "No" msgstr "" -#: amt/site/templates/projects/new.html.j2:147 +#: amt/site/templates/projects/new.html.j2:149 msgid "" "Overview of instruments for the responsible development, deployment, " "assessment and monitoring of algorithms and AI-systems." msgstr "" -#: amt/site/templates/projects/new.html.j2:155 +#: amt/site/templates/projects/new.html.j2:157 msgid "Choose one or more instruments" msgstr "" -#: amt/site/templates/projects/new.html.j2:177 +#: amt/site/templates/projects/new.html.j2:181 msgid "Create Algorithm System" msgstr "" -#: amt/site/templates/projects/new.html.j2:195 +#: amt/site/templates/projects/new.html.j2:198 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 1e457b00..fb15ccaa 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 4a280015..d78b5d85 100644 --- a/amt/locale/en_US/LC_MESSAGES/messages.po +++ b/amt/locale/en_US/LC_MESSAGES/messages.po @@ -134,7 +134,7 @@ msgstr "" msgid "Model" msgstr "" -#: amt/api/navigation.py:59 amt/site/templates/projects/new.html.j2:145 +#: amt/api/navigation.py:59 amt/site/templates/projects/new.html.j2:147 msgid "Instruments" msgstr "" @@ -162,6 +162,14 @@ msgstr "" msgid "Exception of application" msgstr "" +#: amt/core/exception_handlers.py:20 +msgid "String should have at least {min_length} characters" +msgstr "" + +#: amt/core/exception_handlers.py:21 +msgid "Field required" +msgstr "" + #: amt/core/exceptions.py:20 msgid "" "An error occurred while configuring the options for '{field}'. Please " @@ -264,6 +272,14 @@ msgstr "" msgid "An error occurred. Please try again later" msgstr "" +#: amt/site/templates/errors/_RequestValidationError_400.html.j2:3 +msgid "There are some errors" +msgstr "" + +#: amt/site/templates/errors/_RequestValidationError_400.html.j2:5 +msgid "There is one error:" +msgstr "" + #: amt/site/templates/layouts/base.html.j2:11 msgid "Algorithmic Management Toolkit (AMT)" msgstr "" @@ -416,7 +432,7 @@ msgstr "" #: amt/site/templates/parts/filter_list.html.j2:99 #: amt/site/templates/projects/details_info.html.j2:24 -#: amt/site/templates/projects/new.html.j2:37 +#: amt/site/templates/projects/new.html.j2:38 msgid "Lifecycle" msgstr "" @@ -602,27 +618,27 @@ msgstr "" msgid "Algorithm System name" msgstr "" -#: amt/site/templates/projects/new.html.j2:26 +#: amt/site/templates/projects/new.html.j2:27 msgid "Name of the algorithm system" msgstr "" -#: amt/site/templates/projects/new.html.j2:39 +#: amt/site/templates/projects/new.html.j2:41 msgid "Select the lifecycle your algorithm system is currently in." msgstr "" -#: amt/site/templates/projects/new.html.j2:40 +#: amt/site/templates/projects/new.html.j2:42 msgid "For more information on lifecycle, read the" msgstr "" -#: amt/site/templates/projects/new.html.j2:43 +#: amt/site/templates/projects/new.html.j2:45 msgid "Algorithm Framework" msgstr "" -#: amt/site/templates/projects/new.html.j2:61 +#: amt/site/templates/projects/new.html.j2:63 msgid "AI Act Profile" msgstr "" -#: amt/site/templates/projects/new.html.j2:63 +#: amt/site/templates/projects/new.html.j2:65 msgid "" "The AI Act profile provides insight into, among other things, the type of" " AI system and the associated obligations from the European AI Act. If " @@ -631,33 +647,33 @@ msgid "" "tree." msgstr "" -#: amt/site/templates/projects/new.html.j2:70 +#: amt/site/templates/projects/new.html.j2:72 msgid "Find your AI Act profile" msgstr "" -#: amt/site/templates/projects/new.html.j2:128 +#: amt/site/templates/projects/new.html.j2:130 msgid "Yes" msgstr "" -#: amt/site/templates/projects/new.html.j2:138 +#: amt/site/templates/projects/new.html.j2:140 msgid "No" msgstr "" -#: amt/site/templates/projects/new.html.j2:147 +#: amt/site/templates/projects/new.html.j2:149 msgid "" "Overview of instruments for the responsible development, deployment, " "assessment and monitoring of algorithms and AI-systems." msgstr "" -#: amt/site/templates/projects/new.html.j2:155 +#: amt/site/templates/projects/new.html.j2:157 msgid "Choose one or more instruments" msgstr "" -#: amt/site/templates/projects/new.html.j2:177 +#: amt/site/templates/projects/new.html.j2:181 msgid "Create Algorithm System" msgstr "" -#: amt/site/templates/projects/new.html.j2:195 +#: amt/site/templates/projects/new.html.j2:198 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 8e677db1..dc090db2 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 4090ede8..e71e4efb 100644 --- a/amt/locale/nl_NL/LC_MESSAGES/messages.po +++ b/amt/locale/nl_NL/LC_MESSAGES/messages.po @@ -134,7 +134,7 @@ msgstr "Data" msgid "Model" msgstr "Model" -#: amt/api/navigation.py:59 amt/site/templates/projects/new.html.j2:145 +#: amt/api/navigation.py:59 amt/site/templates/projects/new.html.j2:147 msgid "Instruments" msgstr "Instrumenten" @@ -162,6 +162,14 @@ msgstr "Verboden AI" msgid "Exception of application" msgstr "Uitzondering van toepassing" +#: amt/core/exception_handlers.py:20 +msgid "String should have at least {min_length} characters" +msgstr "Tekst moet minimaal {min_length} tekens bevatten" + +#: amt/core/exception_handlers.py:21 +msgid "Field required" +msgstr "Veld verplicht" + #: amt/core/exceptions.py:20 msgid "" "An error occurred while configuring the options for '{field}'. Please " @@ -272,6 +280,14 @@ msgstr "Er is een fout opgetreden" msgid "An error occurred. Please try again later" msgstr "Er is een fout opgetreden. Probeer het later opnieuw." +#: amt/site/templates/errors/_RequestValidationError_400.html.j2:3 +msgid "There are some errors" +msgstr "r zijn enkele fouten" + +#: amt/site/templates/errors/_RequestValidationError_400.html.j2:5 +msgid "There is one error:" +msgstr "Er is één fout:" + #: amt/site/templates/layouts/base.html.j2:11 msgid "Algorithmic Management Toolkit (AMT)" msgstr "Algoritme Management Toolkit (AMT)" @@ -434,7 +450,7 @@ msgstr "Algoritmesysteem naam" #: amt/site/templates/parts/filter_list.html.j2:99 #: amt/site/templates/projects/details_info.html.j2:24 -#: amt/site/templates/projects/new.html.j2:37 +#: amt/site/templates/projects/new.html.j2:38 msgid "Lifecycle" msgstr "Levenscyclus" @@ -620,31 +636,31 @@ msgstr "Creëer een Algoritmesysteem" #: amt/site/templates/projects/new.html.j2:23 msgid "Algorithm System name" -msgstr "Naam algoritmesysteem" +msgstr "Algoritme Systeem naam" -#: amt/site/templates/projects/new.html.j2:26 +#: amt/site/templates/projects/new.html.j2:27 msgid "Name of the algorithm system" -msgstr "Naam van het algoritmesysteem" +msgstr "Nieuw algoritmesysteem" -#: amt/site/templates/projects/new.html.j2:39 +#: amt/site/templates/projects/new.html.j2:41 msgid "Select the lifecycle your algorithm system is currently in." msgstr "" "Selecteer de levenscyclus waarin uw algoritmesysteem zich momenteel " "bevindt." -#: amt/site/templates/projects/new.html.j2:40 +#: amt/site/templates/projects/new.html.j2:42 msgid "For more information on lifecycle, read the" msgstr "Lees voor meer meer informatie over levenscyclus het" -#: amt/site/templates/projects/new.html.j2:43 +#: amt/site/templates/projects/new.html.j2:45 msgid "Algorithm Framework" msgstr "Algoritmekader" -#: amt/site/templates/projects/new.html.j2:61 +#: amt/site/templates/projects/new.html.j2:63 msgid "AI Act Profile" msgstr "AI Verordening Profiel" -#: amt/site/templates/projects/new.html.j2:63 +#: amt/site/templates/projects/new.html.j2:65 msgid "" "The AI Act profile provides insight into, among other things, the type of" " AI system and the associated obligations from the European AI Act. If " @@ -656,19 +672,19 @@ msgstr "" "onder andere het type AI systeem, de regelgeving die van toepassing is en" " de risicocategorie." -#: amt/site/templates/projects/new.html.j2:70 +#: amt/site/templates/projects/new.html.j2:72 msgid "Find your AI Act profile" msgstr "Vind uw AI Act profiel" -#: amt/site/templates/projects/new.html.j2:128 +#: amt/site/templates/projects/new.html.j2:130 msgid "Yes" msgstr "Ja" -#: amt/site/templates/projects/new.html.j2:138 +#: amt/site/templates/projects/new.html.j2:140 msgid "No" msgstr "Nee" -#: amt/site/templates/projects/new.html.j2:147 +#: amt/site/templates/projects/new.html.j2:149 msgid "" "Overview of instruments for the responsible development, deployment, " "assessment and monitoring of algorithms and AI-systems." @@ -676,15 +692,15 @@ msgstr "" "Overzicht van aanbevolen instrument voor het verantwoord ontwikkelen, " "gebruiken, beoordelen en monitoren van algoritmes en AI-systemen." -#: amt/site/templates/projects/new.html.j2:155 +#: amt/site/templates/projects/new.html.j2:157 msgid "Choose one or more instruments" msgstr "Kies één of meerdere instrumenten" -#: amt/site/templates/projects/new.html.j2:177 +#: amt/site/templates/projects/new.html.j2:181 msgid "Create Algorithm System" msgstr "Creëer Algoritmesysteem" -#: amt/site/templates/projects/new.html.j2:195 +#: amt/site/templates/projects/new.html.j2:198 msgid "Copy results and close" msgstr "Resultaten overnemen en sluiten" diff --git a/amt/site/static/scss/layout.scss b/amt/site/static/scss/layout.scss index 85bc9933..1453dfcd 100644 --- a/amt/site/static/scss/layout.scss +++ b/amt/site/static/scss/layout.scss @@ -286,3 +286,8 @@ main { .rvo-table-row td:first-child { width: 20%; } + +.amt-error-message { + color: var(--rvo-form-feedback-error-color); + font-weight: var(--rvo-form-feedback-error-font-weight); +} diff --git a/amt/site/templates/errors/_RequestValidationError_400.html.j2 b/amt/site/templates/errors/_RequestValidationError_400.html.j2 new file mode 100644 index 00000000..9a54ab2d --- /dev/null +++ b/amt/site/templates/errors/_RequestValidationError_400.html.j2 @@ -0,0 +1,26 @@ +
+ {% if message|length > 1 %} +

{% trans %}There are some errors{% endtrans %}

+ {% else %} +

{% trans %}There is one error:{% endtrans %}

+ {% endif %} + {% for msg in message %} +
+ {{ msg['loc'][-1] | capitalize }}: + + {{ msg['msg'] }} +
+ {% endfor %} +
+{% for msg in message %} +
+ + {{ msg['msg'] }} +
+{% endfor %} diff --git a/amt/site/templates/projects/new.html.j2 b/amt/site/templates/projects/new.html.j2 index d6cf3037..44e36496 100644 --- a/amt/site/templates/projects/new.html.j2 +++ b/amt/site/templates/projects/new.html.j2 @@ -22,6 +22,7 @@
+
+

{% trans %}Select the lifecycle your algorithm system is currently in.{% endtrans %} {% trans %}For more information on lifecycle, read the{% endtrans %} @@ -170,15 +172,16 @@

+

-
diff --git a/tests/api/routes/test_projects.py b/tests/api/routes/test_projects.py index e1bf9dee..cdd09045 100644 --- a/tests/api/routes/test_projects.py +++ b/tests/api/routes/test_projects.py @@ -70,7 +70,7 @@ def test_post_new_projects_bad_request(client: TestClient, mocker: MockFixture) # then assert response.status_code == 400 assert response.headers["content-type"] == "text/html; charset=utf-8" - assert b"name: Field required" in response.content + assert b"Field required" in response.content def test_post_new_projects(client: TestClient, mocker: MockFixture) -> None: