Refactor beamtime relationships in models and related APIs

Updated relationships for beamtime in models to support many-to-many associations with pucks, samples, and dewars. Refactored API endpoints to accommodate these changes, ensuring accurate assignment and retrieval of data. Improved sample data generation logic and incremented the application version for the new updates.
This commit is contained in:
GotthardG 2025-05-08 16:04:05 +02:00
parent 0fa038be94
commit 6a0953c913
12 changed files with 404 additions and 210 deletions

View File

@ -407,17 +407,43 @@ beamtimes = [
), ),
Beamtime( Beamtime(
id=2, id=2,
pgroups="p20003", pgroups="p20001",
shift="afternoon", shift="afternoon",
beamtime_name="p20003-test", beamtime_name="p20001-test",
beamline="X06DA", beamline="X06DA",
start_date=datetime.strptime("07.05.2025", "%d.%m.%Y").date(), start_date=datetime.strptime("06.05.2025", "%d.%m.%Y").date(),
end_date=datetime.strptime("08.05.2025", "%d.%m.%Y").date(), end_date=datetime.strptime("07.05.2025", "%d.%m.%Y").date(),
status="confirmed", status="confirmed",
comments="this is a test beamtime", comments="this is a test beamtime",
proposal_id=2, proposal_id=2,
local_contact_id=2, local_contact_id=2,
), ),
Beamtime(
id=3,
pgroups="p20003",
shift="morning",
beamtime_name="p20003-test",
beamline="X06SA",
start_date=datetime.strptime("06.05.2025", "%d.%m.%Y").date(),
end_date=datetime.strptime("06.05.2025", "%d.%m.%Y").date(),
status="confirmed",
comments="this is a test beamtime",
proposal_id=1,
local_contact_id=1,
),
Beamtime(
id=4,
pgroups="p20002",
shift="night",
beamtime_name="p20002-test",
beamline="X06DA",
start_date=datetime.strptime("08.05.2025", "%d.%m.%Y").date(),
end_date=datetime.strptime("08.05.2025", "%d.%m.%Y").date(),
status="confirmed",
comments="this is a test beamtime",
proposal_id=3,
local_contact_id=2,
),
] ]
# Define shipments # Define shipments
@ -679,7 +705,8 @@ samples = []
sample_id_counter = 1 sample_id_counter = 1
# Assign a beamtime to each dewar # Assign a beamtime to each dewar
dewar_to_beamtime = { dewar_to_beamtime = {
dewar.id: random.choice([1, 2]) for dewar in dewars # Or use actual beamtime ids dewar.id: random.choice([1, 2, 3, 4])
for dewar in dewars # Or use actual beamtime ids
} }
# Update dewars and their pucks with consistent beamtime # Update dewars and their pucks with consistent beamtime
@ -688,10 +715,9 @@ for dewar in dewars:
for puck in pucks: for puck in pucks:
dewar_id = puck.dewar_id # Assuming puck has dewar_id dewar_id = puck.dewar_id # Assuming puck has dewar_id
assigned_beamtime = dewar_to_beamtime[dewar_id] assigned_beamtime = dewar_to_beamtime[dewar_id] # this is the id (int)
puck.beamtime_id = ( # Fix here: use assigned_beamtime (which is the id)
assigned_beamtime # Associate puck to the same beamtime as its dewar assigned_beamtime_obj = next(b for b in beamtimes if b.id == assigned_beamtime)
)
positions_with_samples = random.randint(1, 16) positions_with_samples = random.randint(1, 16)
occupied_positions = random.sample(range(1, 17), positions_with_samples) occupied_positions = random.sample(range(1, 17), positions_with_samples)
@ -703,11 +729,14 @@ for puck in pucks:
sample_name=f"Sample{sample_id_counter:03}", sample_name=f"Sample{sample_id_counter:03}",
position=pos, position=pos,
puck_id=puck.id, puck_id=puck.id,
beamtime_id=assigned_beamtime,
) )
sample.beamtimes.append(
assigned_beamtime_obj
) # assigned_beamtime_obj is a Beamtime instance
samples.append(sample) samples.append(sample)
sample_id_counter += 1 sample_id_counter += 1
# Define possible event types for samples # Define possible event types for samples
event_types = ["Mounting", "Failed", "Unmounting", "Lost"] event_types = ["Mounting", "Failed", "Unmounting", "Lost"]

View File

