diff --git a/amt/api/forms/measure.py b/amt/api/forms/measure.py index 4a8d40e4..621671bf 100644 --- a/amt/api/forms/measure.py +++ b/amt/api/forms/measure.py @@ -4,7 +4,7 @@ async def get_measure_form( - id: str, current_values: dict[str, str | list[str]], translations: NullTranslations + id: str, current_values: dict[str, str | list[str] | list[tuple[str, str]]], translations: NullTranslations ) -> WebForm: _ = translations.gettext diff --git a/amt/api/routes/algorithm.py b/amt/api/routes/algorithm.py index bc5755cd..935cee70 100644 --- a/amt/api/routes/algorithm.py +++ b/amt/api/routes/algorithm.py @@ -1,11 +1,10 @@ import asyncio import logging -from io import BytesIO from collections.abc import Sequence from typing import Annotated, Any, cast from fastapi import APIRouter, Depends, File, Form, Request, Response, UploadFile -from fastapi.responses import HTMLResponse, JSONResponse +from fastapi.responses import HTMLResponse from pydantic import BaseModel, Field from ulid import ULID @@ -526,7 +525,7 @@ async def get_measure( algorithm = await get_algorithm_or_error(algorithm_id, algorithms_service, request) measure = await measures_service.fetch_measures([measure_urn]) measure_task = get_measure_task_or_error(algorithm.system_card, measure_urn) - filenames = [object_storage_service.get_file_filename_and_ext(file) for file in measure_task.files] + filenames = [object_storage_service.get_file_info(file) for file in measure_task.files] measure_form = await get_measure_form( id="measure_state", @@ -776,11 +775,12 @@ async def get_file( ) -> Response: algorithm = await get_algorithm_or_error(algorithm_id, algorithms_service, request) file = object_storage_service.get_file(algorithm.organization_id, algorithm_id, ulid) + filename_ext = object_storage_service.get_file_filename_and_ext(algorithm.organization_id, algorithm_id, ulid) return Response( content=file.read(decode_content=True), headers={ - "Content-Disposition": f"attachment;filename={ulid}", + "Content-Disposition": f"attachment;filename={filename_ext}", "Content-Type": "application/octet-stream", }, ) diff --git a/amt/locale/base.pot b/amt/locale/base.pot index 5a61fef0..a3612615 100644 --- a/amt/locale/base.pot +++ b/amt/locale/base.pot @@ -201,29 +201,29 @@ msgstr "" msgid "Status" msgstr "" -#: amt/api/forms/measure.py:30 +#: amt/api/forms/measure.py:32 msgid "Information on how this measure is implemented" msgstr "" -#: amt/api/forms/measure.py:31 +#: amt/api/forms/measure.py:33 msgid "" "Describe how the measure has been implemented, including challenges and " "solutions." msgstr "" -#: amt/api/forms/measure.py:38 +#: amt/api/forms/measure.py:40 msgid "Add files" msgstr "" -#: amt/api/forms/measure.py:43 +#: amt/api/forms/measure.py:45 msgid "Add URI" msgstr "" -#: amt/api/forms/measure.py:46 +#: amt/api/forms/measure.py:48 msgid "Add links to documents" msgstr "" -#: amt/api/forms/measure.py:47 +#: amt/api/forms/measure.py:49 msgid "URI" msgstr "" @@ -238,28 +238,28 @@ msgstr "" msgid "Name of the organization" msgstr "" -#: amt/api/forms/organization.py:23 +#: amt/api/forms/organization.py:24 msgid "The slug is the web path, like /organizations/my-organization-name" msgstr "" -#: amt/api/forms/organization.py:24 +#: amt/api/forms/organization.py:25 #: amt/site/templates/organizations/home.html.j2:29 msgid "Slug" msgstr "" -#: amt/api/forms/organization.py:25 +#: amt/api/forms/organization.py:26 msgid "The slug for this organization" msgstr "" -#: amt/api/forms/organization.py:30 +#: amt/api/forms/organization.py:32 msgid "Add members" msgstr "" -#: amt/api/forms/organization.py:31 +#: amt/api/forms/organization.py:33 msgid "Search for a person..." msgstr "" -#: amt/api/forms/organization.py:36 +#: amt/api/forms/organization.py:38 msgid "Add organization" msgstr "" @@ -323,6 +323,10 @@ msgid "" "later." msgstr "" +#: amt/core/exceptions.py:84 +msgid "Something went wrong storing your file. PLease try again later." +msgstr "" + #: amt/site/templates/algorithms/details_base.html.j2:19 msgid "Delete algoritmic system" msgstr "" @@ -494,7 +498,7 @@ msgstr "" msgid "measures executed" msgstr "" -#: amt/site/templates/algorithms/details_requirements.html.j2:55 +#: amt/site/templates/algorithms/details_requirements.html.j2:59 #: amt/site/templates/macros/editable.html.j2:24 #: amt/site/templates/macros/editable.html.j2:27 msgid "Edit" diff --git a/amt/locale/en_US/LC_MESSAGES/messages.mo b/amt/locale/en_US/LC_MESSAGES/messages.mo index 96ca6835..35f447df 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 a86a3d7d..1b2c587a 100644 --- a/amt/locale/en_US/LC_MESSAGES/messages.po +++ b/amt/locale/en_US/LC_MESSAGES/messages.po @@ -202,29 +202,29 @@ msgstr "" msgid "Status" msgstr "" -#: amt/api/forms/measure.py:30 +#: amt/api/forms/measure.py:32 msgid "Information on how this measure is implemented" msgstr "" -#: amt/api/forms/measure.py:31 +#: amt/api/forms/measure.py:33 msgid "" "Describe how the measure has been implemented, including challenges and " "solutions." msgstr "" -#: amt/api/forms/measure.py:38 +#: amt/api/forms/measure.py:40 msgid "Add files" msgstr "" -#: amt/api/forms/measure.py:43 +#: amt/api/forms/measure.py:45 msgid "Add URI" msgstr "" -#: amt/api/forms/measure.py:46 +#: amt/api/forms/measure.py:48 msgid "Add links to documents" msgstr "" -#: amt/api/forms/measure.py:47 +#: amt/api/forms/measure.py:49 msgid "URI" msgstr "" @@ -239,28 +239,28 @@ msgstr "" msgid "Name of the organization" msgstr "" -#: amt/api/forms/organization.py:23 +#: amt/api/forms/organization.py:24 msgid "The slug is the web path, like /organizations/my-organization-name" msgstr "" -#: amt/api/forms/organization.py:24 +#: amt/api/forms/organization.py:25 #: amt/site/templates/organizations/home.html.j2:29 msgid "Slug" msgstr "" -#: amt/api/forms/organization.py:25 +#: amt/api/forms/organization.py:26 msgid "The slug for this organization" msgstr "" -#: amt/api/forms/organization.py:30 +#: amt/api/forms/organization.py:32 msgid "Add members" msgstr "" -#: amt/api/forms/organization.py:31 +#: amt/api/forms/organization.py:33 msgid "Search for a person..." msgstr "" -#: amt/api/forms/organization.py:36 +#: amt/api/forms/organization.py:38 msgid "Add organization" msgstr "" @@ -324,6 +324,10 @@ msgid "" "later." msgstr "" +#: amt/core/exceptions.py:84 +msgid "Something went wrong storing your file. PLease try again later." +msgstr "" + #: amt/site/templates/algorithms/details_base.html.j2:19 msgid "Delete algoritmic system" msgstr "" @@ -495,7 +499,7 @@ msgstr "" msgid "measures executed" msgstr "" -#: amt/site/templates/algorithms/details_requirements.html.j2:55 +#: amt/site/templates/algorithms/details_requirements.html.j2:59 #: amt/site/templates/macros/editable.html.j2:24 #: amt/site/templates/macros/editable.html.j2:27 msgid "Edit" diff --git a/amt/locale/nl_NL/LC_MESSAGES/messages.mo b/amt/locale/nl_NL/LC_MESSAGES/messages.mo index 688497ec..aef6b446 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 769a642a..5ac7d795 100644 --- a/amt/locale/nl_NL/LC_MESSAGES/messages.po +++ b/amt/locale/nl_NL/LC_MESSAGES/messages.po @@ -202,11 +202,11 @@ msgstr "Organisatie" msgid "Status" msgstr "Status" -#: amt/api/forms/measure.py:30 +#: amt/api/forms/measure.py:32 msgid "Information on how this measure is implemented" msgstr "Informatie over hoe deze maatregel is geïmplementeerd" -#: amt/api/forms/measure.py:31 +#: amt/api/forms/measure.py:33 msgid "" "Describe how the measure has been implemented, including challenges and " "solutions." @@ -214,19 +214,19 @@ msgstr "" "Beschrijf hoe de maatregel is geimplementeerd, inclusief uitdagingen " "enoplossingen." -#: amt/api/forms/measure.py:38 +#: amt/api/forms/measure.py:40 msgid "Add files" msgstr "Bestanden toevoegen" -#: amt/api/forms/measure.py:43 +#: amt/api/forms/measure.py:45 msgid "Add URI" msgstr "Voeg URI toe" -#: amt/api/forms/measure.py:46 +#: amt/api/forms/measure.py:48 msgid "Add links to documents" msgstr "Voeg link naar documenten toe" -#: amt/api/forms/measure.py:47 +#: amt/api/forms/measure.py:49 msgid "URI" msgstr "URI" @@ -241,30 +241,30 @@ msgstr "Naam" msgid "Name of the organization" msgstr "Naam van de organisatie" -#: amt/api/forms/organization.py:23 +#: amt/api/forms/organization.py:24 msgid "The slug is the web path, like /organizations/my-organization-name" msgstr "" "Een slug is het pad in het webadres, zoals /organizations/mijn-" "organisatie-naam" -#: amt/api/forms/organization.py:24 +#: amt/api/forms/organization.py:25 #: amt/site/templates/organizations/home.html.j2:29 msgid "Slug" msgstr "Slug" -#: amt/api/forms/organization.py:25 +#: amt/api/forms/organization.py:26 msgid "The slug for this organization" msgstr "Het web-pad (slug) voor deze organisatie" -#: amt/api/forms/organization.py:30 +#: amt/api/forms/organization.py:32 msgid "Add members" msgstr "Voeg personen toe" -#: amt/api/forms/organization.py:31 +#: amt/api/forms/organization.py:33 msgid "Search for a person..." msgstr "Zoek een persoon..." -#: amt/api/forms/organization.py:36 +#: amt/api/forms/organization.py:38 msgid "Add organization" msgstr "Organisatie toevoegen" @@ -336,6 +336,12 @@ msgstr "" "Er is iets fout gegaan tijdens de autorisatiestroom. Probeer het later " "opnieuw" +#: amt/core/exceptions.py:84 +msgid "Something went wrong storing your file. PLease try again later." +msgstr "" +"Er is iets fout gegaan tijdens het opslaan van uw bestand. Probeer het " +"later opnieuw" + #: amt/site/templates/algorithms/details_base.html.j2:19 msgid "Delete algoritmic system" msgstr "Verwijder algoritme" @@ -509,7 +515,7 @@ msgstr "Annuleren" msgid "measures executed" msgstr "maatregelen uitgevoerd" -#: amt/site/templates/algorithms/details_requirements.html.j2:55 +#: amt/site/templates/algorithms/details_requirements.html.j2:59 #: amt/site/templates/macros/editable.html.j2:24 #: amt/site/templates/macros/editable.html.j2:27 msgid "Edit" diff --git a/amt/schema/webform.py b/amt/schema/webform.py index 7f9845be..392c3c81 100644 --- a/amt/schema/webform.py +++ b/amt/schema/webform.py @@ -40,7 +40,7 @@ def __init__(self, type: WebFormFieldType, name: str, label: str, group: str | N class WebFormField(WebFormBaseField): placeholder: str | None - default_value: str | list[str] | None + default_value: str | list[str] | list[tuple[str, str]] | None options: list[WebFormOption] | None validators: list[Any] description: str | None @@ -53,7 +53,7 @@ def __init__( name: str, label: str, placeholder: str | None = None, - default_value: str | list[str] | None = None, + default_value: str | list[str] | list[tuple[str, str]] | None = None, options: list[WebFormOption] | None = None, attributes: dict[str, str] | None = None, description: str | None = None, @@ -80,7 +80,7 @@ def __init__( name: str, label: str, placeholder: str | None = None, - default_value: str | list[str] | None = None, + default_value: str | list[str] | list[tuple[str, str]] | None = None, options: list[WebFormOption] | None = None, attributes: dict[str, str] | None = None, group: str | None = None, @@ -110,7 +110,7 @@ def __init__( name: str, label: str, placeholder: str | None = None, - default_value: str | list[str] | None = None, + default_value: str | list[str] | list[tuple[str, str]] | None = None, options: list[WebFormOption] | None = None, attributes: dict[str, str] | None = None, group: str | None = None, diff --git a/amt/services/object_storage.py b/amt/services/object_storage.py index 95826368..78b211f1 100644 --- a/amt/services/object_storage.py +++ b/amt/services/object_storage.py @@ -4,7 +4,6 @@ from fastapi import UploadFile from minio import Minio -from minio.datatypes import Object from ulid import ULID from urllib3 import BaseHTTPResponse @@ -114,7 +113,7 @@ def get_file(self, organization_id: str | int, algorithm_id: str | int, ulid: UL raise AMTStorageError() from err return file - def get_file_filename_and_ext(self, name: str) -> str: + def get_file_info(self, name: str) -> tuple[str, str]: """ Gets filename and extension from a file from the object storage without downloading its contents. @@ -124,6 +123,22 @@ def get_file_filename_and_ext(self, name: str) -> str: except Exception as err: raise AMTStorageError() from err + object_name = stats.object_name.split("/")[-1] if stats.object_name else "" + name = stats.metadata["X-Amz-Meta-Filename"] if stats.metadata else "" + ext = stats.metadata["X-Amz-Meta-Ext"] if stats.metadata else "" + return object_name, f"{name}{ext}" + + def get_file_filename_and_ext(self, organization_id: str | int, algorithm_id: str | int, ulid: ULID) -> str: + """ + Gets filename and extension from a file from the object storage without downloading its + contents. + """ + path = self._generate_destination_path(organization_id, algorithm_id, ulid) + try: + stats = self.client.stat_object(self.bucket_name, path) + except Exception as err: + raise AMTStorageError() from err + name = stats.metadata["X-Amz-Meta-Filename"] if stats.metadata else "" ext = stats.metadata["X-Amz-Meta-Ext"] if stats.metadata else "" return f"{name}{ext}" diff --git a/amt/site/templates/macros/form_macros.html.j2 b/amt/site/templates/macros/form_macros.html.j2 index 7de1dd72..07d5badb 100644 --- a/amt/site/templates/macros/form_macros.html.j2 +++ b/amt/site/templates/macros/form_macros.html.j2 @@ -126,7 +126,7 @@