Skip to content
Draft
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
6 changes: 3 additions & 3 deletions components/lif/mdr_dto/transformation_dto.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from pydantic import BaseModel
from typing import List, Optional
from datetime import datetime
from typing import List, Optional

from lif.datatypes.mdr_sql_model import ExpressionLanguageType
from pydantic import BaseModel


class TransformationAttributeDTO(BaseModel):
Expand All @@ -24,7 +24,7 @@ class Config:


class CreateTransformationAttributeDTO(BaseModel):
AttributeId: int
AttributeId: Optional[int] = None # No longer used
EntityId: Optional[int] = None # Existing column
# AttributeName: Optional[str] = None
# EntityName: Optional[str] = None
Expand Down
22 changes: 20 additions & 2 deletions components/lif/mdr_services/entity_association_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import List, Optional

from fastapi import HTTPException
from lif.datatypes.mdr_sql_model import DataModel, DataModelType, Entity, EntityAssociation, ExtInclusionsFromBaseDM
from lif.mdr_dto.entity_association_dto import (
Expand All @@ -11,9 +12,8 @@
from lif.mdr_services.helper_service import check_datamodel_by_id, check_entity_by_id
from lif.mdr_utils.logger_config import get_logger
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import or_, select
from sqlalchemy.orm import aliased

from sqlmodel import or_, select

logger = get_logger(__name__)

Expand Down Expand Up @@ -61,6 +61,24 @@ async def check_existing_association(
return result.scalar_one_or_none() is not None


async def check_entity_association_strict(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you using this instead of check_existing_association? There are cases where ExtendedByDataModelId could be null:

  • If data model type is BaseLIF or SourceSchema, then I think the ExtendedByDataModelId will always be null.
  • If the data model type is OrgLIF or PartnerLIF then the ExtendedByDataModelId may or may not be null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't like that it was an 'or' condition in check_existing_association. I felt we should be able to determine what value the ExtendedByDataModelId should be prior to invoking the method.

Copy link

@mgwozdz-unicon mgwozdz-unicon Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been having a hard time trying to determine how we could reverse engineer the ExtendedByDataModelId of the association. I think what I said in my other comment is true that for the OrgLIF and PartnerLIF, these are the cases where ExtendedByDataModelId of the association will not be null:

  1. The entity/attribute itself is an Extension and has ExtendedByDataModelId non-null.
  2. Someone used "+ Existing" to create an association in their OrgLIF or PartnerLIF that does not exist in BaseLIF.

That 2nd case is difficult to discern. The only thing I can think of to prove this is:

  1. If the Association has Extension=true, then ExtendedByDataModelId=anchor_data_model_id.

But we're trying to fetch the Association based off of this info, so we don't know yet whether or not it's an extension. I guess if you wanted, you could fetch the Association with the 'or' condition and then check if Extension=true and if it does then check that ExtendedByDataModelId=anchor_data_model_id.

Copy link
Contributor Author

@cbeach47 cbeach47 Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like from your last paragraph we can determine existance by the query:

!Extension AND ExtendedByDM == AnchorDM
OR 
Extension AND ExtendedByDM == Null

session: AsyncSession, parent_entity_id: int, child_entity_id: int, extended_by_data_model_id: int | None
) -> None:
query = select(EntityAssociation).where(
EntityAssociation.ParentEntityId == parent_entity_id,
EntityAssociation.ChildEntityId == child_entity_id,
EntityAssociation.Deleted == False,
EntityAssociation.ExtendedByDataModelId == extended_by_data_model_id,
)
result = await session.execute(query)
association = result.scalar_one_or_none() is not None
if not association:
raise HTTPException(
status_code=404,
detail=f"Entity {child_entity_id} with parent {parent_entity_id} not found in data model association with extended-by data model of '{extended_by_data_model_id}'",
)


async def validate_entity_associations_for_transformation_attribute(
session: AsyncSession, transformation_attribute: TransformationAttributeDTO
) -> bool:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import List, Optional, Tuple

from fastapi import HTTPException
from sqlalchemy import Select, func, or_, select
from lif.datatypes.mdr_sql_model import (
Attribute,
DataModelType,
Expand All @@ -15,6 +15,7 @@
)
from lif.mdr_services.helper_service import check_attribute_by_id, check_datamodel_by_id, check_entity_by_id
from lif.mdr_utils.logger_config import get_logger
from sqlalchemy import Select, func, or_, select
from sqlalchemy.ext.asyncio import AsyncSession

logger = get_logger(__name__)
Expand All @@ -41,6 +42,24 @@ async def check_existing_association(
return result.scalar_one_or_none() is not None


async def check_entity_attribute_association_strict(
session: AsyncSession, entity_id: int, attribute_id: int, extended_by_data_model_id: int | None
) -> None:
query = select(EntityAttributeAssociation).where(
EntityAttributeAssociation.EntityId == entity_id,
EntityAttributeAssociation.AttributeId == attribute_id,
EntityAttributeAssociation.Deleted == False,
EntityAttributeAssociation.ExtendedByDataModelId == extended_by_data_model_id,
)
result = await session.execute(query)
association = result.scalar_one_or_none() is not None
if not association:
raise HTTPException(
status_code=404,
detail=f"Attribute {attribute_id} association with entity {entity_id} not found in data model association with extended-by data model of '{extended_by_data_model_id}'",
)


async def create_entity_attribute_association(session: AsyncSession, data: CreateEntityAttributeAssociationDTO):
# checking if provided entity id and attribute id exist or not
entity = await check_entity_by_id(session=session, id=data.EntityId)
Expand Down
20 changes: 18 additions & 2 deletions components/lif/mdr_services/inclusions_service.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from fastapi import HTTPException
from lif.datatypes.mdr_sql_model import DatamodelElementType, EntityAttributeAssociation, ExtInclusionsFromBaseDM
from lif.mdr_dto.inclusion_dto import CreateInclusionDTO, InclusionDTO, UpdateInclusionDTO
from lif.mdr_services.attribute_service import get_attribute_by_id
from lif.mdr_services.entity_service import get_entity_by_id
from lif.mdr_services.helper_service import check_datamodel_by_id
from lif.mdr_utils.logger_config import get_logger
from sqlalchemy.ext.asyncio import AsyncSession
from lif.datatypes.mdr_sql_model import EntityAttributeAssociation, ExtInclusionsFromBaseDM
from sqlmodel import select, func
from sqlmodel import func, select

logger = get_logger(__name__)

Expand Down Expand Up @@ -229,3 +229,19 @@ async def get_attribute_inclusions_by_data_model_id_and_entity_id(
inclusions = result.scalars().all()
inclusion_dtos = [InclusionDTO.from_orm(inclusion) for inclusion in inclusions]
return inclusion_dtos


async def check_existing_inclusion(
session: AsyncSession, type: DatamodelElementType, node_id: int, included_by_data_model_id: int
) -> None:
query = select(ExtInclusionsFromBaseDM).where(
ExtInclusionsFromBaseDM.ExtDataModelId == included_by_data_model_id,
ExtInclusionsFromBaseDM.IncludedElementId == node_id,
ExtInclusionsFromBaseDM.ElementType == type,
ExtInclusionsFromBaseDM.Deleted == False,
)
result = await session.execute(query)
if result.scalar_one_or_none() is None:
raise HTTPException(
status_code=404, detail=f"Inclusion of {type} {node_id} not found in data model {included_by_data_model_id}"
)
Loading