Skip to content

Commit

Permalink
Add more editable fields
Browse files Browse the repository at this point in the history
  • Loading branch information
uittenbroekrobbert committed Jan 10, 2025
1 parent 3502b09 commit 93014bb
Show file tree
Hide file tree
Showing 35 changed files with 205 additions and 13,012 deletions.
3 changes: 1 addition & 2 deletions amt/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
time_ago,
)
from amt.schema.shared import IterMixin
from amt.schema.webform import WebFormFieldImplementationType, WebFormFieldType
from amt.schema.webform import WebFormFieldType

T = TypeVar("T", bound=Enum | LocalizableEnum)

Expand All @@ -55,7 +55,6 @@ def custom_context_processor(
"user": get_user(request),
"permissions": permissions,
"WebFormFieldType": WebFormFieldType,
"WebFormFieldImplementationType": WebFormFieldImplementationType,
}


Expand Down
2 changes: 1 addition & 1 deletion amt/api/editable.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ async def get_enriched_resolved_editable(
) -> ResolvedEditable:
"""
Using the given full_resource_path, resolves the resource and current value.
For example, using /algorithm/1/systemcard/info, the value of the info field end the resource,
For example, using /algorithm/1/systemcard/info, the value of the info field and the resource,
being an algorithm object, are available. The first is used in 'get' situations, the resource_object
can be used to store a new value.
Expand Down
3 changes: 2 additions & 1 deletion amt/api/http_browser_caching.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys
import urllib
from functools import lru_cache
from os import PathLike
Expand Down Expand Up @@ -61,7 +62,7 @@ class URLComponents(NamedTuple):
fragment: str


@lru_cache(maxsize=1000)
@lru_cache(maxsize=0 if "pytest" in sys.modules else 1000)
def url_for_cache(name: str, /, **path_params: str) -> str:
if name != "static":
raise AMTOnlyStatic()
Expand Down
24 changes: 19 additions & 5 deletions amt/api/routes/algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
resolve_base_navigation_items,
resolve_navigation_items,
)
from amt.api.routes.shared import UpdateFieldModel, get_filters_and_sort_by
from amt.api.routes.shared import UpdateFieldModel, get_filters_and_sort_by, replace_none_with_empty_string_inplace
from amt.core.authorization import get_user
from amt.core.exceptions import AMTError, AMTNotFound, AMTRepositoryError
from amt.core.internationalization import get_current_translation
Expand Down Expand Up @@ -377,8 +377,11 @@ async def get_system_card(
request,
)

system_card = algorithm.system_card
replace_none_with_empty_string_inplace(system_card)

context = {
"system_card": algorithm.system_card,
"system_card": system_card,
"instrument_state": instrument_state,
"requirements_state": requirements_state,
"last_edited": algorithm.last_edited,
Expand Down Expand Up @@ -469,6 +472,9 @@ async def get_system_card_requirements(
extended_linked_measures: list[ExtendedMeasureTask] = []
for measure in linked_measures:
measure_task = find_measure_task(algorithm.system_card, measure.urn)
# TODO: it is strange if measures would be missing as they should be added by the requirements?
if measure_task is None:
measure_tasks.append(measure)
if measure_task not in measure_tasks:
measure_tasks.append(measure_task)
if measure_task:
Expand Down Expand Up @@ -509,7 +515,7 @@ async def get_measure_task_functions(
) -> dict[str, list[Any]]:
measure_task_functions: dict[str, list[Any]] = defaultdict(list)
for measure_task in measure_tasks:
if measure_task.accountable_persons: # pyright: ignore [reportOptionalMemberAccess]
if hasattr(measure_task, "accountable_persons") and len(measure_task.accountable_persons) > 0:
members_accountable = await users_repository.find_all(
search=measure_task.accountable_persons[0].name, # pyright: ignore [reportOptionalMemberAccess]
sort=sort_by,
Expand All @@ -518,7 +524,7 @@ async def get_measure_task_functions(
if members_accountable:
measure_task_functions[measure_task.urn].append(members_accountable[0]) # pyright: ignore [reportOptionalMemberAccess]

if measure_task.reviewer_persons: # pyright: ignore [reportOptionalMemberAccess]
if hasattr(measure_task, "reviewer_persons") and len(measure_task.reviewer_persons) > 0:
members_reviewer = await users_repository.find_all(
search=measure_task.reviewer_persons[0].name, # pyright: ignore [reportOptionalMemberAccess]
sort=sort_by,
Expand All @@ -527,7 +533,7 @@ async def get_measure_task_functions(
if members_reviewer:
measure_task_functions[measure_task.urn].append(members_reviewer[0]) # pyright: ignore [reportOptionalMemberAccess]

if measure_task.responsible_persons: # pyright: ignore [reportOptionalMemberAccess]
if hasattr(measure_task, "responsible_persons") and len(measure_task.responsible_persons) > 0:
members_responsible = await users_repository.find_all(
search=measure_task.responsible_persons[0].name, # pyright: ignore [reportOptionalMemberAccess]
sort=sort_by,
Expand Down Expand Up @@ -821,13 +827,17 @@ async def get_assessment_card(
logger.warning("assessment card not found")
raise AMTNotFound()

editables = get_resolved_editables(context_variables={"algorithm_id": algorithm_id})

context = {
"instrument_state": instrument_state,
"requirements_state": requirements_state,
"assessment_card": assessment_card_data,
"last_edited": algorithm.last_edited,
"sub_menu_items": sub_menu_items,
"breadcrumbs": breadcrumbs,
"algorithm_id": algorithm.id,
"editables": editables,
}

return templates.TemplateResponse(request, "pages/assessment_card.html.j2", context)
Expand Down Expand Up @@ -869,7 +879,10 @@ async def get_model_card(
logger.warning("model card not found")
raise AMTNotFound()

editables = get_resolved_editables(context_variables={"algorithm_id": algorithm_id})

context = {
"base_href": f"/algorithm/{ algorithm_id }",
"instrument_state": instrument_state,
"requirements_state": requirements_state,
"model_card": model_card_data,
Expand All @@ -878,6 +891,7 @@ async def get_model_card(
"algorithm": algorithm,
"algorithm_id": algorithm.id,
"tab_items": tab_items,
"editables": editables,
}

return templates.TemplateResponse(request, "pages/model_card.html.j2", context)
Expand Down
32 changes: 32 additions & 0 deletions amt/api/routes/shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from amt.api.organization_filter_options import OrganizationFilterOptions, get_localized_organization_filter
from amt.api.risk_group import RiskGroup, get_localized_risk_group
from amt.schema.localized_value_item import LocalizedValueItem
from amt.schema.shared import IterMixin


def get_filters_and_sort_by(
Expand Down Expand Up @@ -94,3 +95,34 @@ def nested_enum_value(obj: Any, attr_path: str, language: str) -> Any: # noqa:

class UpdateFieldModel(BaseModel):
value: str


def replace_none_with_empty_string_inplace(obj: dict | list | IterMixin) -> None: # noqa: C901
"""
Recursively replaces all None values within a list, dict,
or an IterMixin (class) object with an empty string.
This function modifies the object in-place.
Args:
obj: The input object, which can be a list, dict, or an IterMixin (class) object.
"""
if isinstance(obj, list):
for i, item in enumerate(obj):
if item is None and isinstance(item, str):
obj[i] = ""
elif isinstance(item, list | dict | IterMixin):
replace_none_with_empty_string_inplace(item)

elif isinstance(obj, dict):
for key, value in obj.items():
if value is None and isinstance(value, str):
obj[key] = ""
elif isinstance(value, (list, dict, IterMixin)): # noqa: UP038
replace_none_with_empty_string_inplace(value)

elif isinstance(obj, IterMixin):
for item in obj:
if isinstance(item, tuple) and item[1] is None:
setattr(obj, item[0], "")
if isinstance(item, list | dict | IterMixin):
replace_none_with_empty_string_inplace(item)
3 changes: 2 additions & 1 deletion amt/clients/clients.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import sys
from enum import StrEnum
from typing import Any

Expand Down Expand Up @@ -54,7 +55,7 @@ async def get_task_by_urn(self, task_type: TaskType, urn: str, version: str = "l
return response_data


@alru_cache
@alru_cache(maxsize=0 if "pytest" in sys.modules else 1000)
async def get_task_by_urn(task_type: TaskType, urn: str, version: str = "latest") -> dict[str, Any]:
client = TaskRegistryAPIClient()
return await client.get_task_by_urn(task_type, urn, version)
5 changes: 3 additions & 2 deletions amt/core/internationalization.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import sys
from datetime import UTC, datetime, timedelta
from functools import lru_cache

Expand All @@ -13,7 +14,7 @@
supported_translations: tuple[str, ...] = ("en", "nl")


@lru_cache(maxsize=len(supported_translations))
@lru_cache(maxsize=0 if "pytest" in sys.modules else len(supported_translations))
def get_dynamic_field_translations(lang: str) -> dict[str, str]:
lang = get_supported_translation(lang)
with open(f"amt/languages/{lang}.yaml") as stream:
Expand All @@ -27,7 +28,7 @@ def get_supported_translation(lang: str) -> str:
return lang


@lru_cache(maxsize=len(supported_translations))
@lru_cache(maxsize=0 if "pytest" in sys.modules else len(supported_translations))
def get_translation(lang: str) -> NullTranslations:
lang = get_supported_translation(lang)
return Translations.load("amt/locale", locales=lang)
Expand Down
8 changes: 6 additions & 2 deletions amt/middleware/authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -
if request.url.path.startswith("/static/"):
return await call_next(request)

auth_disable = False if not os.environ.get("DISABLE_AUTH") else os.environ.get("DISABLE_AUTH").lower() == "true"
if auth_disable:
auto_login_uuid: id = os.environ.get("AUTO_LOGIN_UUID", None)
if auto_login_uuid:
request.session["user"] = {"sub": auto_login_uuid}

authorization_service = AuthorizationService()

user = get_user(request)
Expand All @@ -30,8 +36,6 @@ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -

response = await call_next(request)

auth_disable: bool = bool(os.environ.get("DISABLE_AUTH", False))

if auth_disable:
return response

Expand Down
10 changes: 6 additions & 4 deletions amt/schema/system_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,21 @@ class Owner(BaseModel):


class SystemCard(BaseModel):
version: str = Field(..., description="The version of the schema used")
version: str | None = Field(description="The version of the schema used", default="0.0.0")
provenance: Provenance | None = None
name: str | None = Field(None, description="Name used to describe the system")
instruments: list[InstrumentBase] = Field(default=[])
instruments: list[InstrumentBase] = Field(default_factory=list)
upl: str | None = Field(
None,
description="If this algorithm is part of a product offered by the Dutch Government,"
"it should contain a URI from the Uniform Product List",
)
owners: list[Owner] | None = None
owners: list[Owner] = Field(default_factory=list)
description: str | None = Field(None, description="A short description of the system")
ai_act_profile: AiActProfile | None = None
labels: list[Label] | None = Field(None, description="Labels to store meta information about the system")
labels: list[Label] | None = Field(
default_factory=list, description="Labels to store meta information about the system"
)
status: str | None = Field(None, description="Status of the system")
begin_date: date | None = Field(
None,
Expand Down
3 changes: 2 additions & 1 deletion amt/services/algorithms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import sys
from datetime import datetime
from functools import lru_cache
from os import listdir
Expand Down Expand Up @@ -129,7 +130,7 @@ async def update(self, algorithm: Algorithm) -> Algorithm:
return algorithm


@lru_cache
@lru_cache(maxsize=0 if "pytest" in sys.modules else 256)
def get_template_files() -> dict[str, dict[str, str]]:
return {
str(i): {"display_value": k.split(".")[0].replace("_", " "), "value": k}
Expand Down
22 changes: 17 additions & 5 deletions amt/site/static/scss/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,15 @@ main {
}

/* we override the default ROOS style because we want to display as column, not rows */
.amt-theme .rvo-accordion__item > .rvo-accordion__item-summary {
align-items: initial;
flex-direction: column;
.amt-theme {
& .rvo-accordion__item > .rvo-accordion__item-summary {
align-items: initial;
flex-direction: column;
}

& .rvo-accordion__item-title {
align-items: baseline;
}
}

.amt-avatar-list {
Expand Down Expand Up @@ -469,8 +475,14 @@ main {
}

/** TODO: this is a fix for width: 100% on a margin-left element which should be fixed by ROOS */
.amt-theme .rvo-header__logo-wrapper {
width: auto;
.amt-theme {
& .rvo-header__logo-wrapper {
width: auto;
}

& main {
margin-bottom: var(--rvo-size-2xl);
}
}

/* stylelint-enable */
2 changes: 1 addition & 1 deletion amt/site/templates/algorithms/details_requirements.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
{{ requirement.name }}
</h3>
<div class="rvo-accordion-teaser">
<div class="rvo-layout">{{ requirement.description }}</div>
<div class="rvo-layout rvo-margin-block-start--sm rvo-margin-block-end--sm ">{{ requirement.description }}</div>
<div class="rvo-layout-row rvo-layout-gap--sm">
{% if completed_measures_count == 0 %}
<div class="rvo-status-indicator rvo-status-indicator--hemelblauw"></div>
Expand Down
2 changes: 1 addition & 1 deletion amt/site/templates/macros/editable.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<div hx-target="this">
<div id="error-{{ editable_object.relative_resource_path | replace('/', '_') }}"
class="htmx-error-oob margin-top-middle"></div>
<form hx-put="{{ base_href }}/update/?full_resource_path={{ editable_object.full_resource_path }}"
<form hx-put="{{ base_href }}/update?full_resource_path={{ editable_object.full_resource_path }}"
hx-ext="json-enc"
hx-target-error="#errorContainer"
hx-swap="innerHTML"
Expand Down
6 changes: 4 additions & 2 deletions amt/site/templates/pages/assessment_card.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
<td class="rvo-table-cell">
<strong>{{ key.capitalize().replace("_", " ") }}</strong>
</td>
<td class="rvo-table-cell">{{ render.render_value(key, value,0) }}</td>
<td class="rvo-table-cell">
{{ render.render_value(key, value, 0, "algorithm/" + algorithm_id|string, "/system_card/assessments/FIXME") }}
</td>
</tr>
{% endif %}
{% endfor %}
Expand Down Expand Up @@ -49,7 +51,7 @@
{% for key in keys %}
{% set _x = metadata.__setitem__(key, content[key]) %}
{% endfor %}
{{ render.render_value("",metadata,0) }}
{{ render.render_value("",metadata,0, "algorithm/" + algorithm_id|string + "/FIXME", "/system_card/assessments/FIXME") }}
</td>
</tr>
{% endfor %}
Expand Down
9 changes: 7 additions & 2 deletions amt/site/templates/pages/model_card.html.j2
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
<td class="rvo-table-cell">
<strong>{{ key.capitalize().replace("_", " ") }}</strong>
</td>
<td class="rvo-table-cell">{{ render.render_value(key, value,0) }}</td>
{# TODO: the base path should be the name of the assessment probably.. like system_card/assessments/iama #}
<td class="rvo-table-cell">
{{ render.render_value(key, value, 0, "algorithm/" + algorithm_id|string + "/FIXME", "/system_card") }}
</td>
</tr>
{% endif %}
{% endfor %}
Expand All @@ -44,7 +47,9 @@
<td class="rvo-table-cell">
<strong>{{ key.capitalize().replace("_", " ") }}</strong>
</td>
<td class="rvo-table-cell">{{ render.render_value(key, value,0) }}</td>
<td class="rvo-table-cell">
{{ render.render_value(key, value, 0, "algorithm/" + algorithm_id|string + "/FIXME", "/system_card/models/FIXME") }}
</td>
</tr>
{% endif %}
{% endfor %}
Expand Down
Loading

0 comments on commit 93014bb

Please sign in to comment.