Add mount_count and unmount_count tracking for samples
Introduced `mount_count` and `unmount_count` fields to track mounting events for samples. Updated models, schemas, and front-end components to support dynamic calculation and display of these counts. Enhanced backend queries and API responses to include the new data.
This commit is contained in:
parent
3d804c1635
commit
3b315f2997
@ -151,6 +151,16 @@ class Sample(Base):
|
|||||||
puck = relationship("Puck", back_populates="samples")
|
puck = relationship("Puck", back_populates="samples")
|
||||||
events = relationship("SampleEvent", back_populates="sample")
|
events = relationship("SampleEvent", back_populates="sample")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mount_count(self) -> int:
|
||||||
|
# Dynamically calculate mount_count
|
||||||
|
return len([event for event in self.events if event.event_type == "Mounted"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unmount_count(self) -> int:
|
||||||
|
# Dynamically calculate unmount_count
|
||||||
|
return len([event for event in self.events if event.event_type == "Unmounted"])
|
||||||
|
|
||||||
|
|
||||||
class Slot(Base):
|
class Slot(Base):
|
||||||
__tablename__ = "slots"
|
__tablename__ = "slots"
|
||||||
|
@ -9,6 +9,7 @@ from app.schemas import (
|
|||||||
Sample as SampleSchema,
|
Sample as SampleSchema,
|
||||||
SampleEventResponse,
|
SampleEventResponse,
|
||||||
SampleEventCreate,
|
SampleEventCreate,
|
||||||
|
Sample,
|
||||||
)
|
)
|
||||||
from app.models import (
|
from app.models import (
|
||||||
Puck as PuckModel,
|
Puck as PuckModel,
|
||||||
@ -17,6 +18,8 @@ from app.models import (
|
|||||||
)
|
)
|
||||||
from app.dependencies import get_db
|
from app.dependencies import get_db
|
||||||
import logging
|
import logging
|
||||||
|
from sqlalchemy.orm import joinedload
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
@ -43,22 +46,25 @@ async def get_samples_with_events(puck_id: int, db: Session = Depends(get_db)):
|
|||||||
async def get_all_pucks_with_samples_and_events(db: Session = Depends(get_db)):
|
async def get_all_pucks_with_samples_and_events(db: Session = Depends(get_db)):
|
||||||
logging.info("Fetching all pucks with samples and events")
|
logging.info("Fetching all pucks with samples and events")
|
||||||
|
|
||||||
pucks = db.query(PuckModel).all()
|
pucks = (
|
||||||
logging.info(f"Found {len(pucks)} pucks in the database")
|
db.query(PuckModel)
|
||||||
for puck in pucks:
|
.options(
|
||||||
if puck.dewar_id is None:
|
joinedload(PuckModel.samples).joinedload(
|
||||||
puck.dewar_id = -1
|
SampleModel.events
|
||||||
logging.info(f"Puck ID: {puck.id}, Name: {puck.puck_name}")
|
), # Correct nested relationship
|
||||||
|
joinedload(PuckModel.events), # If Puck has its own events relationship
|
||||||
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
if not pucks:
|
if not pucks:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail="No pucks found in the database")
|
||||||
status_code=404, detail="No pucks found in the database"
|
|
||||||
) # More descriptive
|
|
||||||
return pucks
|
return pucks
|
||||||
|
|
||||||
|
|
||||||
# Route to post a new sample event
|
# Route to post a new sample event
|
||||||
@router.post("/samples/{sample_id}/events", response_model=SampleEventResponse)
|
@router.post("/samples/{sample_id}/events", response_model=Sample)
|
||||||
async def create_sample_event(
|
async def create_sample_event(
|
||||||
sample_id: int, event: SampleEventCreate, db: Session = Depends(get_db)
|
sample_id: int, event: SampleEventCreate, db: Session = Depends(get_db)
|
||||||
):
|
):
|
||||||
@ -77,10 +83,13 @@ async def create_sample_event(
|
|||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(sample_event)
|
db.refresh(sample_event)
|
||||||
|
|
||||||
return (
|
# Load events for the sample to be serialized in the response
|
||||||
sample_event # Response will automatically use the SampleEventResponse schema
|
sample.events = (
|
||||||
|
db.query(SampleEventModel).filter(SampleEventModel.sample_id == sample_id).all()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return sample # Return the sample, now including `mount_count`
|
||||||
|
|
||||||
|
|
||||||
# Route to fetch the last (most recent) sample event
|
# Route to fetch the last (most recent) sample event
|
||||||
@router.get("/samples/{sample_id}/events/last", response_model=SampleEventResponse)
|
@router.get("/samples/{sample_id}/events/last", response_model=SampleEventResponse)
|
||||||
|
@ -424,6 +424,9 @@ class Sample(BaseModel):
|
|||||||
comments: Optional[str] = None
|
comments: Optional[str] = None
|
||||||
data_collection_parameters: Optional[DataCollectionParameters]
|
data_collection_parameters: Optional[DataCollectionParameters]
|
||||||
events: List[SampleEventResponse] = []
|
events: List[SampleEventResponse] = []
|
||||||
|
mount_count: Optional[int] = None
|
||||||
|
unmount_count: Optional[int] = None
|
||||||
|
# results: Optional[Results] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
@ -13,6 +13,8 @@ interface Sample {
|
|||||||
crystalname?: string;
|
crystalname?: string;
|
||||||
positioninpuck?: number;
|
positioninpuck?: number;
|
||||||
events: Event[];
|
events: Event[];
|
||||||
|
mount_count: number; // Add this
|
||||||
|
unmount_count: number; // Add this
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Puck {
|
interface Puck {
|
||||||
@ -32,6 +34,8 @@ const SampleTracker: React.FC = () => {
|
|||||||
const fetchPucks = async () => {
|
const fetchPucks = async () => {
|
||||||
try {
|
try {
|
||||||
const data: Puck[] = await SamplesService.getAllPucksWithSamplesAndEventsSamplesPucksSamplesGet();
|
const data: Puck[] = await SamplesService.getAllPucksWithSamplesAndEventsSamplesPucksSamplesGet();
|
||||||
|
|
||||||
|
console.log('Fetched Pucks:', data); // Check for dynamic mount_count and unmount_count
|
||||||
setPucks(data);
|
setPucks(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching pucks', error);
|
console.error('Error fetching pucks', error);
|
||||||
@ -110,12 +114,29 @@ const SampleTracker: React.FC = () => {
|
|||||||
border: sample && sample.events.some((e) => e.event_type === 'Lost')
|
border: sample && sample.events.some((e) => e.event_type === 'Lost')
|
||||||
? '1px solid red'
|
? '1px solid red'
|
||||||
: '1px solid lightgray',
|
: '1px solid lightgray',
|
||||||
|
position: 'relative', // Add for overlay positioning
|
||||||
}}
|
}}
|
||||||
onMouseEnter={() =>
|
onMouseEnter={() =>
|
||||||
sample && setHoveredSample({ name: sample.sample_name, status })
|
sample && setHoveredSample({ name: sample.sample_name, status })
|
||||||
}
|
}
|
||||||
onMouseLeave={() => setHoveredSample(null)}
|
onMouseLeave={() => setHoveredSample(null)}
|
||||||
></div>
|
>
|
||||||
|
{sample && sample.mount_count > 0 && ( // Render only if mount_count > 0
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
color: 'white',
|
||||||
|
fontSize: '8px',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{sample.mount_count}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user