From 13a816c33a8805816a28d072b52778ae1a1be58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien?= Date: Thu, 22 May 2025 17:15:30 +0200 Subject: [PATCH 1/5] Add validation results in MEModelRead --- app/db/model.py | 119 +++++++++++++++++++++++----------------- app/schemas/me_model.py | 2 + 2 files changed, 70 insertions(+), 51 deletions(-) diff --git a/app/db/model.py b/app/db/model.py index 2612fbbf..9d30c896 100644 --- a/app/db/model.py +++ b/app/db/model.py @@ -303,6 +303,73 @@ def etypes(cls) -> Mapped[list["ETypeClass"]]: ) +class Entity(LegacyMixin, Identifiable): + __tablename__ = "entity" + + type: Mapped[EntityType] + annotations = relationship("Annotation", back_populates="entity") + + # TODO: keep the _ ? put on agent ? + created_by = relationship("Agent", uselist=False, foreign_keys="Entity.created_by_id") + # TODO: move to mandatory + created_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) + updated_by = relationship("Agent", uselist=False, foreign_keys="Entity.updated_by_id") + # TODO: move to mandatory + updated_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) + + authorized_project_id: Mapped[uuid.UUID] + authorized_public: Mapped[bool] = mapped_column(default=False) + + contributions: Mapped[list["Contribution"]] = relationship(uselist=True, viewonly=True) + assets: Mapped[list["Asset"]] = relationship( + "Asset", + foreign_keys="Asset.entity_id", + uselist=True, + viewonly=True, + ) + + __mapper_args__ = { # noqa: RUF012 + "polymorphic_identity": __tablename__, + "polymorphic_on": "type", + } + + +class ValidationResult(Entity): + __tablename__ = EntityType.validation_result.value + id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) + passed: Mapped[bool] = mapped_column(default=False) + + name: Mapped[str] = mapped_column(index=True) + + validated_entity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), index=True) + validated_entity: Mapped[Entity] = relationship( + "Entity", + uselist=False, + foreign_keys=[validated_entity_id], + ) + + __mapper_args__ = { # noqa: RUF012 + "polymorphic_identity": __tablename__, + "inherit_condition": id == Entity.id, + } + +class ValidationResultsMixin: + @declared_attr + @classmethod + def validation_results(cls) -> Mapped[list["ValidationResult"]]: + if not issubclass(cls, Entity): + msg = f"{cls} should be an Entity" + raise TypeError(msg) + + return relationship( + "ValidationResult", + primaryjoin=f"foreign(ValidationResult.validated_entity_id) == {cls.__name__}.id", + foreign_keys="[ValidationResult.validated_entity_id]", + cascade="all, delete-orphan", + lazy="dynamic" # or "select", as needed + ) + + class DataMaturityAnnotationBody(AnnotationBody): __tablename__ = AnnotationBodyType.datamaturity_annotation_body.value id: Mapped[uuid.UUID] = mapped_column(ForeignKey("annotation_body.id"), primary_key=True) @@ -342,37 +409,6 @@ def __table_args__(cls): # noqa: D105, PLW3201 ) -class Entity(LegacyMixin, Identifiable): - __tablename__ = "entity" - - type: Mapped[EntityType] - annotations = relationship("Annotation", back_populates="entity") - - # TODO: keep the _ ? put on agent ? - created_by = relationship("Agent", uselist=False, foreign_keys="Entity.created_by_id") - # TODO: move to mandatory - created_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) - updated_by = relationship("Agent", uselist=False, foreign_keys="Entity.updated_by_id") - # TODO: move to mandatory - updated_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) - - authorized_project_id: Mapped[uuid.UUID] - authorized_public: Mapped[bool] = mapped_column(default=False) - - contributions: Mapped[list["Contribution"]] = relationship(uselist=True, viewonly=True) - assets: Mapped[list["Asset"]] = relationship( - "Asset", - foreign_keys="Asset.entity_id", - uselist=True, - viewonly=True, - ) - - __mapper_args__ = { # noqa: RUF012 - "polymorphic_identity": __tablename__, - "polymorphic_on": "type", - } - - class Subject(NameDescriptionVectorMixin, SpeciesMixin, Entity): __tablename__ = EntityType.subject.value id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) @@ -471,7 +507,7 @@ class Mesh(LocationMixin, NameDescriptionVectorMixin, Entity): class MEModel( - MTypesMixin, ETypesMixin, SpeciesMixin, LocationMixin, NameDescriptionVectorMixin, Entity + ValidationResultsMixin, MTypesMixin, ETypesMixin, SpeciesMixin, LocationMixin, NameDescriptionVectorMixin, Entity ): __tablename__ = EntityType.memodel.value id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) @@ -821,25 +857,6 @@ class IonChannelModelToEModel(Base): ) -class ValidationResult(Entity): - __tablename__ = EntityType.validation_result.value - id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) - passed: Mapped[bool] = mapped_column(default=False) - - name: Mapped[str] = mapped_column(index=True) - - validated_entity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), index=True) - validated_entity: Mapped[Entity] = relationship( - "Entity", - uselist=False, - foreign_keys=[validated_entity_id], - ) - - __mapper_args__ = { # noqa: RUF012 - "polymorphic_identity": __tablename__, - "inherit_condition": id == Entity.id, - } - class Asset(Identifiable): """Asset table.""" diff --git a/app/schemas/me_model.py b/app/schemas/me_model.py index f407c443..a392a582 100644 --- a/app/schemas/me_model.py +++ b/app/schemas/me_model.py @@ -18,6 +18,7 @@ from app.schemas.contribution import ContributionReadWithoutEntity from app.schemas.emodel import EModelRead from app.schemas.morphology import ReconstructionMorphologyRead +from app.schemas.validation import ValidationResultRead class MEModelBase(BaseModel): @@ -59,3 +60,4 @@ class MEModelRead( etypes: list[ETypeClassRead] | None morphology: ReconstructionMorphologyRead emodel: EModelRead + validation_results: list[ValidationResultRead] | None = None From 8e90511f8048e28de67bb89be11b628555265d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien?= Date: Thu, 22 May 2025 17:18:31 +0200 Subject: [PATCH 2/5] lint fix --- app/db/model.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/db/model.py b/app/db/model.py index 9d30c896..1d4949c8 100644 --- a/app/db/model.py +++ b/app/db/model.py @@ -353,6 +353,7 @@ class ValidationResult(Entity): "inherit_condition": id == Entity.id, } + class ValidationResultsMixin: @declared_attr @classmethod @@ -366,7 +367,7 @@ def validation_results(cls) -> Mapped[list["ValidationResult"]]: primaryjoin=f"foreign(ValidationResult.validated_entity_id) == {cls.__name__}.id", foreign_keys="[ValidationResult.validated_entity_id]", cascade="all, delete-orphan", - lazy="dynamic" # or "select", as needed + lazy="dynamic", # or "select", as needed ) @@ -507,7 +508,13 @@ class Mesh(LocationMixin, NameDescriptionVectorMixin, Entity): class MEModel( - ValidationResultsMixin, MTypesMixin, ETypesMixin, SpeciesMixin, LocationMixin, NameDescriptionVectorMixin, Entity + ValidationResultsMixin, + MTypesMixin, + ETypesMixin, + SpeciesMixin, + LocationMixin, + NameDescriptionVectorMixin, + Entity, ): __tablename__ = EntityType.memodel.value id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) @@ -857,7 +864,6 @@ class IonChannelModelToEModel(Base): ) - class Asset(Identifiable): """Asset table.""" From d6d798de3d317f70598667ededf44f269a8ca00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien?= Date: Fri, 23 May 2025 09:15:08 +0200 Subject: [PATCH 3/5] move entity and validationresult back to their place --- app/db/model.py | 101 ++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/app/db/model.py b/app/db/model.py index 1d4949c8..246820fd 100644 --- a/app/db/model.py +++ b/app/db/model.py @@ -303,56 +303,6 @@ def etypes(cls) -> Mapped[list["ETypeClass"]]: ) -class Entity(LegacyMixin, Identifiable): - __tablename__ = "entity" - - type: Mapped[EntityType] - annotations = relationship("Annotation", back_populates="entity") - - # TODO: keep the _ ? put on agent ? - created_by = relationship("Agent", uselist=False, foreign_keys="Entity.created_by_id") - # TODO: move to mandatory - created_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) - updated_by = relationship("Agent", uselist=False, foreign_keys="Entity.updated_by_id") - # TODO: move to mandatory - updated_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) - - authorized_project_id: Mapped[uuid.UUID] - authorized_public: Mapped[bool] = mapped_column(default=False) - - contributions: Mapped[list["Contribution"]] = relationship(uselist=True, viewonly=True) - assets: Mapped[list["Asset"]] = relationship( - "Asset", - foreign_keys="Asset.entity_id", - uselist=True, - viewonly=True, - ) - - __mapper_args__ = { # noqa: RUF012 - "polymorphic_identity": __tablename__, - "polymorphic_on": "type", - } - - -class ValidationResult(Entity): - __tablename__ = EntityType.validation_result.value - id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) - passed: Mapped[bool] = mapped_column(default=False) - - name: Mapped[str] = mapped_column(index=True) - - validated_entity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), index=True) - validated_entity: Mapped[Entity] = relationship( - "Entity", - uselist=False, - foreign_keys=[validated_entity_id], - ) - - __mapper_args__ = { # noqa: RUF012 - "polymorphic_identity": __tablename__, - "inherit_condition": id == Entity.id, - } - class ValidationResultsMixin: @declared_attr @@ -410,6 +360,37 @@ def __table_args__(cls): # noqa: D105, PLW3201 ) +class Entity(LegacyMixin, Identifiable): + __tablename__ = "entity" + + type: Mapped[EntityType] + annotations = relationship("Annotation", back_populates="entity") + + # TODO: keep the _ ? put on agent ? + created_by = relationship("Agent", uselist=False, foreign_keys="Entity.created_by_id") + # TODO: move to mandatory + created_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) + updated_by = relationship("Agent", uselist=False, foreign_keys="Entity.updated_by_id") + # TODO: move to mandatory + updated_by_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("agent.id"), index=True) + + authorized_project_id: Mapped[uuid.UUID] + authorized_public: Mapped[bool] = mapped_column(default=False) + + contributions: Mapped[list["Contribution"]] = relationship(uselist=True, viewonly=True) + assets: Mapped[list["Asset"]] = relationship( + "Asset", + foreign_keys="Asset.entity_id", + uselist=True, + viewonly=True, + ) + + __mapper_args__ = { # noqa: RUF012 + "polymorphic_identity": __tablename__, + "polymorphic_on": "type", + } + + class Subject(NameDescriptionVectorMixin, SpeciesMixin, Entity): __tablename__ = EntityType.subject.value id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) @@ -864,6 +845,26 @@ class IonChannelModelToEModel(Base): ) +class ValidationResult(Entity): + __tablename__ = EntityType.validation_result.value + id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), primary_key=True) + passed: Mapped[bool] = mapped_column(default=False) + + name: Mapped[str] = mapped_column(index=True) + + validated_entity_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("entity.id"), index=True) + validated_entity: Mapped[Entity] = relationship( + "Entity", + uselist=False, + foreign_keys=[validated_entity_id], + ) + + __mapper_args__ = { # noqa: RUF012 + "polymorphic_identity": __tablename__, + "inherit_condition": id == Entity.id, + } + + class Asset(Identifiable): """Asset table.""" From 8a6dda8eae64be766c3a9bcc61c7a483da741448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien?= Date: Fri, 23 May 2025 09:16:28 +0200 Subject: [PATCH 4/5] lint fix --- app/db/model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/db/model.py b/app/db/model.py index 246820fd..bd984ce3 100644 --- a/app/db/model.py +++ b/app/db/model.py @@ -303,7 +303,6 @@ def etypes(cls) -> Mapped[list["ETypeClass"]]: ) - class ValidationResultsMixin: @declared_attr @classmethod From c0b74f5900c5a64d6fb7172eba293512f547714d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien?= Date: Tue, 27 May 2025 13:20:19 +0200 Subject: [PATCH 5/5] various fixes --- app/db/model.py | 4 ++-- app/schemas/me_model.py | 2 +- app/service/memodel.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/db/model.py b/app/db/model.py index bd984ce3..f7a65f31 100644 --- a/app/db/model.py +++ b/app/db/model.py @@ -315,8 +315,8 @@ def validation_results(cls) -> Mapped[list["ValidationResult"]]: "ValidationResult", primaryjoin=f"foreign(ValidationResult.validated_entity_id) == {cls.__name__}.id", foreign_keys="[ValidationResult.validated_entity_id]", - cascade="all, delete-orphan", - lazy="dynamic", # or "select", as needed + cascade="all, delete", + uselist=True, ) diff --git a/app/schemas/me_model.py b/app/schemas/me_model.py index a392a582..da78ca3d 100644 --- a/app/schemas/me_model.py +++ b/app/schemas/me_model.py @@ -60,4 +60,4 @@ class MEModelRead( etypes: list[ETypeClassRead] | None morphology: ReconstructionMorphologyRead emodel: EModelRead - validation_results: list[ValidationResultRead] | None = None + validation_results: list[ValidationResultRead] = [] diff --git a/app/service/memodel.py b/app/service/memodel.py index d4ee0cfd..820569c7 100644 --- a/app/service/memodel.py +++ b/app/service/memodel.py @@ -70,6 +70,7 @@ def _load(select: Select): joinedload(MEModel.etypes), joinedload(MEModel.created_by), joinedload(MEModel.updated_by), + selectinload(MEModel.validation_results), raiseload("*"), )