Skip to content

Commit

Permalink
Add organizations
Browse files Browse the repository at this point in the history
  • Loading branch information
uittenbroekrobbert committed Nov 28, 2024
1 parent a86c1ef commit 9a85e16
Show file tree
Hide file tree
Showing 76 changed files with 2,764 additions and 386 deletions.
6 changes: 5 additions & 1 deletion amt/api/deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from jinja2 import Environment, StrictUndefined, Undefined
from jinja2_base64_filters import jinja2_base64_filters # pyright: ignore #noqa
from starlette.background import BackgroundTask
from starlette.templating import _TemplateResponse # pyright: ignore [reportPrivateUsage]

Expand All @@ -30,6 +31,7 @@
)
from amt.schema.localized_value_item import LocalizedValueItem
from amt.schema.shared import IterMixin
from amt.schema.webform import WebFormFieldType

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

Expand All @@ -38,7 +40,7 @@

def custom_context_processor(
request: Request,
) -> dict[str, str | None | list[str] | dict[str, str] | list[NavigationItem]]:
) -> dict[str, str | None | list[str] | dict[str, str] | list[NavigationItem] | type[WebFormFieldType]]:
lang = get_requested_language(request)
translations = get_current_translation(request)
return {
Expand All @@ -48,6 +50,7 @@ def custom_context_processor(
"translations": get_dynamic_field_translations(lang),
"main_menu_items": get_main_menu(request, translations),
"user": get_user(request),
"WebFormFieldType": WebFormFieldType,
}


Expand Down Expand Up @@ -163,3 +166,4 @@ def instance(obj: Class, type_string: str) -> bool:
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]
templates.env.add_extension("jinja2_base64_filters.Base64Filters") # pyright: ignore [reportUnknownMemberType]
36 changes: 36 additions & 0 deletions amt/api/forms/algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from gettext import NullTranslations
from uuid import UUID

from amt.schema.webform import WebForm, WebFormField, WebFormFieldType, WebFormOption
from amt.services.organizations import OrganizationsService


async def get_algorithm_form(
id: str, translations: NullTranslations, organizations_service: OrganizationsService, user_id: str | UUID | None
) -> WebForm:
_ = translations.gettext

algorithm_form: WebForm = WebForm(id=id, post_url="")

user_id = UUID(user_id) if isinstance(user_id, str) else user_id

my_organizations = await organizations_service.get_organizations_for_user(user_id=user_id)

select_organization: WebFormOption = WebFormOption(value="", display_value=_("Select organization"))

algorithm_form.fields = [
WebFormField(
type=WebFormFieldType.SELECT,
name="organization_id",
label=_("Organization"),
options=[select_organization]
+ [
WebFormOption(value=str(organization.id), display_value=organization.name)
for organization in my_organizations
],
default_value="",
group="1",
),
]

return algorithm_form
38 changes: 38 additions & 0 deletions amt/api/forms/organization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from gettext import NullTranslations

from amt.schema.webform import WebForm, WebFormField, WebFormFieldType, WebFormSearchField, WebFormSubmitButton


def get_organization_form(id: str, translations: NullTranslations) -> WebForm:
_ = translations.gettext

organization_form: WebForm = WebForm(id=id, post_url="/organizations/new")

organization_form.fields = [
WebFormField(
type=WebFormFieldType.TEXT,
name="name",
label=_("Name"),
placeholder=_("Name of the organization"),
attributes={"onkeyup": "amt.generate_slug('" + id + "name', '" + id + "slug')"},
group="1",
),
WebFormField(
type=WebFormFieldType.TEXT,
name="slug",
description=_("The slug is the web path, like /organizations/my-organization-name"),
label=_("Slug"),
placeholder=_("The slug for this organization"),
group="1",
),
WebFormSearchField(
name="user_ids",
label=_("Add users"),
placeholder=_("Search for a user"),
search_url="/organizations/users?returnType=search_select_field",
query_var_name="query",
group="1",
),
WebFormSubmitButton(label=_("Add organization"), group="1", name="submit"),
]
return organization_form
5 changes: 3 additions & 2 deletions amt/api/main.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from fastapi import APIRouter

from amt.api.routes import algorithm, algorithms, auth, health, pages, root
from amt.api.routes import algorithm, algorithms, auth, health, organizations, pages, root

