Skip to content
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
2 changes: 1 addition & 1 deletion api/specs/web-server/_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class _ProjectCreateHeaderParams(BaseModel):
)
async def create_project(
_h: Annotated[_ProjectCreateHeaderParams, Depends()],
_path: Annotated[ProjectCreateQueryParams, Depends()],
_query: Annotated[ProjectCreateQueryParams, Depends()],
_body: ProjectCreateNew | ProjectCopyOverride,
):
...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from ..projects import ClassifierID, DateTimeStr, NodesDict, ProjectID
from ..projects_access import AccessRights, GroupIDStr
from ..projects_state import ProjectState
from ..projects_ui import StudyUI
from ..utils._original_fastapi_encoders import jsonable_encoder
from ..utils.common_validators import (
empty_str_to_none_pre_validator,
Expand All @@ -38,6 +37,7 @@
from ..workspaces import WorkspaceID
from ._base import EmptyModel, InputSchema, OutputSchema
from .permalinks import ProjectPermalink
from .projects_ui import StudyUI


class ProjectCreateNew(InputSchema):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from typing import Annotated, TypeAlias

from pydantic import BaseModel, ConfigDict, Field, PlainSerializer
from pydantic_extra_types.color import Color

from ..projects_nodes_layout import Position

PositionUI: TypeAlias = Position


class MarkerUI(BaseModel):
color: Annotated[Color, PlainSerializer(Color.as_hex), Field(...)]

model_config = ConfigDict(extra="forbid")
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
TypedDict,
)

from .projects_nodes_io import NodeID, NodeIDStr
from ..projects_nodes_io import NodeID, NodeIDStr
from ..utils.common_validators import empty_str_to_none_pre_validator
from .projects_nodes_ui import MarkerUI, PositionUI
from .utils.common_validators import empty_str_to_none_pre_validator


