Code indexing in gitaly is broken and leads to code not being visible to the user. We work on the issue with highest priority.

Skip to content
Snippets Groups Projects
Commit 43d67b10 authored by GotthardG's avatar GotthardG
Browse files

Enhance Dewar handling and display in logistics system

Added new fields and enriched data representations in DewarStatusTab, backend schemas, and APIs to improve dewar tracking and management. Introduced new API endpoint `/dewar/table` for simplified data rendering. Applied logging and validations for missing relationships.
parent 25673ae0
No related branches found
No related tags found
No related merge requests found
Pipeline #48592 failed
......@@ -7,7 +7,14 @@ from ..models import (
Slot as SlotModel,
LogisticsEvent as LogisticsEventModel,
)
from ..schemas import LogisticsEventCreate, SlotSchema, Dewar as DewarSchema
from ..schemas import (
LogisticsEventCreate,
SlotSchema,
Dewar as DewarSchema,
DewarTable,
ContactMinimal,
AddressMinimal,
)
from ..database import get_db
import logging
from datetime import datetime, timedelta
......@@ -342,6 +349,64 @@ async def get_all_dewars(db: Session = Depends(get_db)):
return dewars
@router.get("/dewar/table", response_model=List[DewarTable])
async def get_all_dewars_table(db: Session = Depends(get_db)):
dewars = db.query(DewarModel).all()
# Flatten relationships for simplified frontend rendering
response = []
for dewar in dewars:
response.append(
DewarTable(
id=dewar.id,
shipment_id=dewar.shipment_id
if hasattr(dewar, "shipment_id")
else None,
dewar_name=dewar.dewar_name,
shipment_name=dewar.shipment.shipment_name if dewar.shipment else "N/A",
# Use the most recent event if available
status=dewar.events[-1].event_type if dewar.events else "No Events",
tracking_number=dewar.tracking_number or "N/A",
slot_id=dewar.slot[0].id
if dewar.slot
else None, # Use first slot if available
contact=[
ContactMinimal(
firstname=dewar.contact.firstname,
lastname=dewar.contact.lastname,
email=dewar.contact.email,
id=dewar.contact.id,
)
]
if dewar.contact
else [],
address=[
AddressMinimal(
house_number=dewar.return_address.house_number,
street=dewar.return_address.street,
city=dewar.return_address.city,
state=dewar.return_address.state,
country=dewar.return_address.country,
zipcode=dewar.return_address.zipcode,
id=dewar.return_address.id,
)
]
if dewar.return_address
else [],
events=dewar.events[-1].slot_id if dewar.events else "No Events",
last_updated=dewar.events[-1].timestamp if dewar.events else None,
)
)
# Add logging for missing relationships
if not hasattr(dewar, "pgroups"):
logger.warning(f"Dewar {dewar.id} is missing 'pgroups'")
if not hasattr(dewar, "shipment_id"):
logger.warning(f"Dewar {dewar.id} is missing 'shipment_id'")
return response
@router.get("/dewar/{unique_id}", response_model=DewarSchema)
async def get_dewar_by_unique_id(unique_id: str, db: Session = Depends(get_db)):
logger.info(f"Received request for dewar with unique_id: {unique_id}")
......
from typing import List, Optional
from typing import List, Optional, Union
from datetime import datetime
from pydantic import BaseModel, EmailStr, constr, Field, field_validator
from datetime import date
......@@ -410,6 +410,13 @@ class ContactUpdate(BaseModel):
email: Optional[EmailStr] = None
class ContactMinimal(BaseModel):
firstname: str
lastname: str
email: EmailStr
id: int
class AddressCreate(BaseModel):
pgroups: str
house_number: Optional[str] = None
......@@ -438,6 +445,16 @@ class AddressUpdate(BaseModel):
country: Optional[str] = None
class AddressMinimal(BaseModel):
house_number: str
street: str
city: str
state: Optional[str] = None
zipcode: str
country: str
id: int
class Sample(BaseModel):
id: int
sample_name: str
......@@ -578,6 +595,28 @@ class DewarSchema(BaseModel):
# Tracking will also become an event
class DewarTable(BaseModel):
id: int
pgroups: Optional[str] = None # Make "pgroups" optional
shipment_id: Optional[int] = None # Make "shipment_id" optional
shipment_name: str
dewar_name: str
tracking_number: Optional[str] = None
dewar_type_id: Optional[int] = None
dewar_serial_number_id: Optional[int] = None
unique_id: Optional[str] = None
status: Optional[str] = None
contact: Optional[List[ContactMinimal]] = None
address: Optional[List[AddressMinimal]] = None
event_id: Optional[int] = None
slot_id: Optional[int] = None
events: Optional[Union[str, int]] = None
last_updated: Optional[datetime] = None
class Config:
from_attributes = True
class Proposal(BaseModel):
id: int
number: str
......
......@@ -2,12 +2,18 @@ import React, { useEffect, useState } from "react";
import DataGrid from "react-data-grid";
import { Box, Typography, Snackbar, Alert, CircularProgress } from "@mui/material";
import { LogisticsService } from "../../../frontend/openapi";
import "react-data-grid/lib/styles.css";
import dayjs from 'dayjs'; // Import dayjs library
interface Dewar {
id: string;
dewar_name: string;
shipment_name: string; // Added new field
slot_id: string; // Added new field
status: string;
location: string;
beamline_location: string;
timestamp: string; // You can change this type based on your API response
}
......@@ -15,54 +21,149 @@ const DewarStatusTab: React.FC = () => {
const [dewars, setDewars] = useState<Dewar[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string | null>(null);
const slotQRCodes = [
"A1-X06SA",
"A2-X06SA",
"A3-X06SA",
"A4-X06SA",
"A5-X06SA",
"B1-X06SA",
"B2-X06SA",
"B3-X06SA",
"B4-X06SA",
"B5-X06SA",
"C1-X06SA",
"C2-X06SA",
"C3-X06SA",
"C4-X06SA",
"C5-X06SA",
"D1-X06SA",
"D2-X06SA",
"D3-X06SA",
"D4-X06SA",
"D5-X06SA",
"A1-X10SA",
"A2-X10SA",
"A3-X10SA",
"A4-X10SA",
"A5-X10SA",
"B1-X10SA",
"B2-X10SA",
"B3-X10SA",
"B4-X10SA",
"B5-X10SA",
"C1-X10SA",
"C2-X10SA",
"C3-X10SA",
"C4-X10SA",
"C5-X10SA",
"D1-X10SA",
"D2-X10SA",
"D3-X10SA",
"D4-X10SA",
"D5-X10SA",
"NB1",
"NB2",
"NB3",
"NB4",
"NB5",
"NB6",
"X10SA-Beamline",
"X06SA-Beamline",
"X06DA-Beamline",
"Outgoing X10SA",
"Outgoing X06SA",
];
// Updated columns array
const columns = [
{ key: "shipment_name", name: "Shipment Name", resizable: true },
{ key: "dewar_name", name: "Dewar Name", resizable: true },
{
key: "status",
name: "Status",
editable: true,
resizable: true,
editor: (props: { row: any; column: any; onRowChange: any }) => {
return (
<input
type="text"
value={props.row[props.column.key]}
onChange={(e) => props.onRowChange({ ...props.row, [props.column.key]: e.target.value })}
style={{
border: "none",
outline: "none",
padding: "4px",
}}
/>
);
},
},
{ key: "location", name: "Location", resizable: true },
{ key: "timestamp", name: "Last Updated", resizable: true },
{ key: "slot_id", name: "Storage", resizable: true },
{ key: "status", name: "Status", editable: true, resizable: true },
{ key: "beamline_location", name: "Location", resizable: true },
{ key: "last_updated", name: "Last Updated", resizable: true },
{ key: "local_contact", name: "Local Contact", resizable: true }, // Now a string
{ key: "contact", name: "Contact", resizable: true }, // Now a string
{ key: "address", name: "Return Address", resizable: true }, // Now a string
];
// Fetch dewars when component mounts
useEffect(() => {
fetchDewarData();
}, []);
const fetchDewarData = async () => {
setLoading(true);
try {
const dewarData = await LogisticsService.getAllDewarsLogisticsDewarsGet(); // Use your real API call
setDewars(dewarData);
// Fetch data from API
const dewarData = await LogisticsService.getAllDewarsTableLogisticsDewarTableGet();
// Log the raw data for debugging
console.log("Fetched dewarData:", dewarData);
// Flatten and enrich data
const enrichedData = dewarData.map((dewar: any) => {
// Format address into a single string
const returnAddress = dewar.address && dewar.address.length > 0
? `${dewar.address[0].house_number || ""} ${dewar.address[0].street || ""}, ${dewar.address[0].city || ""}, ${dewar.address[0].state || ""}, ${dewar.address[0].zipcode || ""}, ${dewar.address[0].country || ""}`.trim()
: "N/A";
// Format contact into a single string
const contact = dewar.contact && dewar.contact.length > 0
? `${dewar.contact[0].firstname || "N/A"} ${dewar.contact[0].lastname || "N/A"} (${dewar.contact[0].email || "N/A"})`
: "N/A";
// Format local_contact into a single string
const localContact = dewar.local_contact
? `${dewar.local_contact.firstname || "N/A"} ${dewar.local_contact.lastname || "N/A"} (${dewar.local_contact.phone_number || "N/A"})`
: "N/A";
const beamline_location = dewar.events.slot_id || "N/A";
console.log("Beamline location:", beamline_location);
// Log any fields that are missing or appear incorrect
if (!dewar.local_contact) console.warn("Missing local_contact for dewar:", dewar);
if (!dewar.contact) console.warn("Missing contact for dewar:", dewar);
if (!dewar.address) console.warn("Missing address for dewar:", dewar);
return {
...dewar,
local_contact: localContact,
contact: contact,
address: returnAddress, // Replace `address` object with single formatted string
beamline_location: dewar.events !== undefined && slotQRCodes[dewar.events]
? slotQRCodes[dewar.events -1]
: "",
slot_id: dewar.slot_id !== undefined && slotQRCodes[dewar.slot_id]
? slotQRCodes[dewar.slot_id -1]
: "", // Convert slot_id to descriptive label
last_updated: dewar.last_updated
? new Date(dewar.last_updated).toLocaleString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
hour12: true,
})
: ""
};
});
setDewars(enrichedData);
console.log("Final enrichedData:", enrichedData);
} catch (e) {
console.error("Failed to fetch or process dewar data:", e);
setError("Failed to fetch dewar data");
} finally {
setLoading(false);
}
};
const onRowsChange = async (updatedRow: Dewar[]) => {
setDewars(updatedRow);
useEffect(() => {
fetchDewarData();
}, []);
const onRowsChange = async (updatedRows: Dewar[]) => {
setDewars(updatedRows);
try {
const updatedDewar = updatedRow[updatedRow.length - 1]; // Get the last edited row
const updatedDewar = updatedRows[updatedRows.length - 1]; // Get the last edited row
await LogisticsService.updateDewarStatus({ ...updatedDewar }); // Mock API update
} catch (err) {
setError("Error updating dewar");
......@@ -87,7 +188,7 @@ const DewarStatusTab: React.FC = () => {
columns={columns}
rows={dewars}
onRowsChange={onRowsChange}
style={{ height: 600, width: "100%" }}
style={{ height: 600, width: "100%" }} // Make sure height and width are set
/>
)}
</Box>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment