Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove system card mocks #320

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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),
uittenbroekrobbert marked this conversation as resolved.
Show resolved Hide resolved
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
Loading