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:
parent
0fa038be94
commit
6a0953c913
@ -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"]
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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"}
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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]) =>
|
||||||
|
@ -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": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user