Add validations and logging for puck beamtime assignment.
Introduced checks to prevent reassigning beamtime if puck samples have recorded events. Updated logging in beamline-related methods to provide more insight. Simplified data structure updates for dewars, pucks, and samples, ensuring consistency with beamtime assignments.
This commit is contained in:
parent
6a0953c913
commit
707c98c5ce
@ -709,9 +709,18 @@ dewar_to_beamtime = {
|
||||
for dewar in dewars # Or use actual beamtime ids
|
||||
}
|
||||
|
||||
# Update dewars and their pucks with consistent beamtime
|
||||
for dewar in dewars:
|
||||
dewar.beamtime_id = dewar_to_beamtime[dewar.id]
|
||||
assigned_beamtime_obj = next(
|
||||
b for b in beamtimes if b.id == dewar_to_beamtime[dewar.id]
|
||||
)
|
||||
dewar.beamtimes = [assigned_beamtime_obj]
|
||||
|
||||
for puck in pucks:
|
||||
assigned_beamtime_obj = next(
|
||||
b for b in beamtimes if b.id == dewar_to_beamtime[puck.dewar_id]
|
||||
)
|
||||
puck.beamtimes = [assigned_beamtime_obj]
|
||||
|
||||
|
||||
for puck in pucks:
|
||||
dewar_id = puck.dewar_id # Assuming puck has dewar_id
|
||||
|
@ -51,6 +51,8 @@ from app.crud import (
|
||||
)
|
||||
from app.routers.auth import get_current_user
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
dewar_router = APIRouter()
|
||||
|
||||
@ -599,6 +601,19 @@ async def assign_beamtime_to_dewar(
|
||||
if not dewar:
|
||||
raise HTTPException(status_code=404, detail="Dewar not found")
|
||||
|
||||
# Check if any sample (in any puck on this dewar) has sample events
|
||||
for puck in dewar.pucks:
|
||||
for sample in puck.samples:
|
||||
sample_event_exists = (
|
||||
db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).first()
|
||||
)
|
||||
if sample_event_exists:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Cannot change beamtime:"
|
||||
"at least one sample has events recorded.",
|
||||
)
|
||||
|
||||
# Find the Beamtime instance, if not unassigning
|
||||
beamtime = (
|
||||
db.query(BeamtimeModel).filter(BeamtimeModel.id == beamtime_id).first()
|
||||
@ -609,9 +624,7 @@ async def assign_beamtime_to_dewar(
|
||||
if beamtime_id == 0:
|
||||
dewar.beamtimes = []
|
||||
else:
|
||||
dewar.beamtimes = [
|
||||
beamtime
|
||||
] # assign one; append if you want to support multiple
|
||||
dewar.beamtimes = [beamtime]
|
||||
|
||||
db.commit()
|
||||
db.refresh(dewar)
|
||||
@ -621,15 +634,11 @@ async def assign_beamtime_to_dewar(
|
||||
else:
|
||||
puck.beamtimes = [beamtime]
|
||||
for sample in puck.samples:
|
||||
has_sample_event = (
|
||||
db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).count()
|
||||
> 0
|
||||
)
|
||||
if not has_sample_event:
|
||||
if beamtime_id == 0:
|
||||
sample.beamtimes = []
|
||||
else:
|
||||
sample.beamtimes = [beamtime]
|
||||
# Can assume all have no events because of previous check
|
||||
if beamtime_id == 0:
|
||||
sample.beamtimes = []
|
||||
else:
|
||||
sample.beamtimes = [beamtime]
|
||||
|
||||
db.commit()
|
||||
return {"status": "success", "dewar_id": dewar.id, "beamtime_id": beamtime_id}
|
||||
@ -746,6 +755,7 @@ async def get_single_shipment(id: int, db: Session = Depends(get_db)):
|
||||
operation_id="get_dewars_by_beamtime",
|
||||
)
|
||||
async def get_dewars_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)):
|
||||
logger.info(f"get_dewars_by_beamtime called with beamtime_id={beamtime_id}")
|
||||
beamtime = (
|
||||
db.query(BeamtimeModel)
|
||||
.options(joinedload(BeamtimeModel.dewars))
|
||||
@ -753,5 +763,9 @@ async def get_dewars_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)
|
||||
.first()
|
||||
)
|
||||
if not beamtime:
|
||||
logger.warning(f"Beamtime {beamtime_id} not found")
|
||||
raise HTTPException(status_code=404, detail="Beamtime not found")
|
||||
logger.info(
|
||||
f"Returning {len(beamtime.dewars)} dewars: {[d.id for d in beamtime.dewars]}"
|
||||
)
|
||||
return beamtime.dewars
|
||||
|
@ -665,13 +665,25 @@ async def get_pucks_by_slot(slot_identifier: str, db: Session = Depends(get_db))
|
||||
@router.patch("/puck/{puck_id}/assign-beamtime", operation_id="assignPuckToBeamtime")
|
||||
async def assign_beamtime_to_puck(
|
||||
puck_id: int,
|
||||
beamtime_id: int, # expects ?beamtime_id=123 in the query
|
||||
beamtime_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
puck = db.query(PuckModel).filter(PuckModel.id == puck_id).first()
|
||||
if not puck:
|
||||
raise HTTPException(status_code=404, detail="Puck not found")
|
||||
|
||||
# Check if any sample in this puck has sample events
|
||||
for sample in puck.samples:
|
||||
sample_event_exists = (
|
||||
db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).first()
|
||||
)
|
||||
if sample_event_exists:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Cannot change beamtime:"
|
||||
"at least one sample has events recorded.",
|
||||
)
|
||||
|
||||
beamtime = (
|
||||
db.query(BeamtimeModel).filter(BeamtimeModel.id == beamtime_id).first()
|
||||
if beamtime_id
|
||||
@ -681,22 +693,15 @@ async def assign_beamtime_to_puck(
|
||||
if beamtime_id == 0:
|
||||
puck.beamtimes = []
|
||||
else:
|
||||
puck.beamtimes = [
|
||||
beamtime
|
||||
] # or use .append(beamtime) if you want to support multiple
|
||||
puck.beamtimes = [beamtime]
|
||||
|
||||
db.commit()
|
||||
db.refresh(puck)
|
||||
# Update samples as well
|
||||
for sample in puck.samples:
|
||||
has_sample_event = (
|
||||
db.query(SampleEvent).filter(SampleEvent.sample_id == sample.id).count() > 0
|
||||
)
|
||||
if not has_sample_event:
|
||||
if beamtime_id == 0:
|
||||
sample.beamtimes = []
|
||||
else:
|
||||
sample.beamtimes = [beamtime]
|
||||
if beamtime_id == 0:
|
||||
sample.beamtimes = []
|
||||
else:
|
||||
sample.beamtimes = [beamtime]
|
||||
db.commit()
|
||||
return {"status": "success", "puck_id": puck.id, "beamtime_id": beamtime_id}
|
||||
|
||||
@ -707,6 +712,7 @@ async def assign_beamtime_to_puck(
|
||||
operation_id="get_pucks_by_beamtime",
|
||||
)
|
||||
async def get_pucks_by_beamtime(beamtime_id: int, db: Session = Depends(get_db)):
|
||||
logger.info(f"get_pucks_by_beamtime called with beamtime_id={beamtime_id}")
|
||||
beamtime = (
|
||||
db.query(BeamtimeModel)
|
||||
.options(joinedload(BeamtimeModel.pucks)) # eager load pucks
|
||||
@ -714,5 +720,9 @@ async def get_pucks_by_beamtime(beamtime_id: int, db: Session = Depends(get_db))
|
||||
.first()
|
||||
)
|
||||
if not beamtime:
|
||||
logger.warning(f"Beamtime {beamtime_id} not found")
|
||||
raise HTTPException(status_code=404, detail="Beamtime not found")
|
||||
logger.info(
|
||||
f"Returning {len(beamtime.pucks)} pucks: {[p.id for p in beamtime.pucks]}"
|
||||
)
|
||||
return beamtime.pucks
|
||||
|
@ -168,8 +168,8 @@ async def lifespan(app: FastAPI):
|
||||
load_slots_data(db)
|
||||
else: # dev or test environments
|
||||
print(f"{environment.capitalize()} environment: Regenerating database.")
|
||||
# Base.metadata.drop_all(bind=engine)
|
||||
# Base.metadata.create_all(bind=engine)
|
||||
Base.metadata.drop_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)
|
||||
|
@ -84,8 +84,31 @@ const App: React.FC = () => {
|
||||
<Routes>
|
||||
<Route path="/login" element={<LoginView />} />
|
||||
<Route path="/" element={<ProtectedRoute element={<HomePage />} />} />
|
||||
<Route path="/shipments" element={<ProtectedRoute element={<ShipmentView pgroups={pgroups} activePgroup={activePgroup} />} />} />
|
||||
<Route path="/planning" element={<ProtectedRoute element={<PlanningView />} />} />
|
||||
<Route path="/shipments"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
element={
|
||||
<ShipmentView
|
||||
pgroups={pgroups}
|
||||
activePgroup={activePgroup}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/planning"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
element={
|
||||
<PlanningView
|
||||
pgroups={pgroups}
|
||||
activePgroup={activePgroup}
|
||||
onPgroupChange={handlePgroupChange}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/results/:beamtimeId"
|
||||
element={
|
||||
|
@ -5,6 +5,7 @@ import timeGridPlugin from '@fullcalendar/timegrid';
|
||||
import interactionPlugin from '@fullcalendar/interaction';
|
||||
import '../styles/Calendar.css';
|
||||
import { BeamtimesService, DewarsService, PucksService } from '../../openapi';
|
||||
import Chip from '@mui/material/Chip'
|
||||
|
||||
const beamlineColors: { [key: string]: string } = {
|
||||
X06SA: '#FF5733',
|
||||
@ -18,8 +19,15 @@ interface CustomEvent extends EventInput {
|
||||
beamtime_shift: string;
|
||||
beamtime_id?: number;
|
||||
isSubmitted?: boolean;
|
||||
activePgroup?: string;
|
||||
pgroups?: string;
|
||||
}
|
||||
|
||||
interface CalendarProps {
|
||||
activePgroup: string;
|
||||
}
|
||||
|
||||
|
||||
const experimentModes = ['SDU-Scheduled', 'SDU-queued', 'Remote', 'In-person'];
|
||||
|
||||
const darkenColor = (color: string, percent: number): string => {
|
||||
@ -34,7 +42,7 @@ const darkenColor = (color: string, percent: number): string => {
|
||||
return `#${newColor}`;
|
||||
};
|
||||
|
||||
const Calendar: React.FC = () => {
|
||||
const Calendar = ({ activePgroup }: CalendarProps) => {
|
||||
const [events, setEvents] = useState<CustomEvent[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [fetchError, setFetchError] = useState<string | null>(null);
|
||||
@ -59,6 +67,7 @@ const Calendar: React.FC = () => {
|
||||
setFetchError(null);
|
||||
try {
|
||||
const beamtimes = await BeamtimesService.getMyBeamtimesProtectedBeamtimesMyBeamtimesGet();
|
||||
console.log('Loaded beamtimes:', beamtimes);
|
||||
const grouped: { [key: string]: any[] } = {};
|
||||
beamtimes.forEach((beamtime: any) => {
|
||||
const key = `${beamtime.start_date}|${beamtime.beamline}|${beamtime.pgroups}`;
|
||||
@ -70,8 +79,9 @@ const Calendar: React.FC = () => {
|
||||
const shifts = group.map((bt: any) => bt.shift).join(" + ");
|
||||
const ids = group.map((bt: any) => bt.id);
|
||||
const first = group[0];
|
||||
console.log(`[DEBUG] pgroups: ${first.pgroups}`); // Ensure the value of pgroups here is correct
|
||||
return {
|
||||
id: `${first.beamline}-${first.start_date}-${first.pgroups}`, // ensure uniqueness
|
||||
id: `${first.beamline}-${first.start_date}-${first.pgroups}`,
|
||||
title: `${first.beamline}: ${shifts}`,
|
||||
start: first.start_date,
|
||||
end: first.end_date,
|
||||
@ -82,6 +92,9 @@ const Calendar: React.FC = () => {
|
||||
borderColor: '#000',
|
||||
textColor: '#fff',
|
||||
beamtimes: group,
|
||||
extendedProps: {
|
||||
pgroups: first.pgroups, // Check that this is a valid, comma-separated string
|
||||
},
|
||||
};
|
||||
});
|
||||
setEvents(formattedEvents);
|
||||
@ -89,6 +102,7 @@ const Calendar: React.FC = () => {
|
||||
|
||||
// Fetch associations for all
|
||||
const assoc: { [id: string]: { dewars: string[]; pucks: string[] } } = {};
|
||||
console.log('Fetched associations after loading events:', assoc);
|
||||
|
||||
await Promise.all(
|
||||
Object.values(grouped).map(async (group) => {
|
||||
@ -103,6 +117,8 @@ const Calendar: React.FC = () => {
|
||||
DewarsService.getDewarsByBeamtime(beamtimeId),
|
||||
PucksService.getPucksByBeamtime(beamtimeId),
|
||||
]);
|
||||
console.log(`Dewars for beamtime ${beamtimeId}:`, dewars);
|
||||
console.log(`Pucks for beamtime ${beamtimeId}:`, pucks);
|
||||
dewars.forEach((d: any) => dewarsSet.add(d.id));
|
||||
pucks.forEach((p: any) => pucksSet.add(p.id));
|
||||
})
|
||||
@ -115,8 +131,10 @@ const Calendar: React.FC = () => {
|
||||
};
|
||||
})
|
||||
);
|
||||
console.log("Final eventAssociations:", assoc);
|
||||
setEventAssociations(assoc);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
setFetchError('Failed to load beamtime data. Please try again later.');
|
||||
setEvents([]);
|
||||
@ -250,39 +268,38 @@ const Calendar: React.FC = () => {
|
||||
? eventInfo.event.extendedProps.beamtimes.length
|
||||
: 1;
|
||||
const minHeight = beamtimesInGroup * 26;
|
||||
const beamline = eventInfo.event.extendedProps.beamline || 'Unknown';
|
||||
const isSelected = selectedEventId === eventInfo.event.id;
|
||||
const isSubmitted = eventInfo.event.extendedProps.isSubmitted;
|
||||
const assoc = eventAssociations[eventInfo.event.id] || { dewars: [], pucks: [] };
|
||||
const backgroundColor = isSubmitted
|
||||
? darkenColor(beamlineColors[beamline] || beamlineColors.Unknown, -20)
|
||||
: isSelected
|
||||
? '#FFD700'
|
||||
: (beamlineColors[beamline] || beamlineColors.Unknown);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: backgroundColor,
|
||||
color: 'white',
|
||||
border: isSelected ? '2px solid black' : 'none',
|
||||
borderRadius: '3px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
cursor: 'pointer',
|
||||
overflow: 'hidden',
|
||||
boxSizing: 'border-box',
|
||||
padding: '0 6px',
|
||||
minHeight: `${minHeight}px`,
|
||||
}}
|
||||
>
|
||||
const beamline = eventInfo.event.extendedProps.beamline || 'Unknown';
|
||||
const isSelected = selectedEventId === eventInfo.event.id;
|
||||
const isSubmitted = eventInfo.event.extendedProps.isSubmitted;
|
||||
const assoc = eventAssociations[eventInfo.event.id] || { dewars: [], pucks: [] };
|
||||
const backgroundColor = isSubmitted
|
||||
? darkenColor(beamlineColors[beamline] || beamlineColors.Unknown, -20)
|
||||
: isSelected
|
||||
? '#FFD700'
|
||||
: (beamlineColors[beamline] || beamlineColors.Unknown);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: backgroundColor,
|
||||
color: 'white',
|
||||
border: isSelected ? '2px solid black' : 'none',
|
||||
borderRadius: '3px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
cursor: 'pointer',
|
||||
overflow: 'hidden',
|
||||
boxSizing: 'border-box',
|
||||
padding: '0 6px',
|
||||
minHeight: `${minHeight}px`,
|
||||
}}
|
||||
>
|
||||
<span style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||
{eventInfo.event.title}
|
||||
</span>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 6, marginLeft: 8 }}>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 6, marginLeft: 8 }}>
|
||||
<span title="Dewars" style={{ display: 'flex', alignItems: 'center', fontSize: 13 }}>
|
||||
🧊
|
||||
<span style={{
|
||||
@ -311,13 +328,32 @@ const Calendar: React.FC = () => {
|
||||
textAlign: 'center'
|
||||
}}>{assoc.pucks.length}</span>
|
||||
</span>
|
||||
{eventInfo.event.extendedProps?.pgroups && eventInfo.event.extendedProps.pgroups.split(',')
|
||||
.map((pgroup: string) => (
|
||||
<Chip
|
||||
key={pgroup.trim()}
|
||||
label={pgroup.trim()}
|
||||
size="small"
|
||||
sx={{
|
||||
marginLeft: 0.5,
|
||||
marginRight: 0.5,
|
||||
backgroundColor: pgroup.trim() === activePgroup ? '#19d238' : '#b0b0b0',
|
||||
color: pgroup.trim() === activePgroup ? 'white' : 'black',
|
||||
fontWeight: 'bold',
|
||||
borderRadius: '8px',
|
||||
height: '20px',
|
||||
fontSize: '12px',
|
||||
boxShadow: '0px 1px 3px rgba(0, 0, 0, 0.2)',
|
||||
mr: 1,
|
||||
mb: 1,
|
||||
}}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// Used in Dewar/Puck assign status reporting
|
||||
</div>
|
||||
);
|
||||
};
|
||||
function getAssignedEventForDewar(dewarId: string) {
|
||||
return Object.entries(eventAssociations).find(([eid, assoc]) =>
|
||||
assoc.dewars.includes(dewarId)
|
||||
|
@ -39,13 +39,13 @@ interface ProcessingResults {
|
||||
resolution: number;
|
||||
unit_cell: string;
|
||||
spacegroup: string;
|
||||
rmerge: number;
|
||||
rmeas: number;
|
||||
isig: number;
|
||||
rmerge: CCPoint[];
|
||||
rmeas: CCPoint[];
|
||||
isig: CCPoint[];
|
||||
cc: CCPoint[];
|
||||
cchalf: CCPoint[];
|
||||
completeness: number;
|
||||
multiplicity: number;
|
||||
completeness: CCPoint[];
|
||||
multiplicity: CCPoint[];
|
||||
nobs: number;
|
||||
total_refl: number;
|
||||
unique_refl: number;
|
||||
@ -81,13 +81,13 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange, basePath,
|
||||
resolution: res.result?.resolution ?? 0,
|
||||
unit_cell: res.result?.unit_cell || 'N/A',
|
||||
spacegroup: res.result?.spacegroup || 'N/A',
|
||||
rmerge: res.result?.rmerge ?? 0,
|
||||
rmeas: res.result?.rmeas ?? 0,
|
||||
isig: res.result?.isig ?? 0,
|
||||
rmerge: res.result?.rmerge || [],
|
||||
rmeas: res.result?.rmeas || [],
|
||||
isig: res.result?.isig || [],
|
||||
cc: res.result?.cc || [],
|
||||
cchalf: res.result?.cchalf || [],
|
||||
completeness: res.result?.completeness ?? 0,
|
||||
multiplicity: res.result?.multiplicity ?? 0,
|
||||
completeness: res.result?.completeness || [],
|
||||
multiplicity: res.result?.multiplicity || [],
|
||||
nobs: res.result?.nobs ?? 0,
|
||||
total_refl: res.result?.total_refl ?? 0,
|
||||
unique_refl: res.result?.unique_refl ?? 0,
|
||||
@ -111,15 +111,45 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange, basePath,
|
||||
{ field: 'resolution', headerName: 'Resolution (Å)', flex: 1 },
|
||||
{ field: 'unit_cell', headerName: 'Unit Cell (Å)', flex: 1.5 },
|
||||
{ field: 'spacegroup', headerName: 'Spacegroup', flex: 1 },
|
||||
{ field: 'rmerge', headerName: 'Rmerge', flex: 1 },
|
||||
{ field: 'rmeas', headerName: 'Rmeas', flex: 1 },
|
||||
{ field: 'isig', headerName: 'I/sig(I)', flex: 1 },
|
||||
{
|
||||
field: 'rmerge',
|
||||
headerName: 'Rmerge',
|
||||
flex: 1,
|
||||
valueGetter: (params: GridValueGetterParams<ProcessingResults, string>) =>
|
||||
params.row?.rmerge
|
||||
? Array.isArray(params.row.rmerge)
|
||||
? params.row.rmerge.map((value: CCPoint) => `${value.value.toFixed(2)}@${value.resolution.toFixed(2)}`).join(', ')
|
||||
: params.row.rmerge.toFixed(2)
|
||||
: 'N/A',
|
||||
},
|
||||
{
|
||||
field: 'rmeas',
|
||||
headerName: 'Rmeas',
|
||||
flex: 1,
|
||||
valueGetter: (params: GridValueGetterParams<ProcessingResults, string>) =>
|
||||
params.row?.rmeas
|
||||
? Array.isArray(params.row.rmeas)
|
||||
? params.row.rmeas.map((value: CCPoint) => `${value.value.toFixed(2)}@${value.resolution.toFixed(2)}`).join(', ')
|
||||
: params.row.rmeas.toFixed(2)
|
||||
: 'N/A',
|
||||
},
|
||||
{
|
||||
field: 'isig',
|
||||
headerName: 'I/sig(I)',
|
||||
flex: 1,
|
||||
valueGetter: (params: GridValueGetterParams<ProcessingResults, string>) =>
|
||||
params.row?.isig
|
||||
? Array.isArray(params.row.isig)
|
||||
? params.row.isig.map((value: CCPoint) => `${value.value.toFixed(2)}@${value.resolution.toFixed(2)}`).join(', ')
|
||||
: params.row.isig.toFixed(2)
|
||||
: 'N/A',
|
||||
},
|
||||
{
|
||||
field: 'cc',
|
||||
headerName: 'CC',
|
||||
flex: 1,
|
||||
valueGetter: (params: GridValueGetterParams<ProcessingResults, string>) =>
|
||||
Array.isArray(params.row?.cc)
|
||||
params.row?.cc && Array.isArray(params.row.cc)
|
||||
? params.row.cc.map((point: CCPoint) => `${point.value.toFixed(2)}@${point.resolution.toFixed(2)}`).join(', ')
|
||||
: '',
|
||||
},
|
||||
@ -128,18 +158,39 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange, basePath,
|
||||
headerName: 'CC(1/2)',
|
||||
flex: 1,
|
||||
valueGetter: (params: GridValueGetterParams<ProcessingResults, string>) =>
|
||||
Array.isArray(params.row?.cchalf)
|
||||
params.row?.cchalf && Array.isArray(params.row.cchalf)
|
||||
? params.row.cchalf.map((point: CCPoint) => `${point.value.toFixed(2)}@${point.resolution.toFixed(2)}`).join(', ')
|
||||
: '',
|
||||
},
|
||||
{ field: 'completeness', headerName: 'Completeness (%)', flex: 1 },
|
||||
{ field: 'multiplicity', headerName: 'Multiplicity', flex: 1 },
|
||||
{
|
||||
field: 'completeness',
|
||||
headerName: 'Completeness (%)',
|
||||
flex: 1,
|
||||
valueGetter: (params: GridValueGetterParams<ProcessingResults, string>) =>
|
||||
params.row?.completeness
|
||||
? Array.isArray(params.row.completeness)
|
||||
? params.row.completeness.map((value: CCPoint) => `${value.value.toFixed(2)}@${value.resolution.toFixed(2)}`).join(', ')
|
||||
: params.row.completeness.toFixed(2)
|
||||
: 'N/A',
|
||||
},
|
||||
{
|
||||
field: 'multiplicity',
|
||||
headerName: 'Multiplicity',
|
||||
flex: 1,
|
||||
valueGetter: (params: GridValueGetterParams<ProcessingResults, string>) =>
|
||||
params.row?.multiplicity
|
||||
? Array.isArray(params.row.multiplicity)
|
||||
? params.row.multiplicity.map((value: CCPoint) => `${value.value.toFixed(2)}@${value.resolution.toFixed(2)}`).join(', ')
|
||||
: params.row.multiplicity.toFixed(2)
|
||||
: 'N/A',
|
||||
},
|
||||
{ field: 'nobs', headerName: 'N obs.', flex: 1 },
|
||||
{ field: 'total_refl', headerName: 'Total Reflections', flex: 1 },
|
||||
{ field: 'unique_refl', headerName: 'Unique Reflections', flex: 1 },
|
||||
{ field: 'comments', headerName: 'Comments', flex: 2 },
|
||||
];
|
||||
|
||||
|
||||
const updateHeight = () => {
|
||||
if (containerRef.current) {
|
||||
const newHeight = containerRef.current.offsetHeight;
|
||||
@ -311,31 +362,71 @@ const RunDetails: React.FC<RunDetailsProps> = ({ run, onHeightChange, basePath,
|
||||
</div>
|
||||
|
||||
{processingResult && processingResult.length > 0 && (
|
||||
<div style={{width: 400, marginTop: '16px' }}>
|
||||
<Typography variant="h6" gutterBottom>CC and CC(1/2) vs Resolution</Typography>
|
||||
<div style={{width: 400, marginTop: '16px'}}>
|
||||
<Typography variant="h6" gutterBottom>Processing Metrics vs Resolution</Typography>
|
||||
<LineChart
|
||||
xAxis={[
|
||||
{
|
||||
data: processingResult[0].cc
|
||||
.map((point) => point.resolution) // Grab the resolution values
|
||||
.reverse(), // Reverse the data for resolution
|
||||
.map((point) => point.resolution) // Use resolution values for the x-axis
|
||||
.reverse(), // Reverse the resolution values to go from high-res to low-res
|
||||
label: 'Resolution (Å)',
|
||||
reverse: true, // This ensures the visual flip on the chart, low-res to right and high-res to left
|
||||
reverse: true, // Flip visually so low-res is to the right
|
||||
},
|
||||
]}
|
||||
series={[
|
||||
{
|
||||
data: processingResult[0].cc
|
||||
.map((point) => point.value)
|
||||
.reverse(), // Reverse the CC values to match the reversed resolution
|
||||
.map((point) => point.value) // Map CC values
|
||||
.reverse(), // Reverse order for visual consistency
|
||||
label: 'CC',
|
||||
},
|
||||
{
|
||||
data: processingResult[0].cchalf
|
||||
.map((point) => point.value)
|
||||
.reverse(), // Reverse the CC(1/2) values to match the reversed resolution
|
||||
.map((point) => point.value) // Map CC(1/2) values
|
||||
.reverse(),
|
||||
label: 'CC(1/2)',
|
||||
},
|
||||
{
|
||||
data: Array.isArray(processingResult[0].rmerge)
|
||||
? processingResult[0].rmerge
|
||||
.map((point: CCPoint) => point.value) // Map Rmerge values
|
||||
.reverse()
|
||||
: [], // Handle edge case where Rmerge isn't an array
|
||||
label: 'Rmerge',
|
||||
},
|
||||
{
|
||||
data: Array.isArray(processingResult[0].rmeas)
|
||||
? processingResult[0].rmeas
|
||||
.map((point: CCPoint) => point.value) // Map Rmeas values
|
||||
.reverse()
|
||||
: [],
|
||||
label: 'Rmeas',
|
||||
},
|
||||
{
|
||||
data: Array.isArray(processingResult[0].isig)
|
||||
? processingResult[0].isig
|
||||
.map((point: CCPoint) => point.value) // Map I/sig(I) values
|
||||
.reverse()
|
||||
: [],
|
||||
label: 'I/sig(I)',
|
||||
},
|
||||
{
|
||||
data: Array.isArray(processingResult[0].completeness)
|
||||
? processingResult[0].completeness
|
||||
.map((point: CCPoint) => point.value) // Map Completeness values
|
||||
.reverse()
|
||||
: [],
|
||||
label: 'Completeness (%)',
|
||||
},
|
||||
{
|
||||
data: Array.isArray(processingResult[0].multiplicity)
|
||||
? processingResult[0].multiplicity
|
||||
.map((point: CCPoint) => point.value) // Map Multiplicity values
|
||||
.reverse()
|
||||
: [],
|
||||
label: 'Multiplicity',
|
||||
},
|
||||
]}
|
||||
height={300}
|
||||
/>
|
||||
|
@ -1,10 +1,16 @@
|
||||
// Planning.tsx
|
||||
import React from 'react';
|
||||
import CustomCalendar from '../components/Calendar.tsx';
|
||||
|
||||
const PlanningView: React.FC = () => {
|
||||
return <CustomCalendar />;
|
||||
//return <div>Welcome to the Planning Page</div>;
|
||||
interface PlanningViewProps {
|
||||
onPgroupChange?: (pgroup: string) => void;
|
||||
activePgroup: string;
|
||||
}
|
||||
|
||||
const PlanningView: React.FC<PlanningViewProps> = ({ onPgroupChange, activePgroup }) => {
|
||||
return <CustomCalendar
|
||||
activePgroup={activePgroup}
|
||||
onPgroupChange={onPgroupChange}
|
||||
/>;
|
||||
};
|
||||
|
||||
export default PlanningView;
|
@ -446,21 +446,21 @@
|
||||
{
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-05-08T13:31:36.929465Z",
|
||||
"start_time": "2025-05-08T13:31:36.925054Z"
|
||||
"end_time": "2025-05-08T15:17:09.428353Z",
|
||||
"start_time": "2025-05-08T15:17:09.424769Z"
|
||||
}
|
||||
},
|
||||
"cell_type": "code",
|
||||
"source": "sample_id = 44",
|
||||
"source": "sample_id = 106",
|
||||
"id": "54d4d46ca558e7b9",
|
||||
"outputs": [],
|
||||
"execution_count": 28
|
||||
"execution_count": 36
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-05-08T13:31:40.023546Z",
|
||||
"start_time": "2025-05-08T13:31:39.978510Z"
|
||||
"end_time": "2025-05-08T15:17:16.752451Z",
|
||||
"start_time": "2025-05-08T15:17:16.719047Z"
|
||||
}
|
||||
},
|
||||
"cell_type": "code",
|
||||
@ -478,7 +478,7 @@
|
||||
" sample_event_create = SampleEventCreate(\n",
|
||||
" sample_id=sample_id,\n",
|
||||
" #event_type=\"Centering\" # Valid event type\n",
|
||||
" event_type=\"Collecting\" # Valid event type\n",
|
||||
" event_type=\"Mounting\" # Valid event type\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Debug the payload before sending\n",
|
||||
@ -512,7 +512,7 @@
|
||||
"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",
|
||||
" warnings.warn(\n",
|
||||
"DEBUG:urllib3.connectionpool:https://localhost:8000 \"POST /samples/samples/44/events HTTP/1.1\" 200 884\n"
|
||||
"DEBUG:urllib3.connectionpool:https://localhost:8000 \"POST /samples/samples/106/events HTTP/1.1\" 200 718\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -520,25 +520,25 @@
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Payload being sent to API:\n",
|
||||
"{\"event_type\":\"Collecting\"}\n",
|
||||
"{\"event_type\":\"Mounting\"}\n",
|
||||
"API response:\n",
|
||||
"('id', 44)\n",
|
||||
"('sample_name', 'Sample044')\n",
|
||||
"('position', 7)\n",
|
||||
"('puck_id', 7)\n",
|
||||
"('id', 106)\n",
|
||||
"('sample_name', 'Sample106')\n",
|
||||
"('position', 13)\n",
|
||||
"('puck_id', 11)\n",
|
||||
"('crystalname', None)\n",
|
||||
"('proteinname', None)\n",
|
||||
"('positioninpuck', None)\n",
|
||||
"('priority', None)\n",
|
||||
"('comments', None)\n",
|
||||
"('data_collection_parameters', None)\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",
|
||||
"('events', [SampleEventResponse(event_type='Mounting', id=452, sample_id=106, timestamp=datetime.datetime(2025, 5, 8, 15, 17, 16, 743011))])\n",
|
||||
"('mount_count', 0)\n",
|
||||
"('unmount_count', 0)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"execution_count": 29
|
||||
"execution_count": 37
|
||||
},
|
||||
{
|
||||
"metadata": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user