@ -9,6 +9,7 @@ from sqlalchemy import (
Boolean, Boolean,
func, func,
Enum, Enum,
Table,
) )
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from .database import Base from .database import Base
@ -16,6 +17,26 @@ from datetime import datetime
import enum import enum
dewar_beamtime_association = Table(
"dewar_beamtime_association",
Base.metadata,
Column("dewar_id", Integer, ForeignKey("dewars.id")),
Column("beamtime_id", Integer, ForeignKey("beamtimes.id")),
)
puck_beamtime_association = Table(
"puck_beamtime_association",
Base.metadata,
Column("puck_id", Integer, ForeignKey("pucks.id")),
Column("beamtime_id", Integer, ForeignKey("beamtimes.id")),
)
sample_beamtime_association = Table(
"sample_beamtime_association",
Base.metadata,
Column("sample_id", Integer, ForeignKey("samples.id")),
Column("beamtime_id", Integer, ForeignKey("beamtimes.id")),
)
class Shipment(Base): class Shipment(Base):
__tablename__ = "shipments" __tablename__ = "shipments"
@ -120,8 +141,9 @@ class Dewar(Base):
beamline_location = None beamline_location = None
local_contact_id = Column(Integer, ForeignKey("local_contacts.id"), nullable=True) local_contact_id = Column(Integer, ForeignKey("local_contacts.id"), nullable=True)
local_contact = relationship("LocalContact") local_contact = relationship("LocalContact")
beamtime = relationship("Beamtime", back_populates="dewars") beamtimes = relationship(
beamtime_id = Column(Integer, ForeignKey("beamtimes.id"), nullable=True) "Beamtime", secondary=dewar_beamtime_association, back_populates="dewars"
)
@property @property
def number_of_pucks(self) -> int: def number_of_pucks(self) -> int:
@ -155,9 +177,8 @@ class Puck(Base):
dewar = relationship("Dewar", back_populates="pucks") dewar = relationship("Dewar", back_populates="pucks")
samples = relationship("Sample", back_populates="puck") samples = relationship("Sample", back_populates="puck")
events = relationship("PuckEvent", back_populates="puck") events = relationship("PuckEvent", back_populates="puck")
beamtime_id = Column(Integer, ForeignKey("beamtimes.id"), nullable=True) beamtimes = relationship(
beamtime = relationship( "Beamtime", secondary=puck_beamtime_association, back_populates="pucks"
"Beamtime", back_populates="pucks", foreign_keys=[beamtime_id]
) )
@ -178,8 +199,9 @@ class Sample(Base):
puck = relationship("Puck", back_populates="samples") puck = relationship("Puck", back_populates="samples")
events = relationship("SampleEvent", back_populates="sample", lazy="joined") events = relationship("SampleEvent", back_populates="sample", lazy="joined")
images = relationship("Image", back_populates="sample", lazy="joined") images = relationship("Image", back_populates="sample", lazy="joined")
beamtime_id = Column(Integer, ForeignKey("beamtimes.id"), nullable=True) beamtimes = relationship(
beamtime = relationship("Beamtime", back_populates="samples") "Beamtime", secondary=sample_beamtime_association, back_populates="samples"
)
@property @property
def mount_count(self) -> int: def mount_count(self) -> int:
@ -262,9 +284,15 @@ class Beamtime(Base):
local_contact_id = Column(Integer, ForeignKey("local_contacts.id"), nullable=False) local_contact_id = Column(Integer, ForeignKey("local_contacts.id"), nullable=False)
local_contact = relationship("LocalContact") local_contact = relationship("LocalContact")
dewars = relationship("Dewar", back_populates="beamtime") dewars = relationship(
pucks = relationship("Puck", back_populates="beamtime") "Dewar", secondary=dewar_beamtime_association, back_populates="beamtimes"
samples = relationship("Sample", back_populates="beamtime") )
pucks = relationship(
"Puck", secondary=puck_beamtime_association, back_populates="beamtimes"
)
samples = relationship(
"Sample", secondary=sample_beamtime_association, back_populates="beamtimes"
)
class Image(Base): class Image(Base):

View File

@ -36,6 +36,7 @@ from app.models import (
LogisticsEvent, LogisticsEvent,
PuckEvent, PuckEvent,
SampleEvent, SampleEvent,
Beamtime as BeamtimeModel,
) )
from app.dependencies import get_db from app.dependencies import get_db
import qrcode import qrcode
@ -598,18 +599,37 @@ async def assign_beamtime_to_dewar(
if not dewar: if not dewar:
raise HTTPException(status_code=404, detail="Dewar not found") raise HTTPException(status_code=404, detail="Dewar not found")
dewar.beamtime_id = None if beamtime_id == 0 else beamtime_id # Find the Beamtime instance, if not unassigning
beamtime = (
db.query(BeamtimeModel).filter(BeamtimeModel.id == beamtime_id).first()
if beamtime_id
else None
)
if beamtime_id == 0:
dewar.beamtimes = []
else:
dewar.beamtimes = [
beamtime
] # assign one; append if you want to support multiple
db.commit() db.commit()
db.refresh(dewar) db.refresh(dewar)
for puck in dewar.pucks: for puck in dewar.pucks:
puck.beamtime_id = None if beamtime_id == 0 else beamtime_id if beamtime_id == 0:
puck.beamtimes = []
else:
puck.beamtimes = [beamtime]
for sample in puck.samples: for sample in puck.samples:
has_sample_event = ( has_sample_event = (
db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).count() db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).count()
> 0 > 0
) )
if not has_sample_event: if not has_sample_event:
sample.beamtime_id = None if beamtime_id == 0 else beamtime_id if beamtime_id == 0:
sample.beamtimes = []
else:
sample.beamtimes = [beamtime]
db.commit() db.commit()
return {"status": "success", "dewar_id": dewar.id, "beamtime_id": beamtime_id} return {"status": "success", "dewar_id": dewar.id, "beamtime_id": beamtime_id}
@ -726,5 +746,12 @@ async def get_single_shipment(id: int, db: Session = Depends(get_db)):
operation_id="get_dewars_by_beamtime", operation_id="get_dewars_by_beamtime",
) )
async def get_dewars_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)): async def get_dewars_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)):
"""List all dewars assigned to a given beamtime.""" beamtime = (
return db.query(DewarModel).filter(DewarModel.beamtime_id == beamtime_id).all() db.query(BeamtimeModel)
.options(joinedload(BeamtimeModel.dewars))
.filter(BeamtimeModel.id == beamtime_id)
.first()
)
if not beamtime:
raise HTTPException(status_code=404, detail="Beamtime not found")
return beamtime.dewars

View File

