added sample tracker on the frontend

This commit is contained in:
GotthardG 2024-12-04 14:45:47 +01:00
parent 1a1a710edf
commit 7b00db3c0d
6 changed files with 200 additions and 2 deletions

View File

@ -5,7 +5,7 @@ from fastapi.middleware.cors import CORSMiddleware
from app import ssl_heidi from app import ssl_heidi
from pathlib import Path from pathlib import Path
from app.routers import address, contact, proposal, dewar, shipment, puck, spreadsheet, logistics, auth from app.routers import address, contact, proposal, dewar, shipment, puck, spreadsheet, logistics, auth, sample
from app.database import Base, engine, SessionLocal, load_sample_data from app.database import Base, engine, SessionLocal, load_sample_data
app = FastAPI() app = FastAPI()
@ -48,6 +48,7 @@ app.include_router(shipment.router, prefix="/shipments", tags=["shipments"])
app.include_router(puck.router, prefix="/pucks", tags=["pucks"]) app.include_router(puck.router, prefix="/pucks", tags=["pucks"])
app.include_router(spreadsheet.router, tags=["spreadsheet"]) app.include_router(spreadsheet.router, tags=["spreadsheet"])
app.include_router(logistics.router, prefix="/logistics", tags=["logistics"]) app.include_router(logistics.router, prefix="/logistics", tags=["logistics"])
app.include_router(sample.router, prefix="/samples", tags=["samples"])
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -0,0 +1,36 @@
from fastapi import APIRouter, HTTPException, status, Depends
from sqlalchemy.orm import Session
from typing import List
from app.schemas import Puck as PuckSchema, Sample as SampleSchema, SampleEventCreate
from app.models import Puck as PuckModel, Sample as SampleModel, SampleEvent as SampleEventModel
from app.dependencies import get_db
import logging
router = APIRouter()
@router.get("/{puck_id}/samples", response_model=List[SampleSchema])
async def get_samples_with_events(puck_id: str, 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")
samples = db.query(SampleModel).filter(SampleModel.puck_id == puck_id).all()
for sample in samples:
sample.events = db.query(SampleEventModel).filter(SampleEventModel.sample_id == sample.id).all()
return samples
@router.get("/pucks-samples", response_model=List[PuckSchema])
async def get_all_pucks_with_samples_and_events(db: Session = Depends(get_db)):
logging.info("Fetching all pucks with samples and events")
pucks = db.query(PuckModel).all()
logging.info(f"Found {len(pucks)} pucks in the database")
for puck in pucks:
logging.info(f"Puck ID: {puck.id}, Name: {puck.puck_name}")
if not pucks:
raise HTTPException(status_code=404, detail="No pucks found in the database") # More descriptive
return pucks

View File

@ -137,6 +137,7 @@ class Sample(BaseModel):
puck_id: int puck_id: int
crystalname: Optional[str] = Field(None) crystalname: Optional[str] = Field(None)
positioninpuck: Optional[int] = Field(None) positioninpuck: Optional[int] = Field(None)
events: List[SampleEventCreate] = []
class SampleCreate(BaseModel): class SampleCreate(BaseModel):

View File

@ -36,3 +36,61 @@
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
.sample-tracker-container {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
max-width: 80%; /* Reduce this percentage to fit your needs */
width: auto; /* Auto width for responsiveness */
margin: 0 auto; /* Center the tracker horizontally */
overflow-x: auto; /* Allow horizontal scrolling if necessary */
}
.sample-tracker {
display: flex;
flex-direction: column;
align-items: center;
}
.pucks-container {
display: flex;
flex-wrap: nowrap;
align-items: flex-start;
}
.puck-column {
display: flex;
flex-direction: column;
align-items: center;
margin: 0 10px;
}
.puck-label {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
height: 100px;
margin-bottom: 5px;
}
.puck-label span {
display: block;
}
.samples {
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
}
.sample-dot {
width: 15px;
height: 15px;
border-radius: 50%;
display: inline-block;
border: 1px solid lightgray;
}

View File

@ -0,0 +1,95 @@
import React, { useEffect, useState } from 'react';
import { SamplesService } from '../../openapi';
interface Event {
event_type: string;
}
interface Sample {
id: number;
sample_name: string;
position: number;
puck_id: number;
crystalname?: string;
positioninpuck?: number;
events: Event[];
}
interface Puck {
id: number;
puck_name: string;
puck_type: string;
puck_location_in_dewar: number;
dewar_id: number;
samples: Sample[]; // Check that the API returns this attribute
}
const SampleTracker: React.FC = () => {
const [pucks, setPucks] = useState<Puck[]>([]);
useEffect(() => {
const fetchPucks = async () => {
try {
const data: Puck[] = await SamplesService.getAllPucksWithSamplesAndEventsSamplesPucksSamplesGet();
console.log(data); // Log the data to inspect it
setPucks(data);
} catch (error) {
console.error('Error fetching pucks', error);
}
};
fetchPucks();
}, []);
const getSampleColor = (events: Event[] = []) => {
const hasMounted = events.some(e => e.event_type === 'Mounted');
const hasUnmounted = events.some(e => e.event_type === 'Unmounted');
const hasLost = events.some(e => e.event_type === 'Lost');
const hasFailed = events.some(e => e.event_type === 'Failed');
if (hasFailed) return 'red';
if (hasLost) return 'orange';
if (hasMounted && hasUnmounted) return 'green';
return 'gray';
};
return (
<div className="sample-tracker-container">
<div className="sample-tracker">
<h2>All Pucks and Samples</h2>
<div className="pucks-container">
{pucks.map((puck) => (
<div key={puck.id} className="puck-column">
<div className="puck-label">
{puck.puck_name.split('').map((char, i) => (
<span key={i}>{char}</span>
))}
</div>
<div className="samples">
{Array.from({length: 16}).map((_, index) => {
const sample = puck.samples.find(s => s.position === index + 1);
return (
<div
key={index}
className="sample-dot"
style={{
backgroundColor: sample
? getSampleColor(sample.events)
: 'transparent',
border: sample && sample.events.some(e => e.event_type === 'Lost')
? '1px solid red' : '1px solid lightgray',
}}
></div>
);
})}
</div>
</div>
))}
</div>
</div>
</div>
);
};
export default SampleTracker;

View File

@ -1,8 +1,15 @@
// components/ResultView.tsx // components/ResultView.tsx
import React from 'react'; import React from 'react';
import SampleTracker from '../components/SampleTracker';
const ResultsView: React.FC = () => { const ResultsView: React.FC = () => {
return <div>Results page</div>; return (
<div>
<h1>Results Page</h1>
<SampleTracker />
</div>
);
}; };
export default ResultsView; export default ResultsView;