Skip to content

Commit

Permalink
Remove system card mocks (#320)
Browse files Browse the repository at this point in the history
  • Loading branch information
uittenbroekrobbert authored Oct 31, 2024
2 parents 4773cc7 + 89dbe39 commit 7d8edf4
Show file tree
Hide file tree
Showing 38 changed files with 2,068 additions and 1,131 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ RUN pyright
FROM development AS test

COPY ./example/ ./example/
COPY ./example_system_card/ ./example_system_card/
COPY ./resources/ ./resources/
COPY ./example_registry/ ./example_registry/
RUN npm run build

Expand All @@ -81,7 +81,7 @@ USER amt
COPY --chown=root:root --chmod=755 amt /app/amt
COPY --chown=root:root --chmod=755 alembic.ini /app/alembic.ini
COPY --chown=root:root --chmod=755 prod.env /app/.env
COPY --chown=root:root --chmod=755 example_system_card /app/example_system_card
COPY --chown=root:root --chmod=755 resources /app/resources
COPY --chown=root:root --chmod=755 example_registry /app/example_registry
COPY --chown=root:root --chmod=755 LICENSE /app/LICENSE
COPY --chown=amt:amt --chmod=755 docker-entrypoint.sh /app/docker-entrypoint.sh
Expand Down
17 changes: 17 additions & 0 deletions amt/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from collections.abc import Sequence
from enum import Enum
from os import PathLike
from pyclbr import Class
from typing import Any, AnyStr, TypeVar

from fastapi import Request
Expand All @@ -28,6 +29,7 @@
time_ago,
)
from amt.schema.localized_value_item import LocalizedValueItem
from amt.schema.shared import IterMixin

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

Expand Down Expand Up @@ -135,6 +137,20 @@ def Redirect(self, request: Request, url: str) -> HTMLResponse:
return self.TemplateResponse(request, "redirect.html.j2", headers=headers)


def instance(obj: Class, type_string: str) -> bool:
match type_string:
case "str":
return isinstance(obj, str)
case "list":
return isinstance(obj, list)
case "IterMixin":
return isinstance(obj, IterMixin)
case "dict":
return isinstance(obj, dict)
case _:
raise TypeError("Unsupported type: " + type_string)


templates = LocaleJinja2Templates(
directory="amt/site/templates/", context_processors=[custom_context_processor], undefined=get_undefined_behaviour()
)
Expand All @@ -146,3 +162,4 @@ def Redirect(self, request: Request, url: str) -> HTMLResponse:
templates.env.globals.update(is_nested_enum=is_nested_enum) # pyright: ignore [reportUnknownMemberType]
templates.env.globals.update(nested_enum=nested_enum) # pyright: ignore [reportUnknownMemberType]
templates.env.globals.update(nested_enum_value=nested_enum_value) # pyright: ignore [reportUnknownMemberType]
templates.env.globals.update(isinstance=instance) # pyright: ignore [reportUnknownMemberType]
97 changes: 30 additions & 67 deletions amt/api/routes/project.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import asyncio
import functools
import logging
from collections.abc import Sequence
from pathlib import Path
from typing import Annotated, Any, cast

from fastapi import APIRouter, Depends, Request
Expand All @@ -28,26 +26,15 @@
from amt.services import task_registry
from amt.services.instruments_and_requirements_state import InstrumentStateService, RequirementsStateService
from amt.services.projects import ProjectsService
from amt.services.storage import StorageFactory
from amt.services.task_registry import fetch_measures, fetch_requirements
from amt.services.tasks import TasksService
from amt.utils.storage import get_include_content

router = APIRouter()
logger = logging.getLogger(__name__)


def get_system_card_data() -> SystemCard:
# TODO: This now loads an example system card independent of the project ID.
path = Path("example_system_card/system_card.yaml")
system_card: Any = StorageFactory.init(storage_type="file", location=path.parent, filename=path.name).read()
return SystemCard(**system_card)


