Refactor puck event handling and add tell filtering

Updated puck event queries to improve robustness and ensure distinct results. Introduced filtering by `tell` in specific API endpoints and added validation for `tell` values. Incremented project version to 0.1.0a21 to reflect API changes.
This commit is contained in:
GotthardG 2025-02-04 17:07:25 +01:00
parent 780ba1959f
commit 9e5ae2b43c
2 changed files with 46 additions and 30 deletions

View File

@ -145,6 +145,7 @@ def get_pucks_at_beamline(slot_id: int, db: Session) -> List[PuckWithTellPositio
PuckModel, PuckModel,
PuckEventModel.event_type, PuckEventModel.event_type,
PuckEventModel.tell_position, PuckEventModel.tell_position,
PuckEventModel.timestamp, # Useful for debugging or edge cases
DewarModel, DewarModel,
) )
.join( .join(
@ -163,22 +164,28 @@ def get_pucks_at_beamline(slot_id: int, db: Session) -> List[PuckWithTellPositio
) )
.join(DewarModel, PuckModel.dewar_id == DewarModel.id, isouter=True) .join(DewarModel, PuckModel.dewar_id == DewarModel.id, isouter=True)
.filter(PuckModel.dewar_id.in_(dewar_ids)) .filter(PuckModel.dewar_id.in_(dewar_ids))
.distinct() # Ensure no duplicates
.all() .all()
) )
# Prepare the results # Prepare the results
results = [] results = {}
for puck, event_type, tell_position, dewar in pucks_with_latest_events: for (
dewar_name = dewar_map.get(puck.dewar_id, "Unknown") puck,
event_type,
tell_position,
event_timestamp,
dewar,
) in pucks_with_latest_events:
dewar_name = dewar_map.get(puck.dewar_id)
pgroup = dewar_pgroups.get(puck.dewar_id) pgroup = dewar_pgroups.get(puck.dewar_id)
# For pucks with no events or whose latest event is "puck_removed", set # If the event is None or explicitly a "puck_removed", set `tell_position=None`
# tell_position to None
if event_type is None or event_type == "puck_removed": if event_type is None or event_type == "puck_removed":
tell_position = None tell_position = None
results.append( # Always replace results since we are processing the latest event
PuckWithTellPosition( results[puck.id] = PuckWithTellPosition(
id=puck.id, id=puck.id,
pgroup=pgroup, pgroup=pgroup,
puck_name=puck.puck_name, puck_name=puck.puck_name,
@ -188,11 +195,10 @@ def get_pucks_at_beamline(slot_id: int, db: Session) -> List[PuckWithTellPositio
else None, else None,
dewar_id=puck.dewar_id, dewar_id=puck.dewar_id,
dewar_name=dewar_name, dewar_name=dewar_name,
tell_position=tell_position, tell_position=tell_position, # Respect if `None` is explicitly set
)
) )
return results return list(results.values())
@router.get("/", response_model=List[PuckSchema]) @router.get("/", response_model=List[PuckSchema])
@ -269,7 +275,7 @@ async def set_tell_positions(
tell=None, # Nullify the `tell` for removal tell=None, # Nullify the `tell` for removal
tell_position=None, tell_position=None,
event_type="puck_removed", event_type="puck_removed",
timestamp=datetime.utcnow(), timestamp=datetime.now(),
) )
db.add(remove_event) db.add(remove_event)
@ -309,9 +315,8 @@ async def set_tell_positions(
puck.id: db.query(PuckEventModel) puck.id: db.query(PuckEventModel)
.filter( .filter(
PuckEventModel.puck_id == puck.id, PuckEventModel.puck_id == puck.id,
PuckEventModel.event_type == "tell_position_set",
) )
.order_by(PuckEventModel.timestamp.desc()) .order_by(PuckEventModel.id.desc())
.first() .first()
for puck in pucks_at_beamline for puck in pucks_at_beamline
} }
@ -358,7 +363,7 @@ async def set_tell_positions(
tell=None, tell=None,
tell_position=None, tell_position=None,
event_type="puck_removed", event_type="puck_removed",
timestamp=datetime.utcnow(), timestamp=datetime.now(),
) )
db.add(remove_event) db.add(remove_event)
@ -368,7 +373,7 @@ async def set_tell_positions(
tell=tell, tell=tell,
tell_position=new_position, tell_position=new_position,
event_type="tell_position_set", event_type="tell_position_set",
timestamp=datetime.utcnow(), timestamp=datetime.now(),
) )
db.add(new_event) db.add(new_event)
@ -409,7 +414,7 @@ async def set_tell_positions(
tell=None, tell=None,
tell_position=None, tell_position=None,
event_type="puck_removed", event_type="puck_removed",
timestamp=datetime.utcnow(), timestamp=datetime.now(),
) )
db.add(remove_event) db.add(remove_event)
results.append( results.append(
@ -436,13 +441,23 @@ async def set_tell_positions(
@router.get("/with-tell-position", response_model=List[PuckWithTellPosition]) @router.get("/with-tell-position", response_model=List[PuckWithTellPosition])
async def get_pucks_with_tell_position(db: Session = Depends(get_db)): async def get_pucks_with_tell_position(
tell: str, # Specify tell as a query parameter
db: Session = Depends(get_db),
):
""" """
Retrieve all pucks with a valid `tell_position` set (non-null), Retrieve all pucks with a valid `tell_position` set (non-null),
their associated samples, and the latest `tell_position` value (if any). their associated samples, and the latest `tell_position` value (if any),
Only include pucks when their latest event has a `tell_position` filtered by a specific `tell`.
set and an `event_type` matching "tell_position_set".
""" """
# Validate the incoming `tell` value
try:
validate_tell(tell) # Ensure `tell` is valid using predefined valid options
except ValueError as error:
raise HTTPException(
status_code=400, detail=str(error)
) # Raise error for invalid tells
# Step 1: Prepare a subquery to fetch the latest event timestamp for each puck. # Step 1: Prepare a subquery to fetch the latest event timestamp for each puck.
latest_event_subquery = ( latest_event_subquery = (
db.query( db.query(
@ -471,6 +486,7 @@ async def get_pucks_with_tell_position(db: Session = Depends(get_db)):
.filter( .filter(
PuckEventModel.event_type == "tell_position_set" PuckEventModel.event_type == "tell_position_set"
) # Only include relevant event types ) # Only include relevant event types
.filter(PuckEventModel.tell == tell) # Filter by the specific `tell` variable
.all() .all()
) )

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "aareDB" name = "aareDB"
version = "0.1.0a20" version = "0.1.0a21"
description = "Backend for next gen sample management system" description = "Backend for next gen sample management system"
authors = [{name = "Guillaume Gotthard", email = "guillaume.gotthard@psi.ch"}] authors = [{name = "Guillaume Gotthard", email = "guillaume.gotthard@psi.ch"}]
license = {text = "MIT"} license = {text = "MIT"}