class WorkbenchUI(BaseModel):
Expand Down
187 changes: 104 additions & 83 deletions packages/models-library/src/models_library/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from .projects_nodes import Node
from .projects_nodes_io import NodeIDStr
from .projects_state import ProjectState
from .projects_ui import StudyUI
from .users import UserID
from .utils.common_validators import (
empty_str_to_none_pre_validator,
Expand Down Expand Up @@ -54,33 +53,41 @@ class ProjectType(str, Enum):

class BaseProjectModel(BaseModel):
# Description of the project
uuid: ProjectID = Field(
...,
description="project unique identifier",
examples=[
"07640335-a91f-468c-ab69-a374fa82078d",
"9bcf8feb-c1b1-41b6-b201-639cd6ccdba8",
],
)
name: str = Field(
..., description="project name", examples=["Temporal Distortion Simulator"]
)
description: str = Field(
...,
description="longer one-line description about the project",
examples=["Dabbling in temporal transitions ..."],
)
thumbnail: HttpUrl | None = Field(
...,
description="url of the project thumbnail",
examples=["https://placeimg.com/171/96/tech/grayscale/?0.jpg"],
)
uuid: Annotated[
ProjectID,
Field(
description="project unique identifier",
examples=[
"07640335-a91f-468c-ab69-a374fa82078d",
"9bcf8feb-c1b1-41b6-b201-639cd6ccdba8",
],
),
]

name: Annotated[
str,
Field(description="project name", examples=["Temporal Distortion Simulator"]),
]
description: Annotated[
str,
Field(
description="longer one-line description about the project",
examples=["Dabbling in temporal transitions ..."],
),
]
thumbnail: Annotated[
HttpUrl | None,
Field(
description="url of the project thumbnail",
examples=["https://placeimg.com/171/96/tech/grayscale/?0.jpg"],
),
]

creation_date: datetime = Field(...)
last_change_date: datetime = Field(...)
creation_date: datetime
last_change_date: datetime

# Pipeline of nodes (SEE projects_nodes.py)
workbench: Annotated[NodesDict, Field(..., description="Project's pipeline")]
workbench: Annotated[NodesDict, Field(description="Project's pipeline")]

# validators
_empty_thumbnail_is_none = field_validator("thumbnail", mode="before")(
Expand All @@ -95,15 +102,18 @@ class BaseProjectModel(BaseModel):
class ProjectAtDB(BaseProjectModel):
# Model used to READ from database

id: int = Field(..., description="The table primary index")
id: Annotated[int, Field(description="The table primary index")]

project_type: ProjectType = Field(..., alias="type", description="The project type")
project_type: Annotated[
ProjectType, Field(alias="type", description="The project type")
]

prj_owner: int | None = Field(..., description="The project owner id")
prj_owner: Annotated[int | None, Field(description="The project owner id")]

published: bool | None = Field(
default=False, description="Defines if a study is available publicly"
)
published: Annotated[
bool | None,
Field(default=False, description="Defines if a study is available publicly"),
]

@field_validator("project_type", mode="before")
@classmethod
Expand All @@ -122,76 +132,87 @@ class Project(BaseProjectModel):
# NOT for usage with DB!!

# Ownership and Access (SEE projects_access.py)
prj_owner: LowerCaseEmailStr = Field(
..., description="user email", alias="prjOwner"
)

# Timestamps
creation_date: DateTimeStr = Field( # type: ignore[assignment]
...,
description="project creation date",
examples=["2018-07-01T11:13:43Z"],
alias="creationDate",
)
last_change_date: DateTimeStr = Field( # type: ignore[assignment]
...,
description="last save date",
examples=["2018-07-01T11:13:43Z"],
alias="lastChangeDate",
)
access_rights: dict[GroupIDStr, AccessRights] = Field(
...,
description="object containing the GroupID as key and read/write/execution permissions as value",
alias="accessRights",
)
prj_owner: Annotated[
LowerCaseEmailStr, Field(description="user email", alias="prjOwner")
]
access_rights: Annotated[
dict[GroupIDStr, AccessRights],
Field(
description="object containing the GroupID as key and read/write/execution permissions as value",
alias="accessRights",
),
]

# Classification
tags: list[int] | None = []
classifiers: Annotated[
list[ClassifierID] | None,
# Lifecycle
creation_date: Annotated[ # type: ignore[assignment]
DateTimeStr,
Field(
default_factory=list,
description="Contains the reference to the project classifiers",
examples=["some:id:to:a:classifier"],
description="project creation date",
examples=["2018-07-01T11:13:43Z"],
alias="creationDate",
),
] = DEFAULT_FACTORY
]
last_change_date: Annotated[ # type: ignore[assignment]
DateTimeStr,
Field(
description="last save date",
examples=["2018-07-01T11:13:43Z"],
alias="lastChangeDate",
),
]

# Project state (SEE projects_state.py)
state: ProjectState | None = None

# UI front-end setup (SEE projects_ui.py)
ui: StudyUI | None = None

# Quality
quality: dict[str, Any] = Field(
default_factory=dict,
description="stores the study quality assessment",
)
# UI front-end fields (SEE projects_ui.py)
ui: dict[str, Any] | None = None
dev: dict[str, Any] | None = None

# Dev only
dev: dict | None = Field(
default=None, description="object used for development purposes only"
)
# Parenthood
workspace_id: Annotated[
WorkspaceID | None,
Field(
description="To which workspace project belongs. If None, belongs to private user workspace.",
alias="workspaceId",
),
] = None

workspace_id: WorkspaceID | None = Field(
default=None,
description="To which workspace project belongs. If None, belongs to private user workspace.",
alias="workspaceId",
)
folder_id: FolderID | None = Field(
default=None,
description="To which folder project belongs. If None, belongs to root folder.",
alias="folderId",
)
folder_id: Annotated[
FolderID | None,
Field(
description="To which folder project belongs. If None, belongs to root folder.",
alias="folderId",
),
] = None

# trash state
trashed: datetime | None = None
trashed_by: Annotated[UserID | None, Field(alias="trashedBy")] = None
trashed_by_primary_gid: Annotated[
GroupID | None, Field(alias="trashedByPrimaryGid")
] = None
trashed_explicitly: Annotated[bool, Field(alias="trashedExplicitly")] = False

# Labeling
tags: Annotated[list[int] | None, Field(default_factory=list)] = DEFAULT_FACTORY
classifiers: Annotated[
list[ClassifierID] | None,
Field(
default_factory=list,
description="Contains the reference to the project classifiers",
examples=["some:id:to:a:classifier"],
),
] = DEFAULT_FACTORY
quality: Annotated[
dict[str, Any],
Field(
default_factory=dict,
description="stores the study quality assessment",
),
] = DEFAULT_FACTORY

model_config = ConfigDict(
# NOTE: this is a security measure until we get rid of the ProjectDict variants
extra="forbid",
populate_by_name=True,
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
PortLink,
SimCoreFileLink,
)
from .projects_nodes_ui import PositionUI
from .projects_nodes_layout import Position
from .projects_state import RunningState
from .services import ServiceKey, ServiceVersion

Expand Down Expand Up @@ -224,7 +224,7 @@ class Node(BaseModel):
] = None

position: Annotated[
PositionUI | None,
Position | None,
Field(
deprecated=True,
description="Use projects_ui.WorkbenchUI.position instead",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pydantic import BaseModel, ConfigDict, Field


class Position(BaseModel):
x: int = Field(..., description="The x position", examples=[["12"]])
y: int = Field(..., description="The y position", examples=[["15"]])

model_config = ConfigDict(extra="forbid")
21 changes: 0 additions & 21 deletions packages/models-library/src/models_library/projects_nodes_ui.py

This file was deleted.

2 changes: 1 addition & 1 deletion packages/models-library/tests/test_projects_nodes_ui.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from models_library.projects_nodes_ui import MarkerUI
from models_library.api_schemas_webserver.projects_nodes_ui import MarkerUI
from pydantic_extra_types.color import Color


Expand Down
2 changes: 1 addition & 1 deletion packages/models-library/tests/test_projects_ui.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from models_library.projects_ui import AnnotationUI
from models_library.api_schemas_webserver.projects_ui import AnnotationUI
from pydantic_extra_types.color import Color


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@
from models_library.projects_access import AccessRights
from models_library.projects_nodes import InputTypes, Node, OutputTypes
from models_library.projects_nodes_io import SimCoreFileLink
from models_library.projects_ui import StudyUI

assert AccessRights # nosec
assert InputTypes # nosec
assert Node # nosec
assert OutputTypes # nosec
assert SimCoreFileLink # nosec
assert StudyUI # nosec

__all__: tuple[str, ...] = (
"AccessRights",
"InputTypes",
"Node",
"OutputTypes",
"SimCoreFileLink",
"StudyUI",
)
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@

import arrow
from models_library.api_schemas_webserver.projects import ProjectCreateNew, ProjectGet
from models_library.api_schemas_webserver.projects_ui import StudyUI
from models_library.basic_types import KeyIDStr
from models_library.projects_nodes import InputID
from pydantic import TypeAdapter

from ..models.basic_types import VersionStr
from ..models.domain.projects import InputTypes, Node, SimCoreFileLink, StudyUI
from ..models.domain.projects import InputTypes, Node, SimCoreFileLink
from ..models.schemas.files import File
from ..models.schemas.jobs import (
ArgumentTypes,
Expand Down
Loading
Loading