Update dependencies and migrate to Node 18 minimum support
Upgraded multiple package versions, including `@esbuild` and dependencies like `@mui/x-data-grid-premium`. Adjusted `node` engine requirement to `>=18` for compatibility. This ensures modernization and alignment with current toolchain standards.
This commit is contained in:
parent
c3cf463f06
commit
2e1d87c31b
@ -260,6 +260,14 @@ class Image(Base):
|
|||||||
sample_id = Column(Integer, ForeignKey("samples.id"), nullable=False)
|
sample_id = Column(Integer, ForeignKey("samples.id"), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class ExperimentParameters(Base):
|
||||||
|
__tablename__ = "experiment_parameters"
|
||||||
|
id = Column(Integer, primary_key=True, index=True, autoincrement=True)
|
||||||
|
run_number = Column(Integer, nullable=False)
|
||||||
|
beamline_parameters = Column(JSON, nullable=True)
|
||||||
|
sample_id = Column(Integer, ForeignKey("samples.id"), nullable=False)
|
||||||
|
|
||||||
|
|
||||||
# class Results(Base):
|
# class Results(Base):
|
||||||
# __tablename__ = "results"
|
# __tablename__ = "results"
|
||||||
#
|
#
|
||||||
|
@ -12,6 +12,8 @@ from app.schemas import (
|
|||||||
Image,
|
Image,
|
||||||
ImageCreate,
|
ImageCreate,
|
||||||
SampleResult,
|
SampleResult,
|
||||||
|
ExperimentParametersCreate,
|
||||||
|
ExperimentParametersRead,
|
||||||
)
|
)
|
||||||
from app.models import (
|
from app.models import (
|
||||||
Puck as PuckModel,
|
Puck as PuckModel,
|
||||||
@ -19,6 +21,7 @@ from app.models import (
|
|||||||
SampleEvent as SampleEventModel,
|
SampleEvent as SampleEventModel,
|
||||||
Image as ImageModel,
|
Image as ImageModel,
|
||||||
Dewar as DewarModel,
|
Dewar as DewarModel,
|
||||||
|
ExperimentParameters as ExperimentParametersModel,
|
||||||
)
|
)
|
||||||
from app.dependencies import get_db
|
from app.dependencies import get_db
|
||||||
import logging
|
import logging
|
||||||
@ -192,7 +195,7 @@ async def upload_sample_image(
|
|||||||
|
|
||||||
@router.get("/results", response_model=List[SampleResult])
|
@router.get("/results", response_model=List[SampleResult])
|
||||||
async def get_sample_results(active_pgroup: str, db: Session = Depends(get_db)):
|
async def get_sample_results(active_pgroup: str, db: Session = Depends(get_db)):
|
||||||
# Query samples for the active pgroup using joins
|
# Query samples for the active pgroup using joins.
|
||||||
samples = (
|
samples = (
|
||||||
db.query(SampleModel)
|
db.query(SampleModel)
|
||||||
.join(SampleModel.puck)
|
.join(SampleModel.puck)
|
||||||
@ -207,8 +210,18 @@ async def get_sample_results(active_pgroup: str, db: Session = Depends(get_db)):
|
|||||||
|
|
||||||
results = []
|
results = []
|
||||||
for sample in samples:
|
for sample in samples:
|
||||||
# Query images associated with each sample
|
# Query images associated with the sample.
|
||||||
images = db.query(ImageModel).filter(ImageModel.sample_id == sample.id).all()
|
images = db.query(ImageModel).filter(ImageModel.sample_id == sample.id).all()
|
||||||
|
|
||||||
|
# Query experiment parameters (which include beamline parameters) for the
|
||||||
|
# sample.
|
||||||
|
experiment_parameters = (
|
||||||
|
db.query(ExperimentParametersModel)
|
||||||
|
.filter(ExperimentParametersModel.sample_id == sample.id)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
print("Experiment Parameters for sample", sample.id, experiment_parameters)
|
||||||
|
|
||||||
results.append(
|
results.append(
|
||||||
{
|
{
|
||||||
"sample_id": sample.id,
|
"sample_id": sample.id,
|
||||||
@ -221,7 +234,52 @@ async def get_sample_results(active_pgroup: str, db: Session = Depends(get_db)):
|
|||||||
{"id": img.id, "filepath": img.filepath, "comment": img.comment}
|
{"id": img.id, "filepath": img.filepath, "comment": img.comment}
|
||||||
for img in images
|
for img in images
|
||||||
],
|
],
|
||||||
|
"experiment_runs": [
|
||||||
|
{
|
||||||
|
"id": ex.id,
|
||||||
|
"run_number": ex.run_number,
|
||||||
|
"beamline_parameters": ex.beamline_parameters,
|
||||||
|
"sample_id": ex.sample_id,
|
||||||
|
}
|
||||||
|
for ex in experiment_parameters
|
||||||
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"/samples/{sample_id}/experiment_parameters",
|
||||||
|
response_model=ExperimentParametersRead,
|
||||||
|
)
|
||||||
|
def create_experiment_parameters_for_sample(
|
||||||
|
sample_id: int,
|
||||||
|
exp_params: ExperimentParametersCreate,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
# Calculate the new run_number for the given sample.
|
||||||
|
# This assumes that the run_number is computed as one plus the maximum
|
||||||
|
# current value.
|
||||||
|
last_exp = (
|
||||||
|
db.query(ExperimentParametersModel)
|
||||||
|
.filter(ExperimentParametersModel.sample_id == sample_id)
|
||||||
|
.order_by(ExperimentParametersModel.run_number.desc())
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
new_run_number = last_exp.run_number + 1 if last_exp else 1
|
||||||
|
|
||||||
|
# Create a new ExperimentParameters record. The beamline_parameters are
|
||||||
|
# stored as JSON.
|
||||||
|
new_exp = ExperimentParametersModel(
|
||||||
|
run_number=new_run_number,
|
||||||
|
beamline_parameters=exp_params.beamline_parameters.dict()
|
||||||
|
if exp_params.beamline_parameters
|
||||||
|
else None,
|
||||||
|
sample_id=sample_id,
|
||||||
|
)
|
||||||
|
db.add(new_exp)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(new_exp)
|
||||||
|
|
||||||
|
return new_exp
|
||||||
|
@ -812,9 +812,97 @@ class ImageInfo(BaseModel):
|
|||||||
comment: Optional[str] = None
|
comment: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
class RotationParameters(BaseModel):
|
||||||
|
omegaStart_deg: float
|
||||||
|
omegaStep: float
|
||||||
|
chi: float
|
||||||
|
phi: float
|
||||||
|
numberOfImages: int
|
||||||
|
exposureTime_s: float
|
||||||
|
|
||||||
|
|
||||||
|
class gridScanParamers(BaseModel):
|
||||||
|
xStart: float
|
||||||
|
xStep: float
|
||||||
|
yStart: float
|
||||||
|
yStep: float
|
||||||
|
zStart: float
|
||||||
|
zStep: float
|
||||||
|
numberOfImages: int
|
||||||
|
exposureTime_s: float
|
||||||
|
|
||||||
|
|
||||||
|
class jetParameters(BaseModel):
|
||||||
|
hplc_pump_ml_min: float
|
||||||
|
pressure_bar: float
|
||||||
|
jetDiameter_um: int
|
||||||
|
jetSpeed_mm_s: float
|
||||||
|
exposureTime_s: float
|
||||||
|
|
||||||
|
|
||||||
|
class detector(BaseModel):
|
||||||
|
manufacturer: str
|
||||||
|
model: str
|
||||||
|
type: str
|
||||||
|
serialNumber: str
|
||||||
|
detectorDistance_mm: float
|
||||||
|
beamCenterX_px: float
|
||||||
|
beamCenterY_px: float
|
||||||
|
pixelSizeX_um: float
|
||||||
|
pixelSizeY_um: float
|
||||||
|
|
||||||
|
|
||||||
|
class BeamlineParameters(BaseModel):
|
||||||
|
synchrotron: str
|
||||||
|
beamline: str
|
||||||
|
detector: detector
|
||||||
|
wavelength: float
|
||||||
|
# energy: float
|
||||||
|
ringCurrent_A: float
|
||||||
|
ringMode: str
|
||||||
|
undulator: Optional[str] = None
|
||||||
|
undulatorgap_mm: Optional[float] = None
|
||||||
|
monochromator: str
|
||||||
|
# bandwidth_percent: float
|
||||||
|
transmission: float
|
||||||
|
focusingOptic: str
|
||||||
|
beamlineFluxAtSample_ph_s: Optional[float] = None
|
||||||
|
beamSizeWidth: Optional[float] = None
|
||||||
|
beamSizeHeight: Optional[float] = None
|
||||||
|
# dose_MGy: float
|
||||||
|
rotation: Optional[RotationParameters] = None
|
||||||
|
gridScan: Optional[gridScanParamers] = None
|
||||||
|
jet: Optional[jetParameters] = None
|
||||||
|
cryojetTemperature_K: Optional[float] = None
|
||||||
|
humidifierTemperature_K: Optional[float] = None
|
||||||
|
humidifierHumidity: Optional[float] = None
|
||||||
|
# experimentalHutchTemerature_K: Optional[float] = None
|
||||||
|
# experimentalHutchHumidity_percent: Optional[float] = None
|
||||||
|
# beamstopDistance_mm: Optional[float] = None
|
||||||
|
# beamstopDiameter_mm: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ExperimentParametersBase(BaseModel):
|
||||||
|
run_number: int
|
||||||
|
beamline_parameters: Optional[BeamlineParameters] = None
|
||||||
|
sample_id: int
|
||||||
|
|
||||||
|
|
||||||
|
class ExperimentParametersCreate(ExperimentParametersBase):
|
||||||
|
run_number: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class ExperimentParametersRead(ExperimentParametersBase):
|
||||||
|
id: int
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
class SampleResult(BaseModel):
|
class SampleResult(BaseModel):
|
||||||
sample_id: int
|
sample_id: int
|
||||||
sample_name: str
|
sample_name: str
|
||||||
puck_name: Optional[str]
|
puck_name: Optional[str]
|
||||||
dewar_name: Optional[str]
|
dewar_name: Optional[str]
|
||||||
images: List[ImageInfo]
|
images: List[ImageInfo]
|
||||||
|
experiment_runs: Optional[List[ExperimentParametersRead]] = []
|
||||||
|
@ -139,6 +139,15 @@ def on_startup():
|
|||||||
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 app.models import ExperimentParameters # adjust the import as needed
|
||||||
|
# inspector = reflection.Inspector.from_engine(engine)
|
||||||
|
# tables_exist = inspector.get_table_names()
|
||||||
|
#
|
||||||
|
# if ExperimentParameters.__tablename__ not in tables_exist:
|
||||||
|
# print("Creating missing table: ExperimentParameters")
|
||||||
|
# ExperimentParameters.__table__.create(bind=engine)
|
||||||
|
#
|
||||||
if environment == "dev":
|
if environment == "dev":
|
||||||
from app.database import load_sample_data
|
from app.database import load_sample_data
|
||||||
|
|
||||||
|
646
frontend/package-lock.json
generated
646
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,8 @@
|
|||||||
"@mui/icons-material": "^6.1.5",
|
"@mui/icons-material": "^6.1.5",
|
||||||
"@mui/material": "^6.1.6",
|
"@mui/material": "^6.1.6",
|
||||||
"@mui/system": "^6.1.6",
|
"@mui/system": "^6.1.6",
|
||||||
"@mui/x-data-grid": "^7.23.3",
|
"@mui/x-data-grid-premium": "^7.27.2",
|
||||||
|
"@mui/x-tree-view": "^7.26.0",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"chokidar": "^4.0.1",
|
"chokidar": "^4.0.1",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
@ -59,7 +60,7 @@
|
|||||||
"openapi-typescript": "^7.4.2",
|
"openapi-typescript": "^7.4.2",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
"typescript-eslint": "^8.7.0",
|
"typescript-eslint": "^8.7.0",
|
||||||
"vite": "^5.4.8",
|
"vite": "^6.2.0",
|
||||||
"vite-plugin-svgr": "^4.2.0"
|
"vite-plugin-svgr": "^4.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,131 @@
|
|||||||
// TypeScript (ResultGrid.tsx snippet)
|
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { DataGrid, GridColDef } from '@mui/x-data-grid';
|
import { DataGridPremium, GridColDef } from '@mui/x-data-grid-premium';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import InfoIcon from '@mui/icons-material/Info';
|
||||||
import { OpenAPI, SamplesService } from '../../openapi';
|
import { OpenAPI, SamplesService } from '../../openapi';
|
||||||
import './SampleImage.css';
|
import './SampleImage.css';
|
||||||
|
|
||||||
|
// Extend your image info interface if needed.
|
||||||
interface ImageInfo {
|
interface ImageInfo {
|
||||||
id: number;
|
id: number;
|
||||||
filepath: string;
|
filepath: string;
|
||||||
comment?: string;
|
comment?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This represents an experiment run as returned by your API.
|
||||||
|
interface ExperimentParameters {
|
||||||
|
id: number;
|
||||||
|
run_number: number;
|
||||||
|
sample_id: number;
|
||||||
|
beamline_parameters: {
|
||||||
|
beamSizeHeight: number;
|
||||||
|
beamSizeWidth: number;
|
||||||
|
beamline: string;
|
||||||
|
beamlineFluxAtSample_ph_s: number;
|
||||||
|
cryojetTemperature_K?: number | null;
|
||||||
|
detector: {
|
||||||
|
beamCenterX_px: number;
|
||||||
|
beamCenterY_px: number;
|
||||||
|
detectorDistance_mm: number;
|
||||||
|
manufacturer: string;
|
||||||
|
model: string;
|
||||||
|
pixelSizeX_um: number;
|
||||||
|
pixelSizeY_um: number;
|
||||||
|
serialNumber: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
rotation?: {
|
||||||
|
chi: number;
|
||||||
|
exposureTime_s: number;
|
||||||
|
numberOfImages: number | null;
|
||||||
|
omegaStart_deg: number;
|
||||||
|
omegaStep: number;
|
||||||
|
phi: number;
|
||||||
|
} | null;
|
||||||
|
gridScan?: {
|
||||||
|
xStart: number;
|
||||||
|
xStep: number;
|
||||||
|
yStart: number;
|
||||||
|
yStep: number;
|
||||||
|
zStart: number;
|
||||||
|
zStep: number;
|
||||||
|
numberOfImages: number;
|
||||||
|
exposureTime_s: number;
|
||||||
|
} | null;
|
||||||
|
focusingOptic?: string;
|
||||||
|
humidifierHumidity?: number | null;
|
||||||
|
humidifierTemperature_K?: number | null;
|
||||||
|
jet?: any;
|
||||||
|
monochromator?: string;
|
||||||
|
ringCurrent_A?: number;
|
||||||
|
ringMode?: string;
|
||||||
|
synchrotron?: string;
|
||||||
|
transmission?: number;
|
||||||
|
undulator?: any;
|
||||||
|
undulatorgap_mm?: any;
|
||||||
|
wavelength?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents a sample returned by the API.
|
||||||
interface SampleResult {
|
interface SampleResult {
|
||||||
sample_id: number;
|
sample_id: number;
|
||||||
sample_name: string;
|
sample_name: string;
|
||||||
puck_name?: string;
|
puck_name?: string;
|
||||||
dewar_name?: string;
|
dewar_name?: string;
|
||||||
images: ImageInfo[];
|
images: ImageInfo[];
|
||||||
|
experiment_runs?: ExperimentParameters[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// A flat tree row type for the grid.
|
||||||
|
interface TreeRow {
|
||||||
|
id: string;
|
||||||
|
hierarchy: (string | number)[];
|
||||||
|
type: 'sample' | 'run';
|
||||||
|
sample_id: number;
|
||||||
|
sample_name?: string;
|
||||||
|
puck_name?: string;
|
||||||
|
dewar_name?: string;
|
||||||
|
images?: ImageInfo[];
|
||||||
|
run_number?: number;
|
||||||
|
beamline_parameters?: ExperimentParameters['beamline_parameters'];
|
||||||
|
experimentType?: string;
|
||||||
|
numberOfImages?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ResultGridProps {
|
interface ResultGridProps {
|
||||||
activePgroup: string;
|
activePgroup: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to safely get the number of images.
|
||||||
|
const getNumberOfImages = (run: ExperimentParameters): number => {
|
||||||
|
const params = run.beamline_parameters;
|
||||||
|
if (params.rotation && params.rotation.numberOfImages != null) {
|
||||||
|
return params.rotation.numberOfImages;
|
||||||
|
} else if (params.gridScan && params.gridScan.numberOfImages != null) {
|
||||||
|
return params.gridScan.numberOfImages;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to determine the experiment type.
|
||||||
|
const getExperimentType = (run: ExperimentParameters): string => {
|
||||||
|
const params = run.beamline_parameters;
|
||||||
|
if (params.rotation && params.rotation.numberOfImages != null && params.rotation.omegaStep != null) {
|
||||||
|
const numImages = params.rotation.numberOfImages;
|
||||||
|
const omegaStep = params.rotation.omegaStep;
|
||||||
|
if ([1, 2, 4].includes(numImages) && omegaStep === 90) {
|
||||||
|
return "Characterization";
|
||||||
|
}
|
||||||
|
return "Rotation";
|
||||||
|
} else if (params.gridScan) {
|
||||||
|
return "Grid Scan";
|
||||||
|
}
|
||||||
|
return "Rotation";
|
||||||
|
};
|
||||||
|
|
||||||
const ResultGrid: React.FC<ResultGridProps> = ({ activePgroup }) => {
|
const ResultGrid: React.FC<ResultGridProps> = ({ activePgroup }) => {
|
||||||
const [rows, setRows] = useState<SampleResult[]>([]);
|
const [rows, setRows] = useState<TreeRow[]>([]);
|
||||||
const [basePath, setBasePath] = useState('');
|
const [basePath, setBasePath] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -44,99 +145,124 @@ const ResultGrid: React.FC<ResultGridProps> = ({ activePgroup }) => {
|
|||||||
console.log('Environment Mode:', mode);
|
console.log('Environment Mode:', mode);
|
||||||
console.log('Resolved OpenAPI.BASE:', OpenAPI.BASE);
|
console.log('Resolved OpenAPI.BASE:', OpenAPI.BASE);
|
||||||
|
|
||||||
// Update the base path for images
|
|
||||||
setBasePath(`${OpenAPI.BASE}/`);
|
setBasePath(`${OpenAPI.BASE}/`);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = () => {
|
console.log('Fetching sample results for active_pgroup:', activePgroup);
|
||||||
console.log("Fetching sample results for active_pgroup:", activePgroup);
|
|
||||||
SamplesService.getSampleResultsSamplesResultsGet(activePgroup)
|
SamplesService.getSampleResultsSamplesResultsGet(activePgroup)
|
||||||
.then((response: SampleResult[]) => {
|
.then((response: SampleResult[]) => {
|
||||||
console.log("Response received:", response);
|
console.log('Response received:', response);
|
||||||
setRows(response);
|
const treeRows: TreeRow[] = [];
|
||||||
})
|
|
||||||
.catch((err: Error) => {
|
response.forEach((sample) => {
|
||||||
console.error("Error fetching sample results:", err);
|
// Add the sample row.
|
||||||
});
|
const sampleRow: TreeRow = {
|
||||||
|
id: `sample-${sample.sample_id}`,
|
||||||
|
hierarchy: [sample.sample_id],
|
||||||
|
type: 'sample',
|
||||||
|
sample_id: sample.sample_id,
|
||||||
|
sample_name: sample.sample_name,
|
||||||
|
puck_name: sample.puck_name,
|
||||||
|
dewar_name: sample.dewar_name,
|
||||||
|
images: sample.images,
|
||||||
};
|
};
|
||||||
|
treeRows.push(sampleRow);
|
||||||
|
|
||||||
// Fetch data initially.
|
// Add experiment run rows.
|
||||||
fetchData();
|
if (sample.experiment_runs) {
|
||||||
|
sample.experiment_runs.forEach((run) => {
|
||||||
|
const experimentType = getExperimentType(run);
|
||||||
|
const numImages = getNumberOfImages(run);
|
||||||
|
const runRow: TreeRow = {
|
||||||
|
id: `run-${sample.sample_id}-${run.run_number}`,
|
||||||
|
hierarchy: [sample.sample_id, run.run_number],
|
||||||
|
type: 'run',
|
||||||
|
sample_id: sample.sample_id,
|
||||||
|
run_number: run.run_number,
|
||||||
|
beamline_parameters: run.beamline_parameters,
|
||||||
|
experimentType,
|
||||||
|
numberOfImages: numImages,
|
||||||
|
images: sample.images,
|
||||||
|
};
|
||||||
|
treeRows.push(runRow);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Set up an interval to refresh data every 15 seconds (adjust as needed)
|
setRows(treeRows);
|
||||||
const intervalId = setInterval(fetchData, 15000);
|
})
|
||||||
|
.catch((error) => {
|
||||||
// Clean up when component unmounts or activePgroup changes.
|
console.error('Error fetching sample results:', error);
|
||||||
return () => clearInterval(intervalId);
|
});
|
||||||
}, [activePgroup]);
|
}, [activePgroup]);
|
||||||
|
|
||||||
|
// Define the grid columns, including the new processing results column.
|
||||||
const columns: GridColDef[] = [
|
const columns: GridColDef[] = [
|
||||||
{ field: 'sample_id', headerName: 'ID', width: 70 },
|
|
||||||
{ field: 'sample_name', headerName: 'Sample Name', width: 150 },
|
|
||||||
{ field: 'puck_name', headerName: 'Puck Name', width: 150 },
|
|
||||||
{ field: 'dewar_name', headerName: 'Dewar Name', width: 150 },
|
|
||||||
{
|
{
|
||||||
field: 'images',
|
field: 'sample_name',
|
||||||
headerName: 'Images',
|
headerName: 'Sample Name',
|
||||||
width: 300,
|
width: 200,
|
||||||
|
renderCell: (params) => (params.row.type === 'sample' ? params.value : null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'puck_name',
|
||||||
|
headerName: 'Puck Name',
|
||||||
|
width: 150,
|
||||||
|
renderCell: (params) => (params.row.type === 'sample' ? params.value : null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'dewar_name',
|
||||||
|
headerName: 'Dewar Name',
|
||||||
|
width: 150,
|
||||||
|
renderCell: (params) => (params.row.type === 'sample' ? params.value : null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'experimentType',
|
||||||
|
headerName: 'Experiment Type',
|
||||||
|
width: 150,
|
||||||
|
renderCell: (params) => (params.row.type === 'run' ? params.value : null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'numberOfImages',
|
||||||
|
headerName: 'Number of Images',
|
||||||
|
width: 150,
|
||||||
|
renderCell: (params) => (params.row.type === 'run' ? params.value : null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'processingResults',
|
||||||
|
headerName: 'Processing Results',
|
||||||
|
width: 180,
|
||||||
renderCell: (params) => {
|
renderCell: (params) => {
|
||||||
const imageList: ImageInfo[] = params.value;
|
if (params.row.type === 'run') {
|
||||||
if (!imageList || imageList.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter the images to include only the two bb_raster images
|
|
||||||
const filteredImages = imageList.filter(
|
|
||||||
(img) =>
|
|
||||||
img.filepath.includes("bb_raster_0") ||
|
|
||||||
img.filepath.includes("bb_raster_90")
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filteredImages.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
<IconButton
|
||||||
{filteredImages.map((img) => {
|
aria-label="processing results placeholder"
|
||||||
const url = basePath + img.filepath;
|
onClick={() => {
|
||||||
return (
|
// Placeholder for processing results details.
|
||||||
<img
|
console.log('Clicked processing details for run', params.row.run_number);
|
||||||
key={img.id}
|
|
||||||
src={url}
|
|
||||||
alt={img.comment || 'sample'}
|
|
||||||
className="zoom-image"
|
|
||||||
style={{
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
marginRight: 5,
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
);
|
<InfoIcon />
|
||||||
})}
|
</IconButton>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataGrid
|
<div style={{ height: 600, width: '100%' }}>
|
||||||
|
<DataGridPremium
|
||||||
rows={rows}
|
rows={rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
pageSize={10}
|
treeData
|
||||||
getRowId={(row) => row.sample_id}
|
getTreeDataPath={(row: TreeRow) => row.hierarchy}
|
||||||
sx={{
|
defaultGroupingExpansionDepth={-1}
|
||||||
'& .MuiDataGrid-cell': {
|
getRowId={(row) => row.id}
|
||||||
overflow: 'visible',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
84
frontend/src/components/RunDetails.tsx
Normal file
84
frontend/src/components/RunDetails.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import {SimpleTreeView, TreeItem} from "@mui/x-tree-view";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
|
||||||
|
interface ExperimentParameters {
|
||||||
|
id: number;
|
||||||
|
run_number: number;
|
||||||
|
beamline_parameters: {
|
||||||
|
synchrotron: string;
|
||||||
|
beamline: string;
|
||||||
|
detector: {
|
||||||
|
manufacturer: string;
|
||||||
|
model: string;
|
||||||
|
type: string;
|
||||||
|
serial_number: string;
|
||||||
|
detector_distance_mm: number;
|
||||||
|
beam_center_x_px: number;
|
||||||
|
beam_center_y_px: number;
|
||||||
|
pixel_size_x_um: number;
|
||||||
|
pixel_size_y_um: number;
|
||||||
|
number_of_images: number;
|
||||||
|
exposure_time_s: number;
|
||||||
|
};
|
||||||
|
// Add additional fields if needed.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
<SimpleTreeView
|
||||||
|
defaultCollapseIcon="▾"
|
||||||
|
defaultExpandIcon="▸"
|
||||||
|
sx={{ fontSize: '0.875rem' }}
|
||||||
|
>
|
||||||
|
<TreeItem nodeId="detector-group" label={<strong>Detector Details</strong>}>
|
||||||
|
<TreeItem nodeId="detector-group" label={<strong>Detector Details</strong>}>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="detector-manufacturer"
|
||||||
|
label={`Manufacturer: ${detector?.manufacturer || 'N/A'}`}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="detector-model"
|
||||||
|
label={`Model: ${detector?.model || 'N/A'}`}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="detector-type"
|
||||||
|
label={`Type: ${detector?.type || 'N/A'}`}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="detector-serial"
|
||||||
|
label={`Serial Number: ${detector?.serial_number || 'N/A'}`}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="detector-distance"
|
||||||
|
label={`Distance (mm): ${detector?.detector_distance_mm ?? 'N/A'}`}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="beam-center"
|
||||||
|
label={
|
||||||
|
detector
|
||||||
|
? `Beam Center: x:${detector.beam_center_x_px}, y:${detector.beam_center_y_px}`
|
||||||
|
: 'Beam Center: N/A'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="pixel-size"
|
||||||
|
label={
|
||||||
|
detector
|
||||||
|
? `Pixel Size (µm): x:${detector.pixel_size_x_um}, y:${detector.pixel_size_y_um}`
|
||||||
|
: 'Pixel Size: N/A'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="img-count"
|
||||||
|
label={`Number of Images: ${detector?.number_of_images ?? 'N/A'}`}
|
||||||
|
/>
|
||||||
|
<TreeItem
|
||||||
|
nodeId="exposure-time"
|
||||||
|
label={`Exposure Time (s): ${detector?.exposure_time_s ?? 'N/A'}`}
|
||||||
|
/>
|
||||||
|
</TreeItem>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</TreeItem>
|
||||||
|
</SimpleTreeView>
|
@ -52,7 +52,7 @@ const SampleTracker: React.FC<SampleTrackerProps> = ({ activePgroup }) => {
|
|||||||
fetchPucks();
|
fetchPucks();
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
fetchPucks();
|
fetchPucks();
|
||||||
}, 1000);
|
}, 100000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [activePgroup]);
|
}, [activePgroup]);
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "aareDB"
|
name = "aareDB"
|
||||||
version = "0.1.0a23"
|
version = "0.1.0a24"
|
||||||
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"}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user