Add example scripts and enhance puck event queries

Introduced a new Jupyter Notebook with API usage examples for managing pucks and samples. Refactored puck retrieval logic to include the latest event type and `tell_position`, improving data accuracy. Updated backend version to 0.1.0a16 accordingly.
This commit is contained in:
GotthardG 2025-01-09 16:56:38 +01:00
parent 9bfcc30981
commit c45a46b07b
3 changed files with 276 additions and 29 deletions

View File

@ -271,7 +271,8 @@ async def get_last_tell_position(puck_id: str, db: Session = Depends(get_db)):
@router.get("/slot/{slot_identifier}", response_model=List[PuckWithTellPosition])
async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db)):
"""
Retrieve all pucks in a slot with their latest `tell_position`.
Retrieve all pucks in a slot, reporting their latest event and
`tell_position` value.
"""
# Map keywords to slot IDs
slot_aliases = {
@ -328,50 +329,64 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db))
dewar_ids = [dewar.id for dewar in dewars]
dewar_map = {dewar.id: dewar.dewar_name for dewar in dewars}
# Subquery to fetch the latest tell_position for each puck
subquery = (
# Subquery to fetch the latest event for each puck (any type of event)
latest_event_subquery = (
db.query(
PuckEventModel.puck_id,
PuckEventModel.puck_id.label("puck_id"),
func.max(PuckEventModel.timestamp).label("latest_event_time"),
)
.filter(PuckEventModel.event_type == "tell_position_set")
.group_by(PuckEventModel.puck_id)
.subquery()
.subquery(name="latest_event_subquery") # Explicitly name the subquery
)
# Fetch pucks with their latest tell_position
pucks_with_positions = (
db.query(PuckModel, PuckEventModel.tell_position)
.outerjoin(subquery, subquery.c.puck_id == PuckModel.id)
.outerjoin(
PuckEventModel,
(PuckEventModel.puck_id == PuckModel.id)
& (PuckEventModel.timestamp == subquery.c.latest_event_time),
# Main query to fetch pucks and their latest events
pucks_with_latest_events = (
db.query(
PuckModel,
PuckEventModel.event_type,
PuckEventModel.tell_position,
)
.filter(PuckModel.dewar_id.in_(dewar_ids))
.join( # Join pucks with the latest event
# (outer join to include pucks without events)
latest_event_subquery,
PuckModel.id == latest_event_subquery.c.puck_id,
isouter=True,
)
.join( # Fetch event details from the latest event timestamp
PuckEventModel,
(PuckEventModel.puck_id == latest_event_subquery.c.puck_id)
& (PuckEventModel.timestamp == latest_event_subquery.c.latest_event_time),
isouter=True,
)
.filter(PuckModel.dewar_id.in_(dewar_ids)) # Restrict pucks to relevant dewars
.all()
)
# Log the results of the subquery and pucks fetched:
logger.debug(f"Results from subquery (tell_position): {pucks_with_positions}")
# Log the results of the query
logger.debug(f"Results from query (latest events): {pucks_with_latest_events}")
if not pucks_with_positions:
if not pucks_with_latest_events:
logger.warning(f"No pucks found for slot: {slot_identifier}")
raise HTTPException(
status_code=404, detail=f"No pucks found for slot '{slot_identifier}'"
status_code=404, detail=f"No pucks found for slot {slot_identifier}"
)
# Prepare results:
# Prepare the final response
results = []
for puck, tell_position in pucks_with_positions:
for puck, event_type, tell_position in pucks_with_latest_events:
logger.debug(
f"Puck ID: {puck.id}, Name: {puck.puck_name},"
f" Tell Position: {tell_position}"
f"Puck ID: {puck.id}, Name: {puck.puck_name}, Event Type: {event_type}, "
f"Tell Position: {tell_position}"
)
dewar_name = dewar_map.get(puck.dewar_id, "Unknown")
# Prepare the PuckWithTellPosition instance
# For pucks with no events or whose latest event is `puck_removed`, set
# `tell_position` to None
if event_type is None or event_type == "puck_removed":
tell_position = None
# Construct the response model
results.append(
PuckWithTellPosition(
id=puck.id,
@ -382,11 +397,11 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db))
else None,
dewar_id=puck.dewar_id,
dewar_name=dewar_name,
tell_position=tell_position, # Latest tell_position from subquery
tell_position=tell_position,
)
)
logger.info(f"Final response prepared for slot {slot_identifier}: {results}")
logger.info(f"Final response for slot {slot_identifier}: {results}")
return results
@ -396,8 +411,7 @@ async def get_pucks_with_tell_position(db: Session = Depends(get_db)):
Retrieve all pucks with a `tell_position` set (not null),
their associated samples, and the latest `tell_position` value (if any).
"""
# Query pucks with their latest `tell_position_set` event where
# `tell_position` is not null
pucks_with_events = (
db.query(PuckModel, PuckEventModel)
.join(PuckEventModel, PuckModel.id == PuckEventModel.puck_id)

View File

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

233
testfunctions.ipynb Normal file
View File

@ -0,0 +1,233 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2025-01-09T15:53:14.008758Z",
"start_time": "2025-01-09T15:53:14.005084Z"
}
},
"source": [
"import aareDBclient\n",
"from aareDBclient.rest import ApiException\n",
"from pprint import pprint\n",
"from aareDBclient import SamplesApi\n",
"from aareDBclient.models import SampleEventCreate, SetTellPosition\n",
"\n",
"print(aareDBclient.__version__)\n",
"\n",
"configuration = aareDBclient.Configuration(\n",
" host = \"https://127.0.0.1:8000\"\n",
")\n",
"\n",
"configuration.verify_ssl = False # Disable SSL verification\n",
"print(dir(SamplesApi))"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0.1.0a15\n",
"['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_create_sample_event_samples_samples_sample_id_events_post_serialize', '_get_all_pucks_with_samples_and_events_samples_pucks_samples_get_serialize', '_get_last_sample_event_samples_samples_sample_id_events_last_get_serialize', '_get_samples_with_events_samples_puck_id_samples_get_serialize', 'create_sample_event_samples_samples_sample_id_events_post', 'create_sample_event_samples_samples_sample_id_events_post_with_http_info', 'create_sample_event_samples_samples_sample_id_events_post_without_preload_content', 'get_all_pucks_with_samples_and_events_samples_pucks_samples_get', 'get_all_pucks_with_samples_and_events_samples_pucks_samples_get_with_http_info', 'get_all_pucks_with_samples_and_events_samples_pucks_samples_get_without_preload_content', 'get_last_sample_event_samples_samples_sample_id_events_last_get', 'get_last_sample_event_samples_samples_sample_id_events_last_get_with_http_info', 'get_last_sample_event_samples_samples_sample_id_events_last_get_without_preload_content', 'get_samples_with_events_samples_puck_id_samples_get', 'get_samples_with_events_samples_puck_id_samples_get_with_http_info', 'get_samples_with_events_samples_puck_id_samples_get_without_preload_content']\n"
]
}
],
"execution_count": 6
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-09T15:55:27.795447Z",
"start_time": "2025-01-09T15:55:27.775421Z"
}
},
"cell_type": "code",
"source": [
"with aareDBclient.ApiClient(configuration) as api_client:\n",
" # Create an instance of the API class\n",
" api_instance = aareDBclient.PucksApi(api_client)\n",
" get_pucks_at_beamline = aareDBclient.PucksApi(api_client)\n",
"\n",
" try:\n",
" # Create Return Address\n",
" api_response = api_instance.get_pucks_by_slot_pucks_slot_slot_identifier_get(slot_identifier='X06DA')\n",
" print(\"The response of PucksApi->get_pucks_by_slot_pucks_slot_slot_identifier_get:\\n\")\n",
" pprint(api_response)\n",
"\n",
" except ApiException as e:\n",
" print(\"Exception when calling PucksApi->get_pucks_by_slot_pucks_slot_slot_identifier_get: %s\\n\" % e)"
],
"id": "bbee7c94bf14000c",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The response of PucksApi->get_pucks_by_slot_pucks_slot_slot_identifier_get:\n",
"\n",
"[PuckWithTellPosition(id=31, puck_name='CPS-4093', puck_type='unipuck', puck_location_in_dewar='1', dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position='B1'),\n",
" PuckWithTellPosition(id=32, puck_name='CPS-4178', puck_type='unipuck', puck_location_in_dewar='2', dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position='B2'),\n",
" PuckWithTellPosition(id=33, puck_name='PSIMX-122', puck_type='unipuck', puck_location_in_dewar='3', dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n",
" PuckWithTellPosition(id=34, puck_name='E-07', puck_type='unipuck', puck_location_in_dewar='4', dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n",
" PuckWithTellPosition(id=35, puck_name='CPS-6597', puck_type='unipuck', puck_location_in_dewar='5', dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n",
" PuckWithTellPosition(id=36, puck_name='PSIMX-078', puck_type='unipuck', puck_location_in_dewar='6', dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None),\n",
" PuckWithTellPosition(id=37, puck_name='1002', puck_type='unipuck', puck_location_in_dewar='7', dewar_id=6, dewar_name='Dewar_test', user='e16371', samples=None, tell_position=None)]\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
" warnings.warn(\n"
]
}
],
"execution_count": 14
},
{
"metadata": {
"ExecuteTime": {
"end_time": "2025-01-09T15:55:22.123038Z",
"start_time": "2025-01-09T15:55:22.091256Z"
}
},
"cell_type": "code",
"source": [
"with aareDBclient.ApiClient(configuration) as api_client:\n",
" # Create an instance of the API class\n",
" api_instance = aareDBclient.PucksApi(api_client)\n",
" get_pucks_at_beamline = aareDBclient.PucksApi(api_client)\n",
"\n",
" # This part is commented but will be used to attribute a puck to a position of the TELL\n",
" # Define the puck ID and payload\n",
"\n",
" payload = [SetTellPosition(puck_name='CPS-4093', segment='B', puck_in_segment=1),SetTellPosition(puck_name='CPS-4178', segment='B', puck_in_segment=2)]\n",
" #payload = []\n",
"\n",
" try:\n",
" # Call the PUT method to update the tell_position\n",
" api_response = api_instance.set_tell_positions_pucks_set_tell_positions_put(payload)\n",
" print(\"The response of PucksApi->pucks_puck_id_tell_position_put:\\n\")\n",
" pprint(api_response)\n",
"\n",
" except ApiException as e:\n",
" print(\"Exception when calling PucksApi->pucks_puck_id_tell_position_put: %s\\n\" % e)"
],
"id": "d52d12287dd63299",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The response of PucksApi->pucks_puck_id_tell_position_put:\n",
"\n",
"[{'message': 'The tell_position was updated successfully.',\n",
" 'new_position': 'B1',\n",
" 'previous_position': None,\n",
" 'puck_name': 'CPS-4093',\n",
" 'status': 'updated'},\n",
" {'message': 'The tell_position was updated successfully.',\n",
" 'new_position': 'B2',\n",
" 'previous_position': None,\n",
" 'puck_name': 'CPS-4178',\n",
" 'status': 'updated'}]\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '127.0.0.1'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
" warnings.warn(\n"
]
}
],
"execution_count": 13
},
{
"metadata": {},
"cell_type": "code",
"source": [
"\n",
"\n",
" # GET request: Fetch all pucks in the tell\n",
" try:\n",
" all_pucks_response = api_instance.get_pucks_with_tell_position_pucks_with_tell_position_get() # Replace with appropriate method\n",
" print(\"The response of PucksApi->get_all_pucks_in_tell:\\n\")\n",
" pprint(all_pucks_response)\n",
"\n",
" except ApiException as e:\n",
" print(\"Exception when calling PucksApi->get_all_pucks_in_tell: %s\\n\" % e)"
],
"id": "95f8c133359945d5",
"outputs": [],
"execution_count": null
},
{
"metadata": {},
"cell_type": "code",
"source": [
"\n",
"with aareDBclient.ApiClient(configuration) as api_client:\n",
" # Create an instance of the Samples API class\n",
" api_instance = aareDBclient.SamplesApi(api_client)\n",
"\n",
" # Define the sample ID and event payload using the expected model\n",
" sample_id = 1\n",
" event_payload = SampleEventCreate(\n",
" event_type=\"Unmounted\" # Replace with actual event type if different\n",
" )\n",
"\n",
" try:\n",
" # Post the sample event\n",
" api_response = api_instance.create_sample_event_samples_samples_sample_id_events_post(\n",
" sample_id=sample_id,\n",
" sample_event_create=event_payload # Pass the model instance here\n",
" )\n",
" print(\"The response of post_sample_event:\\n\")\n",
" pprint(api_response)\n",
"\n",
" except ApiException as e:\n",
" print(\"Exception when calling post_sample_event: %s\\n\" % e)\n",
"\n",
" try:\n",
" # Get the last sample event\n",
" last_event_response = api_instance.get_last_sample_event_samples_samples_sample_id_events_last_get(1)\n",
" print(\"The response of get_last_sample_event:\\n\")\n",
" pprint(last_event_response)\n",
"\n",
" except ApiException as e:\n",
" print(\"Exception when calling get_last_sample_event: %s\\n\" % e)\n"
],
"id": "ee8abb293096334a",
"outputs": [],
"execution_count": null
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}