Integration of sqlite3 database now fully functional with all implemented functions

This commit is contained in:
GotthardG
2024-11-02 12:12:30 +01:00
parent a01114a178
commit 84f270b647
4 changed files with 90 additions and 82 deletions

View File

@ -1,9 +1,7 @@
from sqlalchemy import Column, Integer, String, Date, ForeignKey from sqlalchemy import Column, Integer, String, Date, ForeignKey
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from app.database import Base # Ensure this imports correctly from app.database import Base
# SQLAlchemy ORM models
class Shipment(Base): class Shipment(Base):
__tablename__ = "shipments" __tablename__ = "shipments"
@ -21,7 +19,6 @@ class Shipment(Base):
proposal = relationship("Proposal", back_populates="shipments") proposal = relationship("Proposal", back_populates="shipments")
dewars = relationship("Dewar", back_populates="shipment") dewars = relationship("Dewar", back_populates="shipment")
class ContactPerson(Base): class ContactPerson(Base):
__tablename__ = "contact_persons" __tablename__ = "contact_persons"
@ -33,7 +30,6 @@ class ContactPerson(Base):
shipments = relationship("Shipment", back_populates="contact_person") shipments = relationship("Shipment", back_populates="contact_person")
class Address(Base): class Address(Base):
__tablename__ = "addresses" __tablename__ = "addresses"
@ -45,7 +41,6 @@ class Address(Base):
shipments = relationship("Shipment", back_populates="return_address") shipments = relationship("Shipment", back_populates="return_address")
class Dewar(Base): class Dewar(Base):
__tablename__ = "dewars" __tablename__ = "dewars"
@ -65,9 +60,8 @@ class Dewar(Base):
contact_person_id = Column(Integer, ForeignKey("contact_persons.id")) # Added contact_person_id = Column(Integer, ForeignKey("contact_persons.id")) # Added
shipment = relationship("Shipment", back_populates="dewars") shipment = relationship("Shipment", back_populates="dewars")
return_address = relationship("Address") # Defines relationship with Address return_address = relationship("Address")
contact_person = relationship("ContactPerson") # Defines relationship with ContactPerson contact_person = relationship("ContactPerson")
class Proposal(Base): class Proposal(Base):
__tablename__ = "proposals" __tablename__ = "proposals"

View File

