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:
parent
9bfcc30981
commit
c45a46b07b
@ -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])
|
@router.get("/slot/{slot_identifier}", response_model=List[PuckWithTellPosition])
|
||||||
async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db)):
|
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
|
# Map keywords to slot IDs
|
||||||
slot_aliases = {
|
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_ids = [dewar.id for dewar in dewars]
|
||||||
dewar_map = {dewar.id: dewar.dewar_name 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 to fetch the latest event for each puck (any type of event)
|
||||||
subquery = (
|
latest_event_subquery = (
|
||||||
db.query(
|
db.query(
|
||||||
PuckEventModel.puck_id,
|
PuckEventModel.puck_id.label("puck_id"),
|
||||||
func.max(PuckEventModel.timestamp).label("latest_event_time"),
|
func.max(PuckEventModel.timestamp).label("latest_event_time"),
|
||||||
)
|
)
|
||||||
.filter(PuckEventModel.event_type == "tell_position_set")
|
|
||||||
.group_by(PuckEventModel.puck_id)
|
.group_by(PuckEventModel.puck_id)
|
||||||
.subquery()
|
.subquery(name="latest_event_subquery") # Explicitly name the subquery
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fetch pucks with their latest tell_position
|
# Main query to fetch pucks and their latest events
|
||||||
pucks_with_positions = (
|
pucks_with_latest_events = (
|
||||||
db.query(PuckModel, PuckEventModel.tell_position)
|
db.query(
|
||||||
.outerjoin(subquery, subquery.c.puck_id == PuckModel.id)
|
PuckModel,
|
||||||
.outerjoin(
|
PuckEventModel.event_type,
|
||||||
PuckEventModel,
|
PuckEventModel.tell_position,
|
||||||
(PuckEventModel.puck_id == PuckModel.id)
|
|
||||||
& (PuckEventModel.timestamp == subquery.c.latest_event_time),
|
|
||||||
)
|
)
|
||||||
.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()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Log the results of the subquery and pucks fetched:
|
# Log the results of the query
|
||||||
logger.debug(f"Results from subquery (tell_position): {pucks_with_positions}")
|
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}")
|
logger.warning(f"No pucks found for slot: {slot_identifier}")
|
||||||
raise HTTPException(
|
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 = []
|
results = []
|
||||||
for puck, tell_position in pucks_with_positions:
|
for puck, event_type, tell_position in pucks_with_latest_events:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Puck ID: {puck.id}, Name: {puck.puck_name},"
|
f"Puck ID: {puck.id}, Name: {puck.puck_name}, Event Type: {event_type}, "
|
||||||
f" Tell Position: {tell_position}"
|
f"Tell Position: {tell_position}"
|
||||||
)
|
)
|
||||||
|
|
||||||
dewar_name = dewar_map.get(puck.dewar_id, "Unknown")
|
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(
|
results.append(
|
||||||
PuckWithTellPosition(
|
PuckWithTellPosition(
|
||||||
id=puck.id,
|
id=puck.id,
|
||||||
@ -382,11 +397,11 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db))
|
|||||||
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, # 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
|
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),
|
Retrieve all pucks with a `tell_position` set (not null),
|
||||||
their associated samples, and the latest `tell_position` value (if any).
|
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 = (
|
pucks_with_events = (
|
||||||
db.query(PuckModel, PuckEventModel)
|
db.query(PuckModel, PuckEventModel)
|
||||||
.join(PuckEventModel, PuckModel.id == PuckEventModel.puck_id)
|
.join(PuckEventModel, PuckModel.id == PuckEventModel.puck_id)
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "aareDB"
|
name = "aareDB"
|
||||||
version = "0.1.0a15"
|
version = "0.1.0a16"
|
||||||
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"}
|
||||||
|
233
testfunctions.ipynb
Normal file
233
testfunctions.ipynb
Normal 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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user