from typing import List, Optional from datetime import datetime from pydantic import BaseModel, EmailStr, constr, Field from datetime import date class loginToken(BaseModel): access_token: str token_type: str class loginData(BaseModel): username: str pgroups: List[int] class DewarTypeBase(BaseModel): dewar_type: str class DewarTypeCreate(DewarTypeBase): pass class DewarType(DewarTypeBase): id: int class Config: from_attributes = True class DewarSerialNumberBase(BaseModel): serial_number: str dewar_type_id: int class DewarSerialNumberCreate(DewarSerialNumberBase): pass class DewarSerialNumber(DewarSerialNumberBase): id: int dewar_type: DewarType class Config: from_attributes = True class DataCollectionParameters(BaseModel): priority: Optional[int] = None comments: Optional[str] = None directory: Optional[str] = None proteinname: Optional[str] = None oscillation: Optional[float] = None aperture: Optional[str] = None exposure: Optional[float] = None totalrange: Optional[int] = None transmission: Optional[int] = None dose: Optional[float] = None targetresolution: Optional[float] = None datacollectiontype: Optional[str] = None processingpipeline: Optional[str] = None spacegroupnumber: Optional[int] = None cellparameters: Optional[str] = None rescutkey: Optional[str] = None rescutvalue: Optional[float] = None userresolution: Optional[float] = None pdbid: Optional[str] = None autoprocfull: Optional[bool] = None procfull: Optional[bool] = None adpenabled: Optional[bool] = None noano: Optional[bool] = None ffcscampaign: Optional[bool] = None trustedhigh: Optional[float] = None autoprocextraparams: Optional[str] = None chiphiangles: Optional[float] = None class Config: from_attributes = True class SampleEventCreate(BaseModel): event_type: str class Results(BaseModel): # Define attributes for Results here pass class ContactPersonBase(BaseModel): firstname: str lastname: str phone_number: str email: EmailStr class ContactPersonCreate(ContactPersonBase): pass class ContactPerson(ContactPersonBase): id: int class Config: from_attributes = True class ContactPersonUpdate(BaseModel): firstname: Optional[str] = None lastname: Optional[str] = None phone_number: Optional[str] = None email: Optional[EmailStr] = None class AddressCreate(BaseModel): street: str city: str zipcode: str country: str class Address(AddressCreate): id: int class Config: from_attributes = True class AddressUpdate(BaseModel): street: Optional[str] = None city: Optional[str] = None zipcode: Optional[str] = None country: Optional[str] = None class Sample(BaseModel): id: int sample_name: str position: int # Position within the puck puck_id: int crystalname: Optional[str] = Field(None) positioninpuck: Optional[int] = Field(None) events: List[SampleEventCreate] = [] class SampleCreate(BaseModel): sample_name: str = Field(..., alias="crystalname") position: int = Field(..., alias="positioninpuck") data_collection_parameters: Optional[DataCollectionParameters] = None results: Optional[Results] = None events: Optional[List[str]] = None class Config: populate_by_name = True class PuckEvent(BaseModel): id: int puck_id: int tell_position: Optional[str] = None event_type: str timestamp: datetime class Config: from_attributes = True class PuckBase(BaseModel): puck_name: str puck_type: str puck_location_in_dewar: int class PuckCreate(BaseModel): puck_name: str puck_type: str puck_location_in_dewar: int samples: List[SampleCreate] = [] class PuckUpdate(BaseModel): puck_name: Optional[str] = None puck_type: Optional[str] = None puck_location_in_dewar: Optional[int] = None dewar_id: Optional[int] = None class Puck(BaseModel): id: int puck_name: str puck_type: str puck_location_in_dewar: int dewar_id: int events: List[PuckEvent] = [] samples: List[Sample] = [] # List of samples within this puck class Config: from_attributes = True class DewarBase(BaseModel): dewar_name: str dewar_type_id: Optional[int] = None dewar_serial_number_id: Optional[int] = None unique_id: Optional[str] = None tracking_number: str number_of_pucks: int number_of_samples: int status: str ready_date: Optional[date] shipping_date: Optional[date] arrival_date: Optional[date] returning_date: Optional[date] contact_person_id: Optional[int] return_address_id: Optional[int] pucks: List[PuckCreate] = [] class DewarCreate(DewarBase): pass class Dewar(DewarBase): id: int shipment_id: Optional[int] contact_person: Optional[ContactPerson] return_address: Optional[Address] pucks: List[Puck] = [] # List of pucks within this dewar class Config: from_attributes = True class DewarUpdate(BaseModel): dewar_name: Optional[str] = None dewar_type_id: Optional[int] = None dewar_serial_number_id: Optional[int] = None unique_id: Optional[str] = None tracking_number: Optional[str] = None status: Optional[str] = None ready_date: Optional[date] = None shipping_date: Optional[date] = None arrival_date: Optional[date] = None returning_date: Optional[date] = None contact_person_id: Optional[int] = None address_id: Optional[int] = None class DewarSchema(BaseModel): id: int dewar_name: str tracking_number: str status: str contact_person_id: int return_address_id: int class Config: from_attributes = True class Proposal(BaseModel): id: int number: str class Config: from_attributes = True class Shipment(BaseModel): id: int shipment_name: str shipment_date: date shipment_status: str comments: Optional[str] contact_person: Optional[ContactPerson] return_address: Optional[Address] proposal: Optional[Proposal] dewars: List[Dewar] = [] class Config: from_attributes = True class ShipmentCreate(BaseModel): shipment_name: str shipment_date: date shipment_status: str comments: Optional[constr(max_length=200)] contact_person_id: int return_address_id: int proposal_id: int dewars: List[DewarCreate] = [] class Config: from_attributes = True class UpdateShipmentComments(BaseModel): comments: str class LogisticsEventCreate(BaseModel): dewar_qr_code: str location_qr_code: str transaction_type: str class SlotSchema(BaseModel): id: int qr_code: str label: str qr_base: Optional[str] occupied: bool needs_refill: bool dewar_unique_id: Optional[str] dewar_name: Optional[str] time_until_refill: Optional[int] at_beamline: Optional[bool] retrievedTimestamp: Optional[str] beamlineLocation: Optional[str] shipment_name: Optional[str] contact_person: Optional[str] local_contact: Optional[str] class Config: from_attributes = True class SetTellPosition(BaseModel): tell_position: str = Field( ..., pattern="^[A-F][1-5]$|^null$|^None$", # Use 'pattern' instead of 'regex' description="Valid values are A1-A5, B1-B5, ..., F1-F5, or null.", )