@ -2,17 +2,23 @@ from fastapi import APIRouter, HTTPException, status, Query, Depends
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from typing import List, Optional from typing import List, Optional
import uuid import uuid
import json
from datetime import date
from app.schemas import ShipmentCreate, Shipment as ShipmentSchema, ContactPerson as ContactPersonSchema from app.models import Shipment as ShipmentModel, ContactPerson as ContactPersonModel, Address as AddressModel, Proposal as ProposalModel, Dewar as DewarModel
from app.schemas import ShipmentCreate, Shipment as ShipmentSchema, DewarUpdate, ContactPerson as ContactPersonSchema
from app.database import get_db from app.database import get_db
from app.crud import get_shipments, get_shipment_by_id from app.crud import get_shipments, get_shipment_by_id
router = APIRouter() router = APIRouter()
def default_serializer(obj):
if isinstance(obj, date):
return obj.isoformat()
raise TypeError(f"Type {type(obj)} not serializable")
@router.get("", response_model=List[ShipmentSchema]) @router.get("", response_model=List[ShipmentSchema])
async def fetch_shipments(shipment_id: Optional[str] = Query(None), async def fetch_shipments(shipment_id: Optional[str] = Query(None), db: Session = Depends(get_db)):
db: Session = Depends(get_db)):
if shipment_id: if shipment_id:
shipment = get_shipment_by_id(db, shipment_id) shipment = get_shipment_by_id(db, shipment_id)
if not shipment: if not shipment:
@ -20,17 +26,13 @@ async def fetch_shipments(shipment_id: Optional[str] = Query(None),
return [shipment] return [shipment]
return get_shipments(db) return get_shipments(db)
@router.post("", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED) @router.post("", response_model=ShipmentSchema, status_code=status.HTTP_201_CREATED)
async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)): async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db)):
from app.models import Shipment as ShipmentModel, ContactPerson as ContactPersonModel, Address as AddressModel, \
Proposal as ProposalModel, Dewar as DewarModel
contact_person = db.query(ContactPersonModel).filter(ContactPersonModel.id == shipment.contact_person_id).first() contact_person = db.query(ContactPersonModel).filter(ContactPersonModel.id == shipment.contact_person_id).first()
return_address = db.query(AddressModel).filter(AddressModel.id == shipment.return_address_id).first() return_address = db.query(AddressModel).filter(AddressModel.id == shipment.return_address_id).first()
proposal = db.query(ProposalModel).filter(ProposalModel.id == shipment.proposal_id).first() proposal = db.query(ProposalModel).filter(ProposalModel.id == shipment.proposal_id).first()
if not (contact_person and return_address and proposal): if not (contact_person or return_address or proposal):
raise HTTPException(status_code=404, detail="Associated entity not found") raise HTTPException(status_code=404, detail="Associated entity not found")
shipment_id = f'SHIP-{uuid.uuid4().hex[:8].upper()}' shipment_id = f'SHIP-{uuid.uuid4().hex[:8].upper()}'
@ -47,7 +49,8 @@ async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db
# Handling dewars association # Handling dewars association
if shipment.dewars: if shipment.dewars:
dewars = db.query(DewarModel).filter(DewarModel.id.in_(shipment.dewars)).all() dewar_ids = [dewar.dewar_id for dewar in shipment.dewars]
dewars = db.query(DewarModel).filter(DewarModel.id.in_(dewar_ids)).all()
if len(dewars) != len(shipment.dewars): if len(dewars) != len(shipment.dewars):
raise HTTPException(status_code=404, detail="One or more dewars not found") raise HTTPException(status_code=404, detail="One or more dewars not found")
db_shipment.dewars.extend(dewars) db_shipment.dewars.extend(dewars)
@ -58,10 +61,8 @@ async def create_shipment(shipment: ShipmentCreate, db: Session = Depends(get_db
return db_shipment return db_shipment
@router.delete("/{shipment_id}", status_code=status.HTTP_204_NO_CONTENT) @router.delete("/{shipment_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_shipment(shipment_id: str, db: Session = Depends(get_db)): async def delete_shipment(shipment_id: str, db: Session = Depends(get_db)):
from app.models import Shipment as ShipmentModel
shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first() shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first()
if not shipment: if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found") raise HTTPException(status_code=404, detail="Shipment not found")
@ -69,35 +70,22 @@ async def delete_shipment(shipment_id: str, db: Session = Depends(get_db)):
db.commit() db.commit()
return return
@router.put("/{shipment_id}", response_model=ShipmentSchema)
@router.put("/shipments/{shipment_id}", response_model=ShipmentSchema)
async def update_shipment(shipment_id: str, updated_shipment: ShipmentCreate, db: Session = Depends(get_db)): async def update_shipment(shipment_id: str, updated_shipment: ShipmentCreate, db: Session = Depends(get_db)):
from app.models import Shipment as ShipmentModel, ContactPerson as ContactPersonModel, Address as AddressModel, \ print("Received payload:", json.dumps(updated_shipment.dict(), indent=2, default=default_serializer))
Dewar as DewarModel
# Log incoming payload for detailed inspection
print("Received payload:", json.dumps(updated_shipment.dict(), indent=2))
try:
shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first() shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first()
if not shipment: if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found") raise HTTPException(status_code=404, detail="Shipment not found")
# Validate relationships by IDs # Validate relationships by IDs
contact_person = db.query(ContactPersonModel).filter( contact_person = db.query(ContactPersonModel).filter(ContactPersonModel.id == updated_shipment.contact_person_id).first()
ContactPersonModel.id == updated_shipment.contact_person_id).first()
return_address = db.query(AddressModel).filter(AddressModel.id == updated_shipment.return_address_id).first() return_address = db.query(AddressModel).filter(AddressModel.id == updated_shipment.return_address_id).first()
if not contact_person: if not contact_person:
raise HTTPException(status_code=404, detail="Contact person not found") raise HTTPException(status_code=404, detail="Contact person not found")
if not return_address: if not return_address:
raise HTTPException(status_code=404, detail="Return address not found") raise HTTPException(status_code=404, detail="Return address not found")
# Handling dewars association by IDs
dewars_ids = [d['id'] for d in updated_shipment.dewars]
dewars = db.query(DewarModel).filter(DewarModel.id.in_(dewars_ids)).all()
if len(dewars) != len(dewars_ids):
raise HTTPException(status_code=422, detail="One or more dewars not found")
# Update shipment details # Update shipment details
shipment.shipment_name = updated_shipment.shipment_name shipment.shipment_name = updated_shipment.shipment_name
shipment.shipment_date = updated_shipment.shipment_date shipment.shipment_date = updated_shipment.shipment_date
@ -105,21 +93,35 @@ async def update_shipment(shipment_id: str, updated_shipment: ShipmentCreate, db
shipment.comments = updated_shipment.comments shipment.comments = updated_shipment.comments
shipment.contact_person_id = updated_shipment.contact_person_id shipment.contact_person_id = updated_shipment.contact_person_id
shipment.return_address_id = updated_shipment.return_address_id shipment.return_address_id = updated_shipment.return_address_id
shipment.dewars = dewars
# Process and update dewars' details
for dewar_data in updated_shipment.dewars:
dewar = db.query(DewarModel).filter(DewarModel.id == dewar_data.dewar_id).first()
if not dewar:
raise HTTPException(status_code=404, detail=f"Dewar with ID {dewar_data.dewar_id} not found")
# Dynamically update the dewar fields based on provided input
update_fields = dewar_data.dict(exclude_unset=True)
for key, value in update_fields.items():
if key == 'contact_person_id':
contact_person = db.query(ContactPersonModel).filter(ContactPersonModel.id == value).first()
if not contact_person:
raise HTTPException(status_code=404, detail=f"Contact person with ID {value} for Dewar {dewar_data.dewar_id} not found")
if key == 'return_address_id':
address = db.query(AddressModel).filter(AddressModel.id == value).first()
if not address:
raise HTTPException(status_code=404, detail=f"Address with ID {value} for Dewar {dewar_data.dewar_id} not found")
for key, value in update_fields.items():
if key != 'dewar_id':
setattr(dewar, key, value)
db.commit() db.commit()
db.refresh(shipment) db.refresh(shipment)
return shipment return shipment
except Exception as e:
print(f"Update failed with exception: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/{shipment_id}/add_dewar", response_model=ShipmentSchema) @router.post("/{shipment_id}/add_dewar", response_model=ShipmentSchema)
async def add_dewar_to_shipment(shipment_id: str, dewar_id: str, db: Session = Depends(get_db)): async def add_dewar_to_shipment(shipment_id: str, dewar_id: str, db: Session = Depends(get_db)):
from app.models import Shipment as ShipmentModel, Dewar as DewarModel
shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first() shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first()
if not shipment: if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found") raise HTTPException(status_code=404, detail="Shipment not found")
@ -133,10 +135,8 @@ async def add_dewar_to_shipment(shipment_id: str, dewar_id: str, db: Session = D
db.refresh(shipment) db.refresh(shipment)
return shipment return shipment
@router.delete("/{shipment_id}/remove_dewar/{dewar_id}", response_model=ShipmentSchema) @router.delete("/{shipment_id}/remove_dewar/{dewar_id}", response_model=ShipmentSchema)
async def remove_dewar_from_shipment(shipment_id: str, dewar_id: str, db: Session = Depends(get_db)): async def remove_dewar_from_shipment(shipment_id: str, dewar_id: str, db: Session = Depends(get_db)):
from app.models import Shipment as ShipmentModel, Dewar as DewarModel
shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first() shipment = db.query(ShipmentModel).filter(ShipmentModel.shipment_id == shipment_id).first()
if not shipment: if not shipment:
raise HTTPException(status_code=404, detail="Shipment not found") raise HTTPException(status_code=404, detail="Shipment not found")
@ -146,9 +146,7 @@ async def remove_dewar_from_shipment(shipment_id: str, dewar_id: str, db: Sessio
db.refresh(shipment) db.refresh(shipment)
return shipment return shipment
@router.get("/contact_persons", response_model=List[ContactPersonSchema]) @router.get("/contact_persons", response_model=List[ContactPersonSchema])
async def get_shipment_contact_persons(db: Session = Depends(get_db)): async def get_shipment_contact_persons(db: Session = Depends(get_db)):
from app.models import ContactPerson as ContactPersonModel
contact_persons = db.query(ContactPersonModel).all() contact_persons = db.query(ContactPersonModel).all()
return contact_persons return contact_persons

View File

@ -101,8 +101,21 @@ class Shipment(BaseModel):
class Config: class Config:
from_attributes = True from_attributes = True
class DewarUpdate(BaseModel):
dewar_id: str
dewar_name: Optional[str] = None
tracking_number: Optional[str] = None
number_of_pucks: Optional[int] = None
number_of_samples: Optional[int] = None
status: Optional[str] = None
ready_date: Optional[date] = None
shipping_date: Optional[date] = None
arrival_date: Optional[date] = None
returning_date: Optional[date] = None
qrcode: Optional[str] = None
contact_person_id: Optional[int] = None
address_id: Optional[int] = None # Added
# Create schema for Shipment
class ShipmentCreate(BaseModel): class ShipmentCreate(BaseModel):
shipment_name: str shipment_name: str
shipment_date: date shipment_date: date
@ -110,8 +123,8 @@ class ShipmentCreate(BaseModel):
comments: Optional[str] = "" comments: Optional[str] = ""
contact_person_id: int contact_person_id: int
return_address_id: int return_address_id: int
proposal_id: int # Change "proposal_number_id" to "proposal_id" proposal_id: int
dewars: Optional[List[str]] = [] dewars: Optional[List[DewarUpdate]] = []
class Config: class Config:
from_attributes = True from_attributes = True

View File

@ -12,7 +12,7 @@ import QRCode from 'react-qr-code';
import { import {
ContactPerson, ContactPerson,
Address, Address,
Dewar, ContactsService, AddressesService, ShipmentsService, Dewar, ContactsService, AddressesService, ShipmentsService, DewarsService,
} from '../../openapi'; } from '../../openapi';
interface DewarDetailsProps { interface DewarDetailsProps {
@ -200,10 +200,10 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
const handleSaveChanges = async () => { const handleSaveChanges = async () => {
console.log('handleSaveChanges called'); console.log('handleSaveChanges called');
const formatDate = (dateString: string | undefined): string => { const formatDate = (dateString: string | undefined): string | null => {
if (!dateString) return ''; if (!dateString) return null;
const date = new Date(dateString); const date = new Date(dateString);
if (isNaN(date.getTime())) return ''; if (isNaN(date.getTime())) return null;
return date.toISOString().split('T')[0]; return date.toISOString().split('T')[0];
}; };
@ -231,7 +231,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
} }
const updatedDewar = { const updatedDewar = {
id: dewar.id, dewar_id: dewar.id,
dewar_name: dewar.dewar_name, dewar_name: dewar.dewar_name,
tracking_number: dewar.tracking_number, tracking_number: dewar.tracking_number,
number_of_pucks: dewar.number_of_pucks, number_of_pucks: dewar.number_of_pucks,
@ -243,7 +243,7 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
returning_date: dewar.returning_date, returning_date: dewar.returning_date,
qrcode: dewar.qrcode, qrcode: dewar.qrcode,
return_address_id: selectedReturnAddress, return_address_id: selectedReturnAddress,
contact_person_id: selectedContactPerson, contact_person_id: selectedContactPerson, // Set dewar-specific contact person
}; };
const payload = { const payload = {
@ -252,26 +252,29 @@ const DewarDetails: React.FC<DewarDetailsProps> = ({
shipment_date: existingShipment.shipment_date, shipment_date: existingShipment.shipment_date,
shipment_status: existingShipment.shipment_status, shipment_status: existingShipment.shipment_status,
comments: existingShipment.comments, comments: existingShipment.comments,
contact_person_id: selectedContactPerson, contact_person_id: existingShipment.contact_person.id, // Keep main shipment contact person
return_address_id: selectedReturnAddress, return_address_id: selectedReturnAddress,
proposal_id: existingShipment.proposal?.id, proposal_id: existingShipment.proposal?.id,
dewars: [ dewars: [updatedDewar], // Updated dewars array
updatedDewar
],
}; };
console.log('Payload for update:', JSON.stringify(payload, null, 2)); console.log('Payload for update:', JSON.stringify(payload, null, 2));
try { try {
await ShipmentsService.updateShipmentShipmentsShipmentsShipmentIdPut(shipmentId, payload); await ShipmentsService.updateShipmentShipmentsShipmentIdPut(shipmentId, payload);
setFeedbackMessage('Changes saved successfully.'); setFeedbackMessage('Changes saved successfully.');
setChangesMade(false); setChangesMade(false);
refreshShipments(); refreshShipments();
} catch (error) { } catch (error: any) {
console.error('Update Shipment Error:', error); console.error('Update Shipment Error:', error);
if (error.response && error.response.data) {
setFeedbackMessage(`Failed to save shipment. Validation errors: ${JSON.stringify(error.response.data)}`);
} else {
setFeedbackMessage('Failed to save changes. Please try again later.'); setFeedbackMessage('Failed to save changes. Please try again later.');
} }
setOpenSnackbar(true); setOpenSnackbar(true);
}
}; };
return ( return (