Add support for data collection parameters across layers

Introduced serialization for `data_collection_parameters` in backend models and processing. Added logic to parse and attach data collection parameters in the frontend. This ensures consistent handling and storage of these parameters throughout the application.
This commit is contained in:
GotthardG
2025-01-08 09:19:23 +01:00
parent 35369fd13c
commit 9b4f8599f3
3 changed files with 60 additions and 4 deletions

View File

@ -8,7 +8,7 @@ from sqlalchemy.orm import Session, joinedload
from typing import List from typing import List
import logging import logging
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from pydantic import ValidationError from pydantic import ValidationError, BaseModel
from app.schemas import ( from app.schemas import (
Dewar as DewarSchema, Dewar as DewarSchema,
DewarCreate, DewarCreate,
@ -88,6 +88,26 @@ async def create_dewar(
db.refresh(puck) db.refresh(puck)
for sample_data in puck_data.samples: for sample_data in puck_data.samples:
logging.debug(
f"data_collection_parameters: "
f"{sample_data.data_collection_parameters}"
)
if sample_data.data_collection_parameters is None:
serialized_params = {}
elif hasattr(sample_data.data_collection_parameters, "to_dict"):
serialized_params = sample_data.data_collection_parameters.to_dict()
elif isinstance(sample_data.data_collection_parameters, BaseModel):
serialized_params = sample_data.data_collection_parameters.dict(
exclude_unset=True
)
elif isinstance(sample_data.data_collection_parameters, dict):
serialized_params = sample_data.data_collection_parameters
else:
raise ValueError(
"data_collection_parameters must be a dictionary,"
"have a to_dict method, or be None"
)
sample = SampleModel( sample = SampleModel(
puck_id=puck.id, puck_id=puck.id,
sample_name=sample_data.sample_name, sample_name=sample_data.sample_name,
@ -95,7 +115,7 @@ async def create_dewar(
position=sample_data.position, position=sample_data.position,
priority=sample_data.priority, priority=sample_data.priority,
comments=sample_data.comments, comments=sample_data.comments,
data_collection_parameters=sample_data.data_collection_parameters, data_collection_parameters=serialized_params,
) )
db.add(sample) db.add(sample)
db.commit() db.commit()

View File

@ -91,6 +91,12 @@ class DataCollectionParameters(BaseModel):
chiphiangles: Optional[float] = None # Optional float field between 0 and 30 chiphiangles: Optional[float] = None # Optional float field between 0 and 30
dose: Optional[float] = None # Optional float field dose: Optional[float] = None # Optional float field
def to_dict(self):
"""Convert the model instance to a dictionary."""
return self.dict(
exclude_unset=True
) # Use this built-in method for serialization
class Config: class Config:
from_attributes = True from_attributes = True
@ -416,6 +422,7 @@ class Sample(BaseModel):
positioninpuck: Optional[int] = Field(None) positioninpuck: Optional[int] = Field(None)
priority: Optional[int] = None priority: Optional[int] = None
comments: Optional[str] = None comments: Optional[str] = None
data_collection_parameters: Optional[DataCollectionParameters]
events: List[SampleEventCreate] = [] events: List[SampleEventCreate] = []
@ -423,7 +430,7 @@ class SampleCreate(BaseModel):
sample_name: str = Field(..., alias="crystalname") sample_name: str = Field(..., alias="crystalname")
proteinname: Optional[str] = None proteinname: Optional[str] = None
position: int = Field(..., alias="positioninpuck") position: int = Field(..., alias="positioninpuck")
data_collection_parameters: Optional[DataCollectionParameters] = None data_collection_parameters: Optional[DataCollectionParameters] = Field(default=None)
priority: Optional[int] = None priority: Optional[int] = None
comments: Optional[str] = None comments: Optional[str] = None
results: Optional[Results] = None results: Optional[Results] = None

View File

@ -247,6 +247,7 @@ const SpreadsheetTable = ({
continue; continue;
} }
// Extract values from the appropriate columns
const dewarName = typeof row.data[dewarNameIdx] === 'string' ? row.data[dewarNameIdx].trim() : null; const dewarName = typeof row.data[dewarNameIdx] === 'string' ? row.data[dewarNameIdx].trim() : null;
const puckName = row.data[puckNameIdx] !== undefined && row.data[puckNameIdx] !== null ? String(row.data[puckNameIdx]).trim() : null; const puckName = row.data[puckNameIdx] !== undefined && row.data[puckNameIdx] !== null ? String(row.data[puckNameIdx]).trim() : null;
const puckType = typeof row.data[puckTypeIdx] === 'string' ? row.data[puckTypeIdx] : 'Unipuck'; const puckType = typeof row.data[puckTypeIdx] === 'string' ? row.data[puckTypeIdx] : 'Unipuck';
@ -256,6 +257,34 @@ const SpreadsheetTable = ({
const priority = row?.data?.[priorityIdx] ? Number(row.data[priorityIdx]) : null; const priority = row?.data?.[priorityIdx] ? Number(row.data[priorityIdx]) : null;
const comments = typeof row.data[commentsIdx] === 'string' ? row.data[commentsIdx].trim() : null; const comments = typeof row.data[commentsIdx] === 'string' ? row.data[commentsIdx].trim() : null;
// Create data_collection_parameters object
const dataCollectionParameters = {
directory: row.data[fieldToCol['directory']],
oscillation: row.data[fieldToCol['oscillation']] ? parseFloat(row.data[fieldToCol['oscillation']]) : undefined,
aperture: row.data[fieldToCol['aperture']] ? row.data[fieldToCol['aperture']].trim() : undefined,
exposure: row.data[fieldToCol['exposure']] ? parseFloat(row.data[fieldToCol['exposure']]) : undefined,
totalrange: row.data[fieldToCol['totalrange']] ? parseInt(row.data[fieldToCol['totalrange']], 10) : undefined,
transmission: row.data[fieldToCol['transmission']] ? parseInt(row.data[fieldToCol['transmission']], 10) : undefined,
dose: row.data[fieldToCol['dose']] ? parseFloat(row.data[fieldToCol['dose']]) : undefined,
targetresolution: row.data[fieldToCol['targetresolution']] ? parseFloat(row.data[fieldToCol['targetresolution']]) : undefined,
datacollectiontype: row.data[fieldToCol['datacollectiontype']],
processingpipeline: row.data[fieldToCol['processingpipeline']],
spacegroupnumber: row.data[fieldToCol['spacegroupnumber']] ? parseInt(row.data[fieldToCol['spacegroupnumber']], 10) : undefined,
cellparameters: row.data[fieldToCol['cellparameters']],
rescutkey: row.data[fieldToCol['rescutkey']],
rescutvalue: row.data[fieldToCol['rescutvalue']] ? parseFloat(row.data[fieldToCol['rescutvalue']]) : undefined,
userresolution: row.data[fieldToCol['userresolution']] ? parseFloat(row.data[fieldToCol['userresolution']]) : undefined,
pdbid: row.data[fieldToCol['pdbid']],
autoprocfull: row.data[fieldToCol['autoprocfull']] === true,
procfull: row.data[fieldToCol['procfull']] === true,
adpenabled: row.data[fieldToCol['adpenabled']] === true,
noano: row.data[fieldToCol['noano']] === true,
ffcscampaign: row.data[fieldToCol['ffcscampaign']] === true,
trustedhigh: row.data[fieldToCol['trustedhigh']] ? parseFloat(row.data[fieldToCol['trustedhigh']]) : undefined,
autoprocextraparams: row.data[fieldToCol['autoprocextraparams']],
chiphiangles: row.data[fieldToCol['chiphiangles']] ? parseFloat(row.data[fieldToCol['chiphiangles']]) : undefined,
};
if (dewarName && puckName) { if (dewarName && puckName) {
let dewar; let dewar;
if (!dewars.has(dewarName)) { if (!dewars.has(dewarName)) {
@ -301,7 +330,7 @@ const SpreadsheetTable = ({
position: samplePosition, position: samplePosition,
priority: priority, priority: priority,
comments: comments, comments: comments,
data_collection_parameters: null, data_collection_parameters: dataCollectionParameters, // Attach the parameters
results: null // Placeholder for results field results: null // Placeholder for results field
}; };