api_router = APIRouter()
api_router: APIRouter = APIRouter()
api_router.include_router(root.router)
api_router.include_router(health.router, prefix="/health", tags=["health"])
api_router.include_router(pages.router, prefix="/pages", tags=["pages"])
api_router.include_router(algorithms.router, prefix="/algorithms", tags=["algorithms"])
api_router.include_router(algorithm.router, prefix="/algorithm", tags=["algorithm"])
api_router.include_router(auth.router, prefix="/auth", tags=["auth"])
api_router.include_router(organizations.router, prefix="/organizations", tags=["organizations"])
18 changes: 18 additions & 0 deletions amt/api/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class DisplayText(Enum):
ASSESSMENTCARD = "assessmentcard"
MODELCARD = "modelcard"
DETAILS = "details"
ORGANIZATIONS = "organizations"
PEOPLE = "people"


def get_translation(key: DisplayText, translations: NullTranslations) -> str:
Expand Down Expand Up @@ -57,6 +59,9 @@ def get_translation(key: DisplayText, translations: NullTranslations) -> str:
DisplayText.DATA: _("Data"),
DisplayText.MODEL: _("Model"),
DisplayText.INSTRUMENTS: _("Instruments"),
DisplayText.ORGANIZATIONS: _("Organizations"),
DisplayText.ALGORITHMS: _("Algorithms"),
DisplayText.PEOPLE: _("People"),
}
return keys[key]

Expand Down Expand Up @@ -128,6 +133,18 @@ class Navigation:
ALGORITHM_INSTRUMENTS = BaseNavigationItem(
display_text=DisplayText.INSTRUMENTS, url="/algorithm/{algorithm_id}/details/system_card/instruments"
)
ORGANIZATIONS_ROOT = BaseNavigationItem(
display_text=DisplayText.ORGANIZATIONS, url="/organizations", icon="rvo-icon-man-torso-voor-hoogbouw"
)
ORGANIZATIONS_NEW = BaseNavigationItem(display_text=DisplayText.NEW, url="/organizations/new")
ORGANIZATIONS_OVERVIEW = BaseNavigationItem(display_text=DisplayText.OVERVIEW, url="/organizations/")
ORGANIZATIONS_INFO = BaseNavigationItem(display_text=DisplayText.INFO, url="/organizations/{organization_slug}")
ORGANIZATIONS_ALGORITHMS = BaseNavigationItem(
display_text=DisplayText.ALGORITHMS, url="/organizations/{organization_slug}/algorithms"
)
ORGANIZATIONS_PEOPLE = BaseNavigationItem(
display_text=DisplayText.PEOPLE, url="/organizations/{organization_slug}/people"
)


class NavigationItem:
Expand Down Expand Up @@ -242,6 +259,7 @@ def get_main_menu(request: Request, translations: NullTranslations) -> list[Navi
# main menu items are the same for all pages
main_menu_items = [
NavigationItem(Navigation.ALGORITHMS_ROOT, translations=translations),
NavigationItem(Navigation.ORGANIZATIONS_ROOT, translations=translations),
]

return _mark_active_navigation_item(main_menu_items, request.url.path)
Expand Down
27 changes: 27 additions & 0 deletions amt/api/organization_filter_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from collections.abc import Callable

from fastapi import Request

from ..schema.localized_value_item import LocalizedValueItem
from .localizable import LocalizableEnum, get_localized_enum, get_localized_enums


class OrganizationFilterOptions(LocalizableEnum):
ALL = "ALL"
MY_ORGANIZATIONS = "MY_ORGANIZATIONS"

@classmethod
def get_display_values(
cls: type["OrganizationFilterOptions"], _: Callable[[str], str]
) -> dict["OrganizationFilterOptions", str]:
return {cls.ALL: _("All organizations"), cls.MY_ORGANIZATIONS: _("My organizations")}


def get_localized_organization_filter(
key: OrganizationFilterOptions | None, request: Request
) -> LocalizedValueItem | None:
return get_localized_enum(key, request)


def get_localized_organization_filters(request: Request) -> list[LocalizedValueItem | None]:
return get_localized_enums(OrganizationFilterOptions, request)
64 changes: 56 additions & 8 deletions amt/api/routes/algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,21 @@
resolve_base_navigation_items,
resolve_navigation_items,
)
from amt.core.authorization import get_user
from amt.core.exceptions import AMTNotFound, AMTRepositoryError
from amt.enums.status import Status
from amt.models import Algorithm
from amt.models.task import Task
from amt.repositories.organizations import OrganizationsRepository
from amt.schema.measure import ExtendedMeasureTask, MeasureTask
from amt.schema.requirement import RequirementTask
from amt.schema.system_card import SystemCard
from amt.schema.system_card import Owner, SystemCard
from amt.schema.task import MovedTask
from amt.schema.webform import WebFormOption
from amt.services.algorithms import AlgorithmsService
from amt.services.instruments_and_requirements_state import InstrumentStateService, RequirementsStateService
from amt.services.measures import MeasuresService, create_measures_service
from amt.services.organizations import OrganizationsService
from amt.services.requirements import RequirementsService, create_requirements_service
from amt.services.tasks import TasksService

