diff --git a/.gitignore b/.gitignore index d6e6b144..b2407927 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ amt/site/static/js/* # ignore webpack build files amt/site/templates/layouts/base.html.j2 + +# downloaded system_cards +*00:00.yaml diff --git a/amt/api/routes/algorithm.py b/amt/api/routes/algorithm.py index 42e6c7b8..126f1d3e 100644 --- a/amt/api/routes/algorithm.py +++ b/amt/api/routes/algorithm.py @@ -1,10 +1,12 @@ import asyncio +import datetime import logging from collections.abc import Sequence from typing import Annotated, Any, cast +import yaml from fastapi import APIRouter, Depends, Request -from fastapi.responses import HTMLResponse +from fastapi.responses import FileResponse, HTMLResponse from pydantic import BaseModel, Field from amt.api.deps import templates @@ -736,3 +738,17 @@ async def get_model_card( } return templates.TemplateResponse(request, "pages/model_card.html.j2", context) + + +@router.get("/{algorithm_id}/details/system_card/download") +async def download_algorithm_system_card_as_yaml( + algorithm_id: int, algorithms_service: Annotated[AlgorithmsService, Depends(AlgorithmsService)], request: Request +) -> FileResponse: + algorithm = await get_algorithm_or_error(algorithm_id, algorithms_service, request) + filename = algorithm.name + "_" + datetime.datetime.now(datetime.UTC).isoformat() + ".yaml" + with open(filename, "w") as outfile: + yaml.dump(algorithm.system_card.model_dump(), outfile) + try: + return FileResponse(filename, filename=filename) + except AMTRepositoryError as e: + raise AMTNotFound from e diff --git a/amt/locale/base.pot b/amt/locale/base.pot index 63ed74ae..0e8a820e 100644 --- a/amt/locale/base.pot +++ b/amt/locale/base.pot @@ -8,14 +8,14 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 0001-01-01 00:00+0000\n" -"PO-Revision-Date: 0001-01-01 00:00+0000\n" +"POT-Creation-Date: 2024-12-09 10:35+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.16.0\n" #: amt/api/ai_act_profile.py:24 msgid "Type" @@ -323,40 +323,40 @@ msgstr "" msgid "No" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:57 -msgid "Delete algorithm" +#: amt/site/templates/algorithms/details_base.html.j2:63 +msgid "Download as YAML" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:74 +#: amt/site/templates/algorithms/details_base.html.j2:87 msgid "Does the algorithm meet the requirements?" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:77 -#: amt/site/templates/algorithms/details_base.html.j2:103 -#: amt/site/templates/algorithms/details_base.html.j2:134 -#: amt/site/templates/algorithms/details_base.html.j2:157 +#: amt/site/templates/algorithms/details_base.html.j2:90 +#: amt/site/templates/algorithms/details_base.html.j2:116 +#: amt/site/templates/algorithms/details_base.html.j2:147 +#: amt/site/templates/algorithms/details_base.html.j2:170 #: amt/site/templates/macros/tasks.html.j2:32 msgid "Done" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:99 -#: amt/site/templates/algorithms/details_base.html.j2:155 +#: amt/site/templates/algorithms/details_base.html.j2:112 +#: amt/site/templates/algorithms/details_base.html.j2:168 msgid "To do" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:101 +#: amt/site/templates/algorithms/details_base.html.j2:114 msgid "In progress" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:117 +#: amt/site/templates/algorithms/details_base.html.j2:130 msgid "Go to all requirements" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:131 +#: amt/site/templates/algorithms/details_base.html.j2:144 msgid "Which instruments are executed?" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:171 +#: amt/site/templates/algorithms/details_base.html.j2:184 msgid "Go to all instruments" msgstr "" diff --git a/amt/locale/en_US/LC_MESSAGES/messages.mo b/amt/locale/en_US/LC_MESSAGES/messages.mo index 738f59d7..f14e1c9e 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 288912d0..92bbab77 100644 --- a/amt/locale/en_US/LC_MESSAGES/messages.po +++ b/amt/locale/en_US/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 0001-01-01 00:00+0000\n" +"POT-Creation-Date: 2024-12-09 10:35+0100\n" "PO-Revision-Date: 0001-01-01 00:00+0000\n" "Last-Translator: FULL NAME \n" "Language: en_US\n" @@ -16,7 +16,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.16.0\n" #: amt/api/ai_act_profile.py:24 msgid "Type" @@ -324,40 +324,40 @@ msgstr "" msgid "No" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:57 -msgid "Delete algorithm" +#: amt/site/templates/algorithms/details_base.html.j2:63 +msgid "Download as YAML" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:74 +#: amt/site/templates/algorithms/details_base.html.j2:87 msgid "Does the algorithm meet the requirements?" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:77 -#: amt/site/templates/algorithms/details_base.html.j2:103 -#: amt/site/templates/algorithms/details_base.html.j2:134 -#: amt/site/templates/algorithms/details_base.html.j2:157 +#: amt/site/templates/algorithms/details_base.html.j2:90 +#: amt/site/templates/algorithms/details_base.html.j2:116 +#: amt/site/templates/algorithms/details_base.html.j2:147 +#: amt/site/templates/algorithms/details_base.html.j2:170 #: amt/site/templates/macros/tasks.html.j2:32 msgid "Done" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:99 -#: amt/site/templates/algorithms/details_base.html.j2:155 +#: amt/site/templates/algorithms/details_base.html.j2:112 +#: amt/site/templates/algorithms/details_base.html.j2:168 msgid "To do" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:101 +#: amt/site/templates/algorithms/details_base.html.j2:114 msgid "In progress" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:117 +#: amt/site/templates/algorithms/details_base.html.j2:130 msgid "Go to all requirements" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:131 +#: amt/site/templates/algorithms/details_base.html.j2:144 msgid "Which instruments are executed?" msgstr "" -#: amt/site/templates/algorithms/details_base.html.j2:171 +#: amt/site/templates/algorithms/details_base.html.j2:184 msgid "Go to all instruments" msgstr "" diff --git a/amt/locale/nl_NL/LC_MESSAGES/messages.mo b/amt/locale/nl_NL/LC_MESSAGES/messages.mo index b82af76b..a84759a8 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 e1c86fab..8a18c99c 100644 --- a/amt/locale/nl_NL/LC_MESSAGES/messages.po +++ b/amt/locale/nl_NL/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 0001-01-01 00:00+0000\n" +"POT-Creation-Date: 2024-12-09 10:35+0100\n" "PO-Revision-Date: 0001-01-01 00:00+0000\n" "Last-Translator: FULL NAME \n" "Language: nl_NL\n" @@ -16,7 +16,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.16.0\n" #: amt/api/ai_act_profile.py:24 msgid "Type" @@ -338,40 +338,40 @@ msgstr "Ja" msgid "No" msgstr "Nee" -#: amt/site/templates/algorithms/details_base.html.j2:57 -msgid "Delete algorithm" -msgstr "Verwijder algoritme" +#: amt/site/templates/algorithms/details_base.html.j2:63 +msgid "Download as YAML" +msgstr "Download naar YAML" -#: amt/site/templates/algorithms/details_base.html.j2:74 +#: amt/site/templates/algorithms/details_base.html.j2:87 msgid "Does the algorithm meet the requirements?" msgstr "Voldoet het algoritme aan de vereisten?" -#: amt/site/templates/algorithms/details_base.html.j2:77 -#: amt/site/templates/algorithms/details_base.html.j2:103 -#: amt/site/templates/algorithms/details_base.html.j2:134 -#: amt/site/templates/algorithms/details_base.html.j2:157 +#: amt/site/templates/algorithms/details_base.html.j2:90 +#: amt/site/templates/algorithms/details_base.html.j2:116 +#: amt/site/templates/algorithms/details_base.html.j2:147 +#: amt/site/templates/algorithms/details_base.html.j2:170 #: amt/site/templates/macros/tasks.html.j2:32 msgid "Done" msgstr "Afgerond" -#: amt/site/templates/algorithms/details_base.html.j2:99 -#: amt/site/templates/algorithms/details_base.html.j2:155 +#: amt/site/templates/algorithms/details_base.html.j2:112 +#: amt/site/templates/algorithms/details_base.html.j2:168 msgid "To do" msgstr "Te doen" -#: amt/site/templates/algorithms/details_base.html.j2:101 +#: amt/site/templates/algorithms/details_base.html.j2:114 msgid "In progress" msgstr "Onderhanden" -#: amt/site/templates/algorithms/details_base.html.j2:117 +#: amt/site/templates/algorithms/details_base.html.j2:130 msgid "Go to all requirements" msgstr "Ga naar alle Vereisten" -#: amt/site/templates/algorithms/details_base.html.j2:131 +#: amt/site/templates/algorithms/details_base.html.j2:144 msgid "Which instruments are executed?" msgstr "Welke instrumenten zijn uitgevoerd?" -#: amt/site/templates/algorithms/details_base.html.j2:171 +#: amt/site/templates/algorithms/details_base.html.j2:184 msgid "Go to all instruments" msgstr "Ga naar all instrumenten" diff --git a/amt/site/static/scss/layout.scss b/amt/site/static/scss/layout.scss index 66003a24..e6dcff3a 100644 --- a/amt/site/static/scss/layout.scss +++ b/amt/site/static/scss/layout.scss @@ -162,6 +162,7 @@ main { &.amt-layout-grid-columns--two { grid-template-columns: repeat(2, 1fr); } + /* stylelint-enable */ } @@ -389,6 +390,40 @@ main { visibility: visible; } +.dropdown-content { + display: none; + position: absolute; + background-color: #f9f9f9; + min-width: 160px; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + z-index: 1; +} + +.dropdown-content a { + color: black; + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown-content a:hover { + background-color: #f1f1f1; +} + +.dropdown:hover .dropdown-content { + display: block; +} + +.dropdown-underlay { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1; +} + /* stylelint-enable */ .amt-cursor-pointer { diff --git a/amt/site/static/ts/amt.ts b/amt/site/static/ts/amt.ts index 47d93a26..88d03875 100644 --- a/amt/site/static/ts/amt.ts +++ b/amt/site/static/ts/amt.ts @@ -308,6 +308,61 @@ export function hide_form_search_options(id: string) { }, 250); } +export function hide_download_dropdown() { + const dropdownContent = document.querySelector( + ".dropdown-content", + ) as HTMLElement; + const dropdownUnderlay = document.querySelector( + ".dropdown-underlay", + ) as HTMLElement; + + if (dropdownContent && dropdownUnderlay) { + dropdownContent.style.display = "none"; + dropdownUnderlay.style.display = "none"; + } else { + console.error("Could not find dropdown elements."); + } +} + +export function show_download_dropdown() { + const dropdownContent = document.querySelector( + ".dropdown-content", + ) as HTMLElement; + const dropdownUnderlay = document.querySelector( + ".dropdown-underlay", + ) as HTMLElement; + + if (dropdownContent && dropdownUnderlay) { + dropdownContent.style.display = "block"; + dropdownUnderlay.style.display = "block"; + } else { + console.error("Could not find dropdown elements."); + } +} + +export async function download_as_yaml( + algorithm_id: string, + algorithm_name: string, +): Promise { + try { + const response = await fetch( + `/algorithm/${algorithm_id}/details/system_card/download`, + ); + const blob = await response.blob(); // Get the response as a Blob + + const url = window.URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + const filename = algorithm_name + "_" + new Date().toISOString() + ".yaml"; + link.setAttribute("download", filename); + document.body.appendChild(link); + link.click(); + } catch (error) { + console.error("Error downloading system card:", error); + } + hide_download_dropdown(); +} + export function add_field_on_enter(id: string) { if (!event) { return; diff --git a/amt/site/templates/algorithms/details_base.html.j2 b/amt/site/templates/algorithms/details_base.html.j2 index c561ac0f..34029a32 100644 --- a/amt/site/templates/algorithms/details_base.html.j2 +++ b/amt/site/templates/algorithms/details_base.html.j2 @@ -49,13 +49,26 @@