@ -1,6 +1,6 @@
from datetime import datetime from datetime import datetime
from fastapi import APIRouter, HTTPException, status, Depends from fastapi import APIRouter, HTTPException, status, Depends
from sqlalchemy.orm import Session from sqlalchemy.orm import Session, joinedload
from sqlalchemy.sql import func from sqlalchemy.sql import func
from typing import List from typing import List
import uuid import uuid
@ -21,6 +21,7 @@ from app.models import (
LogisticsEvent as LogisticsEventModel, LogisticsEvent as LogisticsEventModel,
Dewar as DewarModel, Dewar as DewarModel,
SampleEvent, SampleEvent,
Beamtime as BeamtimeModel,
) )
from app.dependencies import get_db from app.dependencies import get_db
import logging import logging
@ -664,23 +665,38 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db))
@router.patch("/puck/{puck_id}/assign-beamtime", operation_id="assignPuckToBeamtime") @router.patch("/puck/{puck_id}/assign-beamtime", operation_id="assignPuckToBeamtime")
async def assign_beamtime_to_puck( async def assign_beamtime_to_puck(
puck_id: int, puck_id: int,
beamtime_id: int, # If you want ?beamtime_id=123 in the query beamtime_id: int, # expects ?beamtime_id=123 in the query
db: Session = Depends(get_db), db: Session = Depends(get_db),
): ):
puck = db.query(PuckModel).filter(PuckModel.id == puck_id).first() puck = db.query(PuckModel).filter(PuckModel.id == puck_id).first()
if not puck: if not puck:
raise HTTPException(status_code=404, detail="Puck not found") raise HTTPException(status_code=404, detail="Puck not found")
puck.beamtime_id = None if beamtime_id == 0 else beamtime_id beamtime = (
db.query(BeamtimeModel).filter(BeamtimeModel.id == beamtime_id).first()
if beamtime_id
else None
)
if beamtime_id == 0:
puck.beamtimes = []
else:
puck.beamtimes = [
beamtime
] # or use .append(beamtime) if you want to support multiple
db.commit() db.commit()
db.refresh(puck) db.refresh(puck)
# Update samples # Update samples as well
for sample in puck.samples: for sample in puck.samples:
has_sample_event = ( has_sample_event = (
db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).count() > 0 db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).count() > 0
) )
if not has_sample_event: if not has_sample_event:
sample.beamtime_id = None if beamtime_id == 0 else beamtime_id if beamtime_id == 0:
sample.beamtimes = []
else:
sample.beamtimes = [beamtime]
db.commit() db.commit()
return {"status": "success", "puck_id": puck.id, "beamtime_id": beamtime_id} return {"status": "success", "puck_id": puck.id, "beamtime_id": beamtime_id}
@ -691,5 +707,12 @@ async def assign_beamtime_to_puck(
operation_id="get_pucks_by_beamtime", operation_id="get_pucks_by_beamtime",
) )
async def get_pucks_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)): async def get_pucks_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)):
"""List all pucks assigned to a given beamtime.""" beamtime = (
return db.query(PuckModel).filter(PuckModel.beamtime_id == beamtime_id).all() db.query(BeamtimeModel)
.options(joinedload(BeamtimeModel.pucks)) # eager load pucks
.filter(BeamtimeModel.id == beamtime_id)
.first()
)
if not beamtime:
raise HTTPException(status_code=404, detail="Beamtime not found")
return beamtime.pucks

View File

@ -32,6 +32,7 @@ from app.models import (
Results as ResultsModel, Results as ResultsModel,
Jobs as JobModel, Jobs as JobModel,
JobStatus, JobStatus,
Beamtime as BeamtimeModel,
) )
from app.dependencies import get_db from app.dependencies import get_db
import logging import logging
@ -463,6 +464,7 @@ async def get_results_for_run_and_sample(
formatted_results = [ formatted_results = [
ResultResponse( ResultResponse(
id=result.id, id=result.id,
status=result.status,
sample_id=result.sample_id, sample_id=result.sample_id,
run_id=result.run_id, run_id=result.run_id,
result=ProcessingResults(**result.result), result=ProcessingResults(**result.result),
@ -479,5 +481,12 @@ async def get_results_for_run_and_sample(
operation_id="get_samples_by_beamtime", operation_id="get_samples_by_beamtime",
) )
async def get_samples_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)): async def get_samples_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)):
"""List all samples assigned to a given beamtime.""" beamtime = (
return db.query(SampleModel).filter(SampleModel.beamtime_id == beamtime_id).all() db.query(BeamtimeModel)
.options(joinedload(BeamtimeModel.samples))
.filter(BeamtimeModel.id == beamtime_id)
.first()
)
if not beamtime:
raise HTTPException(status_code=404, detail="Beamtime not found")
return beamtime.samples

View File