Expand Down Expand Up @@ -201,6 +205,7 @@ async def get_algorithm_details(
)

context["breadcrumbs"] = breadcrumbs
context["base_href"] = f"/algorithm/{ algorithm_id }"

return templates.TemplateResponse(request, "algorithms/details_info.html.j2", context)

Expand All @@ -210,10 +215,30 @@ async def get_algorithm_edit(
request: Request,
algorithm_id: int,
algorithms_service: Annotated[AlgorithmsService, Depends(AlgorithmsService)],
organizations_service: Annotated[OrganizationsService, Depends(OrganizationsService)],
path: str,
edit_type: str = "systemcard",
) -> HTMLResponse:
_, context = await get_algorithm_context(algorithm_id, algorithms_service, request)
context["path"] = path.replace("/", ".")
algorithm, context = await get_algorithm_context(algorithm_id, algorithms_service, request)
context.update(
{
"path": path.replace("/", "."),
"edit_type": edit_type,
"object": algorithm,
"base_href": f"/algorithm/{ algorithm_id }",
}
)

if edit_type == "select_my_organizations":
user = get_user(request)

my_organizations = await organizations_service.get_organizations_for_user(user_id=user["sub"] if user else None)

context["select_options"] = [
WebFormOption(value=str(organization.id), display_value=organization.name)
for organization in my_organizations
]

return templates.TemplateResponse(request, "parts/edit_cell.html.j2", context)


Expand All @@ -223,9 +248,17 @@ async def get_algorithm_cancel(
algorithm_id: int,
algorithms_service: Annotated[AlgorithmsService, Depends(AlgorithmsService)],
path: str,
edit_type: str = "systemcard",
) -> HTMLResponse:
_, context = await get_algorithm_context(algorithm_id, algorithms_service, request)
context["path"] = path.replace("/", ".")
algorithm, context = await get_algorithm_context(algorithm_id, algorithms_service, request)
context.update(
{
"path": path.replace("/", "."),
"edit_type": edit_type,
"base_href": f"/algorithm/{ algorithm_id }",
"object": algorithm,
}
)
return templates.TemplateResponse(request, "parts/view_cell.html.j2", context)


Expand Down Expand Up @@ -261,13 +294,28 @@ async def get_algorithm_update(
request: Request,
algorithm_id: int,
algorithms_service: Annotated[AlgorithmsService, Depends(AlgorithmsService)],
organizations_repository: Annotated[OrganizationsRepository, Depends(OrganizationsRepository)],
update_data: UpdateFieldModel,
path: str,
edit_type: str = "systemcard",
) -> HTMLResponse:
algorithm, context = await get_algorithm_context(algorithm_id, algorithms_service, request)
set_path(algorithm, path, update_data.value)
await algorithms_service.update(algorithm)
context["path"] = path.replace("/", ".")
context.update(
{"path": path.replace("/", "."), "edit_type": edit_type, "base_href": f"/algorithm/{ algorithm_id }"}
)

if edit_type == "select_my_organizations":
organization = await organizations_repository.find_by_id(int(update_data.value))
algorithm.organization = organization
# TODO: we need to know which organization to update and what to remove
if not algorithm.system_card.owners:
algorithm.system_card.owners = [Owner(organization=organization.name, oin=str(organization.id))]
algorithm.system_card.owners[0].organization = organization.name
else:
set_path(algorithm, path, update_data.value)

algorithm = await algorithms_service.update(algorithm)
context.update({"object": algorithm})
return templates.TemplateResponse(request, "parts/view_cell.html.j2", context)


Expand Down
Loading

0 comments on commit 9a85e16

Please sign in to comment.