@functools.lru_cache
def get_instrument_state() -> dict[str, Any]:
system_card_data = get_system_card_data()
instrument_state = InstrumentStateService(system_card_data)
def get_instrument_state(system_card: SystemCard) -> dict[str, Any]:
instrument_state = InstrumentStateService(system_card)
instrument_states = instrument_state.get_state_per_instrument()
return {
"instrument_states": instrument_states,
Expand Down Expand Up @@ -117,7 +104,7 @@ async def get_tasks(
tasks_service: Annotated[TasksService, Depends(TasksService)],
) -> HTMLResponse:
project = await get_project_or_error(project_id, projects_service, request)
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)
tab_items = get_project_details_tabs(request)
tasks_by_status = await gather_project_tasks(project_id, task_service=tasks_service)
Expand Down Expand Up @@ -181,14 +168,12 @@ async def get_project_context(
project_id: int, projects_service: ProjectsService, request: Request
) -> tuple[Project, dict[str, Any]]:
project = await get_project_or_error(project_id, projects_service, request)
system_card_data = get_system_card_data()
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)
tab_items = get_project_details_tabs(request)
project.system_card = system_card_data
return project, {
"last_edited": project.last_edited,
"system_card": system_card_data,
"system_card": project.system_card,
"instrument_state": instrument_state,
"requirements_state": requirements_state,
"project": project,
Expand Down Expand Up @@ -285,19 +270,14 @@ async def get_project_update(
return templates.TemplateResponse(request, "parts/view_cell.html.j2", context)


# !!!
# Implementation of this endpoint is for now independent of the project ID, meaning
# that the same system card is rendered for all project ID's. This is due to the fact
# that the logical process flow of a system card is not complete.
# !!!
@router.get("/{project_id}/details/system_card")
async def get_system_card(
request: Request,
project_id: int,
projects_service: Annotated[ProjectsService, Depends(ProjectsService)],
) -> HTMLResponse:
project = await get_project_or_error(project_id, projects_service, request)
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)

tab_items = get_project_details_tabs(request)
Expand All @@ -313,15 +293,8 @@ async def get_system_card(
request,
)

# TODO: This now loads an example system card independent of the project ID.
filepath = Path("example_system_card/system_card.yaml")
file_system_storage_service = StorageFactory.init(
storage_type="file", location=filepath.parent, filename=filepath.name
)
system_card_data = file_system_storage_service.read()

context = {
"system_card": system_card_data,
"system_card": project.system_card,
"instrument_state": instrument_state,
"requirements_state": requirements_state,
"last_edited": project.last_edited,
Expand Down Expand Up @@ -351,15 +324,14 @@ async def get_project_inference(
request,
)

system_card_data = get_system_card_data()
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)

tab_items = get_project_details_tabs(request)

context = {
"last_edited": project.last_edited,
"system_card": system_card_data,
"system_card": project.system_card,
"instrument_state": instrument_state,
"requirements_state": requirements_state,
"project": project,
Expand All @@ -371,21 +343,15 @@ async def get_project_inference(
return templates.TemplateResponse(request, "projects/details_inference.html.j2", context)


# !!!
# Implementation of this endpoint is for now independent of the project ID, meaning
# that the same system card is rendered for all project ID's. This is due to the fact
# that the logical process flow of a system card is not complete.
# !!!
@router.get("/{project_id}/details/system_card/requirements")
async def get_system_card_requirements(
request: Request,
project_id: int,
projects_service: Annotated[ProjectsService, Depends(ProjectsService)],
) -> HTMLResponse:
project = await get_project_or_error(project_id, projects_service, request)
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)
# TODO: This tab is fairly slow, fix in later releases
tab_items = get_project_details_tabs(request)

breadcrumbs = resolve_base_navigation_items(
Expand All @@ -399,11 +365,7 @@ async def get_system_card_requirements(
request,
)

# TODO: This is only for the demo of 18 Oct. In reality one would load the requirements from the requirement
# field in the system card, but one would load the AI Act Profile and determine the requirements from
# the labels in this field.
system_card = project.system_card
requirements = fetch_requirements([requirement.urn for requirement in system_card.requirements])
requirements = fetch_requirements([requirement.urn for requirement in project.system_card.requirements])

# Get measures that correspond to the requirements and merge them with the measuretasks
requirements_and_measures = []
Expand All @@ -412,7 +374,7 @@ async def get_system_card_requirements(
linked_measures = fetch_measures(requirement.links)
extended_linked_measures: list[ExtendedMeasureTask] = []
for measure in linked_measures:
measure_task = find_measure_task(system_card, measure.urn)
measure_task = find_measure_task(project.system_card, measure.urn)
if measure_task:
ext_measure_task = ExtendedMeasureTask(
name=measure.name,
Expand Down Expand Up @@ -546,7 +508,7 @@ async def get_system_card_data_page(
projects_service: Annotated[ProjectsService, Depends(ProjectsService)],
) -> HTMLResponse:
project = await get_project_or_error(project_id, projects_service, request)
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)

tab_items = get_project_details_tabs(request)
Expand Down Expand Up @@ -586,7 +548,7 @@ async def get_system_card_instruments(
projects_service: Annotated[ProjectsService, Depends(ProjectsService)],
) -> HTMLResponse:
project = await get_project_or_error(project_id, projects_service, request)
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)

tab_items = get_project_details_tabs(request)
Expand Down Expand Up @@ -614,11 +576,6 @@ async def get_system_card_instruments(
return templates.TemplateResponse(request, "projects/details_instruments.html.j2", context)


# !!!
# Implementation of this endpoint is for now independent of the project ID, meaning
# that the same system card is rendered for all project ID's. This is due to the fact
# that the logical process flow of a system card is not complete.
# !!!
@router.get("/{project_id}/details/system_card/assessments/{assessment_card}")
async def get_assessment_card(
request: Request,
Expand All @@ -627,7 +584,7 @@ async def get_assessment_card(
projects_service: Annotated[ProjectsService, Depends(ProjectsService)],
) -> HTMLResponse:
project = await get_project_or_error(project_id, projects_service, request)
instrument_state = get_instrument_state()
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)

request.state.path_variables.update({"assessment_card": assessment_card})
Expand All @@ -645,9 +602,10 @@ async def get_assessment_card(
request,
)

# TODO: This now loads an example system card independent of the project ID.
filepath = Path("example_system_card/system_card.yaml")
assessment_card_data = get_include_content(filepath.parent, filepath.name, "assessments", assessment_card)
assessment_card_data = next(
(assessment for assessment in project.system_card.assessments if assessment.name.lower() == assessment_card),
None,
)

if not assessment_card_data:
logger.warning("assessment card not found")
Expand Down Expand Up @@ -678,13 +636,9 @@ async def get_model_card(
projects_service: Annotated[ProjectsService, Depends(ProjectsService)],
) -> HTMLResponse:
project = await get_project_or_error(project_id, projects_service, request)
instrument_state = get_instrument_state()
requirements_state = get_requirements_state(project.system_card)

# TODO: This now loads an example system card independent of the project ID.
filepath = Path("example_system_card/system_card.yaml")
model_card_data = get_include_content(filepath.parent, filepath.name, "models", model_card)
request.state.path_variables.update({"model_card": model_card})
instrument_state = get_instrument_state(project.system_card)
requirements_state = get_requirements_state(project.system_card)

tab_items = get_project_details_tabs(request)

Expand All @@ -699,6 +653,15 @@ async def get_model_card(
request,
)

model_card_data = next(
(
model
for model in project.system_card.models
if (model.name is not None and model.name.lower() == model_card)
),
None,
)

if not model_card_data:
logger.warning("model card not found")
raise AMTNotFound()
Expand Down
14 changes: 4 additions & 10 deletions amt/api/routes/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from amt.schema.localized_value_item import LocalizedValueItem
from amt.schema.project import ProjectNew
from amt.services.instruments import InstrumentsService
from amt.services.projects import ProjectsService
from amt.services.projects import ProjectsService, get_template_files

router = APIRouter()
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -130,12 +130,15 @@ async def get_new(

ai_act_profile = get_ai_act_profile_selector(request)

template_files = get_template_files()

context: dict[str, Any] = {
"instruments": instrument_service.fetch_instruments(),
"ai_act_profile": ai_act_profile,
"breadcrumbs": breadcrumbs,
"sub_menu_items": {}, # sub_menu_items disabled for now,
"lifecycles": get_localized_lifecycles(request),
"template_files": template_files,
}

response = templates.TemplateResponse(request, "projects/new.html.j2", context)
Expand All @@ -148,15 +151,6 @@ async def post_new(
project_new: ProjectNew,
projects_service: Annotated[ProjectsService, Depends(ProjectsService)],
) -> HTMLResponse:
# TODO: FOR DEMO 18 OCT
# Override AI Act Profile for demo purposes to values:
project_new.type = "AI-systeem"
project_new.publication_category = "hoog-risico AI"
project_new.transparency_obligations = "geen transparantieverplichtingen"
project_new.role = "gebruiksverantwoordelijke"
project_new.systemic_risk = "geen systeemrisico"
project_new.open_source = "open-source"

project = await projects_service.create(project_new)
response = templates.Redirect(request, f"/algorithm-system/{project.id}/details/tasks")
return response
Loading

0 comments on commit 7d8edf4

Please sign in to comment.