@ -373,13 +373,13 @@ class Results(BaseModel):
resolution: float resolution: float
unit_cell: str unit_cell: str
spacegroup: str spacegroup: str
rmerge: float rmerge: List[CurvePoint]
rmeas: float rmeas: List[CurvePoint]
isig: float isig: List[CurvePoint]
cc: List[CurvePoint] cc: List[CurvePoint]
cchalf: List[CurvePoint] cchalf: List[CurvePoint]
completeness: float completeness: List[CurvePoint]
multiplicity: float multiplicity: List[CurvePoint]
nobs: int nobs: int
total_refl: int total_refl: int
unique_refl: int unique_refl: int
@ -478,6 +478,55 @@ class AddressMinimal(BaseModel):
id: int id: int
class Beamtime(BaseModel):
id: int
pgroups: str
shift: str
beamtime_name: str
beamline: str
start_date: date
end_date: date
status: str
comments: Optional[constr(max_length=200)] = None
proposal_id: Optional[int]
local_contact_id: Optional[int]
local_contact: Optional[LocalContact]
class Config:
from_attributes = True
class BeamtimeCreate(BaseModel):
pgroups: str # this should be changed to pgroup
shift: str
beamtime_name: str
beamline: str
start_date: date
end_date: date
status: str
comments: Optional[constr(max_length=200)] = None
proposal_id: Optional[int]
local_contact_id: Optional[int]
class BeamtimeResponse(BaseModel):
id: int
pgroups: str
shift: str
beamtime_name: str
beamline: str
start_date: date
end_date: date
status: str
comments: Optional[str] = None
proposal_id: Optional[int]
local_contact_id: Optional[int]
local_contact: Optional[LocalContact]
class Config:
from_attributes = True
class Sample(BaseModel): class Sample(BaseModel):
id: int id: int
sample_name: str sample_name: str
@ -493,6 +542,7 @@ class Sample(BaseModel):
mount_count: Optional[int] = None mount_count: Optional[int] = None
unmount_count: Optional[int] = None unmount_count: Optional[int] = None
# results: Optional[Results] = None # results: Optional[Results] = None
beamtimes: List[Beamtime] = []
class Config: class Config:
from_attributes = True from_attributes = True
@ -507,6 +557,7 @@ class SampleCreate(BaseModel):
comments: Optional[str] = None comments: Optional[str] = None
results: Optional[Results] = None results: Optional[Results] = None
events: Optional[List[str]] = None events: Optional[List[str]] = None
beamtime_ids: List[int] = []
class Config: class Config:
populate_by_name = True populate_by_name = True
@ -534,7 +585,7 @@ class PuckCreate(BaseModel):
puck_type: str puck_type: str
puck_location_in_dewar: int puck_location_in_dewar: int
samples: List[SampleCreate] = [] samples: List[SampleCreate] = []
beamtime_id: Optional[int] = None beamtime_ids: List[int] = []
class PuckUpdate(BaseModel): class PuckUpdate(BaseModel):
@ -542,7 +593,7 @@ class PuckUpdate(BaseModel):
puck_type: Optional[str] = None puck_type: Optional[str] = None
puck_location_in_dewar: Optional[int] = None puck_location_in_dewar: Optional[int] = None
dewar_id: Optional[int] = None dewar_id: Optional[int] = None
beamtime_id: Optional[int] = None beamtime_ids: List[int] = []
class Puck(BaseModel): class Puck(BaseModel):
@ -551,9 +602,9 @@ class Puck(BaseModel):
puck_type: str puck_type: str
puck_location_in_dewar: int puck_location_in_dewar: int
dewar_id: int dewar_id: int
beamtime_id: Optional[int] = None
events: List[PuckEvent] = [] events: List[PuckEvent] = []
samples: List[Sample] = [] samples: List[Sample] = []
beamtimes: List[Beamtime] = []
class Config: class Config:
from_attributes = True from_attributes = True
@ -573,6 +624,7 @@ class DewarBase(BaseModel):
contact_id: Optional[int] contact_id: Optional[int]
return_address_id: Optional[int] return_address_id: Optional[int]
pucks: List[PuckCreate] = [] pucks: List[PuckCreate] = []
beamtimes: List[Beamtime] = []
class Config: class Config:
from_attributes = True from_attributes = True
@ -605,6 +657,7 @@ class DewarUpdate(BaseModel):
status: Optional[str] = None status: Optional[str] = None
contact_id: Optional[int] = None contact_id: Optional[int] = None
address_id: Optional[int] = None address_id: Optional[int] = None
beamtime_ids: List[int] = []
class DewarSchema(BaseModel): class DewarSchema(BaseModel):
@ -786,55 +839,6 @@ class DewarWithPucksResponse(BaseModel):
pucks: List[PuckResponse] pucks: List[PuckResponse]
class Beamtime(BaseModel):
id: int
pgroups: str
shift: str
beamtime_name: str
beamline: str
start_date: date
end_date: date
status: str
comments: Optional[constr(max_length=200)] = None
proposal_id: Optional[int]
local_contact_id: Optional[int]
local_contact: Optional[LocalContact]
class Config:
from_attributes = True
class BeamtimeCreate(BaseModel):
pgroups: str # this should be changed to pgroup
shift: str
beamtime_name: str
beamline: str
start_date: date
end_date: date
status: str
comments: Optional[constr(max_length=200)] = None
proposal_id: Optional[int]
local_contact_id: Optional[int]
class BeamtimeResponse(BaseModel):
id: int
pgroups: str
shift: str
beamtime_name: str
beamline: str
start_date: date
end_date: date
status: str
comments: Optional[str] = None
proposal_id: Optional[int]
local_contact_id: Optional[int]
local_contact: Optional[LocalContact]
class Config:
from_attributes = True
class ImageCreate(BaseModel): class ImageCreate(BaseModel):
pgroup: str pgroup: str
sample_id: int sample_id: int

View File

@ -168,8 +168,8 @@ async def lifespan(app: FastAPI):
load_slots_data(db) load_slots_data(db)
else: # dev or test environments else: # dev or test environments
print(f"{environment.capitalize()} environment: Regenerating database.") print(f"{environment.capitalize()} environment: Regenerating database.")
Base.metadata.drop_all(bind=engine) # Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine) # Base.metadata.create_all(bind=engine)
# from sqlalchemy.engine import reflection # from sqlalchemy.engine import reflection
# from app.models import ExperimentParameters # adjust the import as needed # from app.models import ExperimentParameters # adjust the import as needed
# inspector = reflection.Inspector.from_engine(engine) # inspector = reflection.Inspector.from_engine(engine)

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "aareDB" name = "aareDB"
version = "0.1.1a2" version = "0.1.1a3"
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"}

View File

@ -1,7 +1,7 @@
{ {
"ssl_cert_path": "ssl/cert.pem", "ssl_cert_path": "ssl/cert.pem",
"ssl_key_path": "ssl/key.pem", "ssl_key_path": "ssl/key.pem",
"OPENAPI_URL": "https://127.0.0.1:8000/openapi.json", "OPENAPI_URL": "https://0.0.0.0:8000/openapi.json",
"SCHEMA_PATH": "./src/openapi.json", "SCHEMA_PATH": "./src/openapi.json",
"OUTPUT_DIRECTORY": "./openapi", "OUTPUT_DIRECTORY": "./openapi",
"PORT": 8000, "PORT": 8000,

View File

@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { DataGridPremium, GridColDef } from '@mui/x-data-grid-premium'; import { DataGridPremium, GridColDef } from '@mui/x-data-grid-premium';
import { useNavigate } from 'react-router-dom'; // For navigation import { useNavigate } from 'react-router-dom';
import { BeamtimesService } from '../../openapi'; import {Beamtime, BeamtimesService} from '../../openapi';
import { Chip, Typography } from '@mui/material'; import { Chip, Typography } from '@mui/material';
interface BeamtimeRecord { interface BeamtimeRecord {

View File

@ -59,32 +59,59 @@ const Calendar: React.FC = () => {
setFetchError(null); setFetchError(null);
try { try {
const beamtimes = await BeamtimesService.getMyBeamtimesProtectedBeamtimesMyBeamtimesGet(); const beamtimes = await BeamtimesService.getMyBeamtimesProtectedBeamtimesMyBeamtimesGet();
const formattedEvents: CustomEvent[] = beamtimes.map((beamtime: any) => ({ const grouped: { [key: string]: any[] } = {};
id: `${beamtime.beamline}-${beamtime.start_date}`, beamtimes.forEach((beamtime: any) => {
title: `${beamtime.beamline}: ${beamtime.shift}`, const key = `${beamtime.start_date}|${beamtime.beamline}|${beamtime.pgroups}`;
start: beamtime.start_date, if (!grouped[key]) grouped[key] = [];
end: beamtime.end_date, grouped[key].push(beamtime);
beamtime_id: beamtime.id, });
beamline: beamtime.beamline || 'Unknown',
beamtime_shift: beamtime.shift || 'Unknown', const formattedEvents: CustomEvent[] = Object.values(grouped).map((group) => {
backgroundColor: beamlineColors[beamtime.beamline] || beamlineColors.Unknown, const shifts = group.map((bt: any) => bt.shift).join(" + ");
const ids = group.map((bt: any) => bt.id);
const first = group[0];
return {
id: `${first.beamline}-${first.start_date}-${first.pgroups}`, // ensure uniqueness
title: `${first.beamline}: ${shifts}`,
start: first.start_date,
end: first.end_date,
beamtime_ids: ids,
beamline: first.beamline || 'Unknown',
beamtime_shift: shifts,
backgroundColor: beamlineColors[first.beamline] || beamlineColors.Unknown,
borderColor: '#000', borderColor: '#000',
textColor: '#fff', textColor: '#fff',
})); beamtimes: group,
};
});
setEvents(formattedEvents); setEvents(formattedEvents);
// Fetch associations for all // Fetch associations for all
const assoc: { [id: string]: { dewars: string[]; pucks: string[] } } = {}; const assoc: { [id: string]: { dewars: string[]; pucks: string[] } } = {};
await Promise.all( await Promise.all(
beamtimes.map(async (bm: any) => { Object.values(grouped).map(async (group) => {
// multiple (or single) beamtimes per group
const ids = group.map((bt: any) => bt.id);
// fetch and merge for all ids in this group:
let dewarsSet = new Set<string>();
let pucksSet = new Set<string>();
await Promise.all(
ids.map(async (beamtimeId: number) => {
const [dewars, pucks] = await Promise.all([ const [dewars, pucks] = await Promise.all([
DewarsService.getDewarsByBeamtime(bm.id), DewarsService.getDewarsByBeamtime(beamtimeId),
PucksService.getPucksByBeamtime(bm.id), PucksService.getPucksByBeamtime(beamtimeId),
]); ]);
const eventId = `${bm.beamline}-${bm.start_date}`; dewars.forEach((d: any) => dewarsSet.add(d.id));
pucks.forEach((p: any) => pucksSet.add(p.id));
})
);
// key must match event id
const eventId = `${group[0].beamline}-${group[0].start_date}-${group[0].pgroups}`;
assoc[eventId] = { assoc[eventId] = {
dewars: dewars.map((d: any) => d.id), dewars: Array.from(dewarsSet),
pucks: pucks.map((p: any) => p.id), pucks: Array.from(pucksSet),
}; };
}) })
); );
@ -119,20 +146,29 @@ const Calendar: React.FC = () => {
}, [eventDetails]); }, [eventDetails]);
// Refresh associations after (un)assign action // Refresh associations after (un)assign action
const refetchEventAssociations = async (beamtimeId: number, eventId: string) => { const refetchEventAssociations = async (beamtimeIds: number[], eventId: string) => {
let dewarsSet = new Set<string>();
let pucksSet = new Set<string>();
await Promise.all(
beamtimeIds.map(async (beamtimeId: number) => {
const [dewars, pucks] = await Promise.all([ const [dewars, pucks] = await Promise.all([
DewarsService.getDewarsByBeamtime(beamtimeId), DewarsService.getDewarsByBeamtime(beamtimeId),
PucksService.getPucksByBeamtime(beamtimeId), PucksService.getPucksByBeamtime(beamtimeId),
]); ]);
dewars.forEach((d: any) => dewarsSet.add(d.id));
pucks.forEach((p: any) => pucksSet.add(p.id));
})
);
setEventAssociations(prev => ({ setEventAssociations(prev => ({
...prev, ...prev,
[eventId]: { [eventId]: {
dewars: dewars.map((d: any) => d.id), dewars: Array.from(dewarsSet),
pucks: pucks.map((p: any) => p.id), pucks: Array.from(pucksSet),
} }
})); }));
}; };
const handleEventClick = (eventInfo: any) => { const handleEventClick = (eventInfo: any) => {
const clickedEventId = eventInfo.event.id; const clickedEventId = eventInfo.event.id;
setSelectedEventId(clickedEventId); setSelectedEventId(clickedEventId);
@ -171,49 +207,53 @@ const Calendar: React.FC = () => {
const handleDewarAssignment = async (dewarId: string) => { const handleDewarAssignment = async (dewarId: string) => {
if (!selectedEventId) return; if (!selectedEventId) return;
const event = events.find(e => e.id === selectedEventId)!; const event = events.find(e => e.id === selectedEventId)!;
const beamtimeId = event.beamtime_id; const beamtimeIds: number[] = event.beamtime_ids || [];
if (!beamtimeId) return; if (!beamtimeIds.length) return;
// Is this dewar already assigned here?
const assigned = eventAssociations[selectedEventId]?.dewars.includes(dewarId); const assigned = eventAssociations[selectedEventId]?.dewars.includes(dewarId);
try { try {
if (assigned) { await Promise.all(
await DewarsService.assignDewarToBeamtime(Number(dewarId), 0); beamtimeIds.map(btId =>
} else { assigned
await DewarsService.assignDewarToBeamtime(Number(dewarId), Number(beamtimeId)); ? DewarsService.assignDewarToBeamtime(Number(dewarId), 0)
} : DewarsService.assignDewarToBeamtime(Number(dewarId), Number(btId))
await refetchEventAssociations(Number(beamtimeId), selectedEventId); )
);
await refetchEventAssociations(beamtimeIds, selectedEventId);
} catch (e) { } catch (e) {
// Optionally report error /* error handling */}
}
}; };
// Unified assign/unassign for Pucks // Unified assign/unassign for Pucks
const handlePuckAssignment = async (puckId: string) => { const handlePuckAssignment = async (puckId: string) => {
if (!selectedEventId) return; if (!selectedEventId) return;
const event = events.find(e => e.id === selectedEventId)!; const event = events.find(e => e.id === selectedEventId)!;
const beamtimeId = event.beamtime_id; const beamtimeIds: number[] = event.beamtime_ids || [];
if (!beamtimeId) return; if (!beamtimeIds.length) return;
const assigned = eventAssociations[selectedEventId]?.pucks.includes(puckId); const assigned = eventAssociations[selectedEventId]?.pucks.includes(puckId);
try { try {
if (assigned) { await Promise.all(
await PucksService.assignPuckToBeamtime(Number(puckId), 0); beamtimeIds.map(async btId =>
} else { assigned
await PucksService.assignPuckToBeamtime(Number(puckId), Number(beamtimeId)); ? PucksService.assignPuckToBeamtime(Number(puckId), 0)
} : PucksService.assignPuckToBeamtime(Number(puckId), Number(btId))
await refetchEventAssociations(Number(beamtimeId), selectedEventId); )
} catch (e) { );
// Optionally report error await refetchEventAssociations(beamtimeIds, selectedEventId);
} } catch (e) {/* error handling */}
}; };
// For displaying badge in calendar and UI // For displaying badge in calendar and UI
const eventContent = (eventInfo: any) => { const eventContent = (eventInfo: any) => {
const beamtimesInGroup = eventInfo.event.extendedProps.beamtimes
? eventInfo.event.extendedProps.beamtimes.length
: 1;
const minHeight = beamtimesInGroup * 26;
const beamline = eventInfo.event.extendedProps.beamline || 'Unknown'; const beamline = eventInfo.event.extendedProps.beamline || 'Unknown';
const isSelected = selectedEventId === eventInfo.event.id; const isSelected = selectedEventId === eventInfo.event.id;
const isSubmitted = eventInfo.event.extendedProps.isSubmitted; const isSubmitted = eventInfo.event.extendedProps.isSubmitted;
const hasAssociations = const assoc = eventAssociations[eventInfo.event.id] || { dewars: [], pucks: [] };
eventAssociations[eventInfo.event.id]?.dewars.length > 0 ||
eventAssociations[eventInfo.event.id]?.pucks.length > 0;
const backgroundColor = isSubmitted const backgroundColor = isSubmitted
? darkenColor(beamlineColors[beamline] || beamlineColors.Unknown, -20) ? darkenColor(beamlineColors[beamline] || beamlineColors.Unknown, -20)
: isSelected : isSelected
@ -228,21 +268,55 @@ const Calendar: React.FC = () => {
border: isSelected ? '2px solid black' : 'none', border: isSelected ? '2px solid black' : 'none',
borderRadius: '3px', borderRadius: '3px',
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'space-between',
alignItems: 'center', alignItems: 'center',
height: '100%', height: '100%',
width: '100%', width: '100%',
cursor: 'pointer', cursor: 'pointer',
overflow: 'hidden', overflow: 'hidden',
boxSizing: 'border-box', boxSizing: 'border-box',
padding: '0 6px',
minHeight: `${minHeight}px`,
}} }}
> >
<span style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{eventInfo.event.title} {eventInfo.event.title}
{hasAssociations && <span style={{marginLeft: 8}}>🧊</span>} </span>
<span style={{ display: 'flex', alignItems: 'center', gap: 6, marginLeft: 8 }}>
<span title="Dewars" style={{ display: 'flex', alignItems: 'center', fontSize: 13 }}>
🧊
<span style={{
background: 'rgba(0,0,0,0.45)',
borderRadius: '8px',
marginLeft: 2,
minWidth: 14,
color: '#fff',
fontSize: 12,
padding: '0 4px',
fontWeight: 600,
textAlign: 'center'
}}>{assoc.dewars.length}</span>
</span>
<span title="Pucks" style={{ display: 'flex', alignItems: 'center', fontSize: 13 }}>
<span style={{
background: 'rgba(0,0,0,0.45)',
borderRadius: '8px',
marginLeft: 2,
minWidth: 14,
color: '#fff',
fontSize: 12,
padding: '0 4px',
fontWeight: 600,
textAlign: 'center'
}}>{assoc.pucks.length}</span>
</span>
</span>
</div> </div>
); );
}; };
// Used in Dewar/Puck assign status reporting // Used in Dewar/Puck assign status reporting
function getAssignedEventForDewar(dewarId: string) { function getAssignedEventForDewar(dewarId: string) {
return Object.entries(eventAssociations).find(([eid, assoc]) => return Object.entries(eventAssociations).find(([eid, assoc]) =>

View File

@ -446,21 +446,21 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-05-05T13:31:12.902282Z", "end_time": "2025-05-08T13:31:36.929465Z",
"start_time": "2025-05-05T13:31:12.900432Z" "start_time": "2025-05-08T13:31:36.925054Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
"source": "sample_id = 277", "source": "sample_id = 44",
"id": "54d4d46ca558e7b9", "id": "54d4d46ca558e7b9",
"outputs": [], "outputs": [],
"execution_count": 7 "execution_count": 28
}, },
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-05-05T13:31:16.752616Z", "end_time": "2025-05-08T13:31:40.023546Z",
"start_time": "2025-05-05T13:31:16.720296Z" "start_time": "2025-05-08T13:31:39.978510Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@ -512,7 +512,7 @@
"DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): localhost:8000\n", "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): localhost:8000\n",
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
" warnings.warn(\n", " warnings.warn(\n",
"DEBUG:urllib3.connectionpool:https://localhost:8000 \"POST /samples/samples/277/events HTTP/1.1\" 200 413\n" "DEBUG:urllib3.connectionpool:https://localhost:8000 \"POST /samples/samples/44/events HTTP/1.1\" 200 884\n"
] ]
}, },
{ {
@ -522,29 +522,29 @@
"Payload being sent to API:\n", "Payload being sent to API:\n",
"{\"event_type\":\"Collecting\"}\n", "{\"event_type\":\"Collecting\"}\n",
"API response:\n", "API response:\n",
"('id', 277)\n", "('id', 44)\n",
"('sample_name', 'Sample277')\n", "('sample_name', 'Sample044')\n",
"('position', 15)\n", "('position', 7)\n",
"('puck_id', 30)\n", "('puck_id', 7)\n",
"('crystalname', None)\n", "('crystalname', None)\n",
"('proteinname', None)\n", "('proteinname', None)\n",
"('positioninpuck', None)\n", "('positioninpuck', None)\n",
"('priority', None)\n", "('priority', None)\n",
"('comments', None)\n", "('comments', None)\n",
"('data_collection_parameters', None)\n", "('data_collection_parameters', None)\n",
"('events', [SampleEventResponse(event_type='Mounting', id=533, sample_id=277, timestamp=datetime.datetime(2025, 5, 4, 14, 9)), SampleEventResponse(event_type='Collecting', id=534, sample_id=277, timestamp=datetime.datetime(2025, 5, 5, 13, 31, 16, 741949))])\n", "('events', [SampleEventResponse(event_type='Mounting', id=87, sample_id=44, timestamp=datetime.datetime(2025, 5, 7, 10, 16)), SampleEventResponse(event_type='Unmounting', id=88, sample_id=44, timestamp=datetime.datetime(2025, 5, 7, 10, 16, 50)), SampleEventResponse(event_type='Collecting', id=507, sample_id=44, timestamp=datetime.datetime(2025, 5, 8, 13, 31, 40, 6059))])\n",
"('mount_count', 0)\n", "('mount_count', 0)\n",
"('unmount_count', 0)\n" "('unmount_count', 0)\n"
] ]
} }
], ],
"execution_count": 8 "execution_count": 29
}, },
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-04-29T14:27:46.730515Z", "end_time": "2025-05-08T13:31:43.663278Z",
"start_time": "2025-04-29T14:27:46.622922Z" "start_time": "2025-05-08T13:31:43.651369Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@ -572,18 +572,18 @@
"traceback": [ "traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)", "\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)",
"Cell \u001B[0;32mIn[48], line 8\u001B[0m\n\u001B[1;32m 4\u001B[0m api_instance \u001B[38;5;241m=\u001B[39m aareDBclient\u001B[38;5;241m.\u001B[39mSamplesApi(api_client)\n\u001B[1;32m 6\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 7\u001B[0m \u001B[38;5;66;03m# Get the last sample event\u001B[39;00m\n\u001B[0;32m----> 8\u001B[0m last_event_response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_last_sample_event_samples_samples_sample_id_events_last_get\u001B[49m(\u001B[38;5;241m27\u001B[39m)\n\u001B[1;32m 9\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mThe response of get_last_sample_event:\u001B[39m\u001B[38;5;130;01m\\n\u001B[39;00m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m 10\u001B[0m pprint(last_event_response)\n", "Cell \u001B[0;32mIn[30], line 8\u001B[0m\n\u001B[1;32m 4\u001B[0m api_instance \u001B[38;5;241m=\u001B[39m aareDBclient\u001B[38;5;241m.\u001B[39mSamplesApi(api_client)\n\u001B[1;32m 6\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[1;32m 7\u001B[0m \u001B[38;5;66;03m# Get the last sample event\u001B[39;00m\n\u001B[0;32m----> 8\u001B[0m last_event_response \u001B[38;5;241m=\u001B[39m \u001B[43mapi_instance\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mget_last_sample_event_samples_samples_sample_id_events_last_get\u001B[49m(\u001B[38;5;241m27\u001B[39m)\n\u001B[1;32m 9\u001B[0m \u001B[38;5;28mprint\u001B[39m(\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mThe response of get_last_sample_event:\u001B[39m\u001B[38;5;130;01m\\n\u001B[39;00m\u001B[38;5;124m\"\u001B[39m)\n\u001B[1;32m 10\u001B[0m pprint(last_event_response)\n",
"\u001B[0;31mAttributeError\u001B[0m: 'SamplesApi' object has no attribute 'get_last_sample_event_samples_samples_sample_id_events_last_get'" "\u001B[0;31mAttributeError\u001B[0m: 'SamplesApi' object has no attribute 'get_last_sample_event_samples_samples_sample_id_events_last_get'"
] ]
} }
], ],
"execution_count": 48 "execution_count": 30
}, },
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-05-05T13:31:21.833174Z", "end_time": "2025-05-08T13:31:46.103881Z",
"start_time": "2025-05-05T13:31:21.791711Z" "start_time": "2025-05-08T13:31:46.061151Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@ -654,7 +654,7 @@
"DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): 127.0.0.1:8000\n", "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): 127.0.0.1:8000\n",
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: 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", "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: 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", " warnings.warn(\n",
"DEBUG:urllib3.connectionpool:https://127.0.0.1:8000 \"POST /samples/277/upload-images HTTP/1.1\" 200 205\n" "DEBUG:urllib3.connectionpool:https://127.0.0.1:8000 \"POST /samples/44/upload-images HTTP/1.1\" 200 203\n"
] ]
}, },
{ {
@ -664,11 +664,11 @@
"Uploading after_dc.jpeg.jpg...\n", "Uploading after_dc.jpeg.jpg...\n",
"API Response for after_dc.jpeg.jpg:\n", "API Response for after_dc.jpeg.jpg:\n",
"200\n", "200\n",
"{'pgroup': 'p20003', 'sample_id': 277, 'sample_event_id': 534, 'filepath': 'images/p20003/2025-05-05/Dewar Five/PKK007/15/Collecting_2025-05-05_13-31-16/after_dc.jpeg.jpg', 'status': 'active', 'comment': None, 'id': 1}\n" "{'pgroup': 'p20001', 'sample_id': 44, 'sample_event_id': 507, 'filepath': 'images/p20001/2025-05-08/Dewar One/PUCK007/7/Collecting_2025-05-08_13-31-40/after_dc.jpeg.jpg', 'status': 'active', 'comment': None, 'id': 1}\n"
] ]
} }
], ],
"execution_count": 9 "execution_count": 31
}, },
{ {
"metadata": {}, "metadata": {},
@ -681,8 +681,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-05-05T13:34:32.164108Z", "end_time": "2025-05-08T13:53:49.337315Z",
"start_time": "2025-05-05T13:34:32.130230Z" "start_time": "2025-05-08T13:53:49.288039Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@ -799,7 +799,7 @@
"DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): localhost:8000\n", "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): localhost:8000\n",
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
" warnings.warn(\n", " warnings.warn(\n",
"DEBUG:urllib3.connectionpool:https://localhost:8000 \"POST /samples/samples/277/experiment_parameters HTTP/1.1\" 200 904\n" "DEBUG:urllib3.connectionpool:https://localhost:8000 \"POST /samples/samples/44/experiment_parameters HTTP/1.1\" 200 903\n"
] ]
}, },
{ {
@ -807,17 +807,17 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"API Response:\n", "API Response:\n",
"run_number=2 type='standard' beamline_parameters=BeamlineParameters(synchrotron='Swiss Light Source', beamline='PXIII', detector=Detector(manufacturer='DECTRIS', model='PILATUS4 2M', type='photon-counting', serial_number='16684dscsd668468', detector_distance_mm=232.0, beam_center_x_px=768.0, beam_center_y_px=857.0, pixel_size_x_um=150.0, pixel_size_y_um=150.0), wavelength=1.033, ring_current_a=0.4, ring_mode='Machine Development', undulator=None, undulatorgap_mm=None, monochromator='Si111', transmission=10.0, focusing_optic='Kirkpatrick-Baez', beamline_flux_at_sample_ph_s=0.0, beam_size_width=30.0, beam_size_height=30.0, characterization=None, rotation=RotationParameters(omega_start_deg=0.0, omega_step=0.2, chi=0.0, phi=10.0, number_of_images=1800, exposure_time_s=0.01), grid_scan=None, jet=None, cryojet_temperature_k=None, humidifier_temperature_k=None, humidifier_humidity=None) dataset=None sample_id=277 id=2\n" "run_number=2 type='standard' beamline_parameters=BeamlineParameters(synchrotron='Swiss Light Source', beamline='PXIII', detector=Detector(manufacturer='DECTRIS', model='PILATUS4 2M', type='photon-counting', serial_number='16684dscsd668468', detector_distance_mm=232.0, beam_center_x_px=768.0, beam_center_y_px=857.0, pixel_size_x_um=150.0, pixel_size_y_um=150.0), wavelength=1.033, ring_current_a=0.4, ring_mode='Machine Development', undulator=None, undulatorgap_mm=None, monochromator='Si111', transmission=10.0, focusing_optic='Kirkpatrick-Baez', beamline_flux_at_sample_ph_s=0.0, beam_size_width=30.0, beam_size_height=30.0, characterization=None, rotation=RotationParameters(omega_start_deg=0.0, omega_step=0.2, chi=0.0, phi=10.0, number_of_images=1800, exposure_time_s=0.01), grid_scan=None, jet=None, cryojet_temperature_k=None, humidifier_temperature_k=None, humidifier_humidity=None) dataset=None sample_id=44 id=2\n"
] ]
} }
], ],
"execution_count": 13 "execution_count": 34
}, },
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-05-05T13:34:49.891432Z", "end_time": "2025-05-08T13:53:58.864551Z",
"start_time": "2025-05-05T13:34:49.872697Z" "start_time": "2025-05-08T13:53:58.837176Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@ -859,7 +859,7 @@
"DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): localhost:8000\n", "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): localhost:8000\n",
"/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n", "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/urllib3/connectionpool.py:1103: InsecureRequestWarning: Unverified HTTPS request is being made to host 'localhost'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings\n",
" warnings.warn(\n", " warnings.warn(\n",
"DEBUG:urllib3.connectionpool:https://localhost:8000 \"PATCH /samples/update-dataset/277/2 HTTP/1.1\" 200 1085\n" "DEBUG:urllib3.connectionpool:https://localhost:8000 \"PATCH /samples/update-dataset/44/2 HTTP/1.1\" 200 1084\n"
] ]
}, },
{ {
@ -867,11 +867,11 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Dataset updated successfully:\n", "Dataset updated successfully:\n",
"run_number=2 type='standard' beamline_parameters=BeamlineParameters(synchrotron='Swiss Light Source', beamline='PXIII', detector=Detector(manufacturer='DECTRIS', model='PILATUS4 2M', type='photon-counting', serial_number='16684dscsd668468', detector_distance_mm=232.0, beam_center_x_px=768.0, beam_center_y_px=857.0, pixel_size_x_um=150.0, pixel_size_y_um=150.0), wavelength=1.033, ring_current_a=0.4, ring_mode='Machine Development', undulator=None, undulatorgap_mm=None, monochromator='Si111', transmission=10.0, focusing_optic='Kirkpatrick-Baez', beamline_flux_at_sample_ph_s=0.0, beam_size_width=30.0, beam_size_height=30.0, characterization=None, rotation=RotationParameters(omega_start_deg=0.0, omega_step=0.2, chi=0.0, phi=10.0, number_of_images=1800, exposure_time_s=0.01), grid_scan=None, jet=None, cryojet_temperature_k=None, humidifier_temperature_k=None, humidifier_humidity=None) dataset=Datasets(filepath='/das/work/p11/p11206/raw_data/vincent/20250415_6D_SLS2_1st_data/20250415_fullbeam_dtz220_Lyso102_again_360deg', status='written', written_at=datetime.datetime(2025, 5, 5, 15, 34, 49, 874526)) sample_id=277 id=2\n" "run_number=2 type='standard' beamline_parameters=BeamlineParameters(synchrotron='Swiss Light Source', beamline='PXIII', detector=Detector(manufacturer='DECTRIS', model='PILATUS4 2M', type='photon-counting', serial_number='16684dscsd668468', detector_distance_mm=232.0, beam_center_x_px=768.0, beam_center_y_px=857.0, pixel_size_x_um=150.0, pixel_size_y_um=150.0), wavelength=1.033, ring_current_a=0.4, ring_mode='Machine Development', undulator=None, undulatorgap_mm=None, monochromator='Si111', transmission=10.0, focusing_optic='Kirkpatrick-Baez', beamline_flux_at_sample_ph_s=0.0, beam_size_width=30.0, beam_size_height=30.0, characterization=None, rotation=RotationParameters(omega_start_deg=0.0, omega_step=0.2, chi=0.0, phi=10.0, number_of_images=1800, exposure_time_s=0.01), grid_scan=None, jet=None, cryojet_temperature_k=None, humidifier_temperature_k=None, humidifier_humidity=None) dataset=Datasets(filepath='/das/work/p11/p11206/raw_data/vincent/20250415_6D_SLS2_1st_data/20250415_fullbeam_dtz220_Lyso102_again_360deg', status='written', written_at=datetime.datetime(2025, 5, 8, 15, 53, 58, 838599)) sample_id=44 id=2\n"
] ]
} }
], ],
"execution_count": 15 "execution_count": 35
}, },
{ {
"metadata": { "metadata": {