v0.4.0 - merge from claude/dev-015sto5mf2MpPCE57TbNKtaF #1
22
database.py
Normal file
22
database.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
SQLALCHEMY_DATABASE_URL = "sqlite:///./seismo_fleet.db"
|
||||||
|
|
||||||
|
engine = create_engine(
|
||||||
|
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
||||||
|
)
|
||||||
|
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
"""Dependency for database sessions"""
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
41
main.py
Normal file
41
main.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
from database import engine, Base
|
||||||
|
from routes import router
|
||||||
|
|
||||||
|
# Create database tables
|
||||||
|
Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
# Initialize FastAPI app
|
||||||
|
app = FastAPI(
|
||||||
|
title="Seismo Fleet Manager",
|
||||||
|
description="Backend API for managing seismograph fleet status",
|
||||||
|
version="0.1.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure CORS (adjust origins as needed for your deployment)
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"], # In production, specify exact origins
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include API routes
|
||||||
|
app.include_router(router)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
def root():
|
||||||
|
"""Root endpoint - health check"""
|
||||||
|
return {
|
||||||
|
"message": "Seismo Fleet Manager API v0.1",
|
||||||
|
"status": "running"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
18
models.py
Normal file
18
models.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
from sqlalchemy import Column, String, DateTime
|
||||||
|
from datetime import datetime
|
||||||
|
from database import Base
|
||||||
|
|
||||||
|
|
||||||
|
class Emitter(Base):
|
||||||
|
"""Emitter model representing a seismograph unit in the fleet"""
|
||||||
|
__tablename__ = "emitters"
|
||||||
|
|
||||||
|
id = Column(String, primary_key=True, index=True)
|
||||||
|
unit_type = Column(String, nullable=False)
|
||||||
|
last_seen = Column(DateTime, default=datetime.utcnow)
|
||||||
|
last_file = Column(String, nullable=False)
|
||||||
|
status = Column(String, nullable=False) # OK, Pending, Missing
|
||||||
|
notes = Column(String, nullable=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Emitter(id={self.id}, type={self.unit_type}, status={self.status})>"
|
||||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
fastapi==0.104.1
|
||||||
|
uvicorn[standard]==0.24.0
|
||||||
|
sqlalchemy==2.0.23
|
||||||
|
pydantic==2.5.0
|
||||||
|
python-multipart==0.0.6
|
||||||
82
routes.py
Normal file
82
routes.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Optional, List
|
||||||
|
|
||||||
|
from database import get_db
|
||||||
|
from models import Emitter
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
# Pydantic schemas for request/response validation
|
||||||
|
class EmitterReport(BaseModel):
|
||||||
|
unit: str
|
||||||
|
unit_type: str
|
||||||
|
timestamp: str
|
||||||
|
file: str
|
||||||
|
status: str
|
||||||
|
|
||||||
|
|
||||||
|
class EmitterResponse(BaseModel):
|
||||||
|
id: str
|
||||||
|
unit_type: str
|
||||||
|
last_seen: datetime
|
||||||
|
last_file: str
|
||||||
|
status: str
|
||||||
|
notes: Optional[str] = None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/emitters/report", status_code=200)
|
||||||
|
def report_emitter(report: EmitterReport, db: Session = Depends(get_db)):
|
||||||
|
"""
|
||||||
|
Endpoint for emitters to report their status.
|
||||||
|
Creates a new emitter if it doesn't exist, or updates an existing one.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Parse the timestamp
|
||||||
|
timestamp = datetime.fromisoformat(report.timestamp.replace('Z', '+00:00'))
|
||||||
|
except ValueError:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid timestamp format")
|
||||||
|
|
||||||
|
# Check if emitter already exists
|
||||||
|
emitter = db.query(Emitter).filter(Emitter.id == report.unit).first()
|
||||||
|
|
||||||
|
if emitter:
|
||||||
|
# Update existing emitter
|
||||||
|
emitter.unit_type = report.unit_type
|
||||||
|
emitter.last_seen = timestamp
|
||||||
|
emitter.last_file = report.file
|
||||||
|
emitter.status = report.status
|
||||||
|
else:
|
||||||
|
# Create new emitter
|
||||||
|
emitter = Emitter(
|
||||||
|
id=report.unit,
|
||||||
|
unit_type=report.unit_type,
|
||||||
|
last_seen=timestamp,
|
||||||
|
last_file=report.file,
|
||||||
|
status=report.status
|
||||||
|
)
|
||||||
|
db.add(emitter)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
db.refresh(emitter)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"message": "Emitter report received",
|
||||||
|
"unit": emitter.id,
|
||||||
|
"status": emitter.status
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/fleet/status", response_model=List[EmitterResponse])
|
||||||
|
def get_fleet_status(db: Session = Depends(get_db)):
|
||||||
|
"""
|
||||||
|
Returns a list of all emitters and their current status.
|
||||||
|
"""
|
||||||
|
emitters = db.query(Emitter).all()
|
||||||
|
return emitters
|
||||||
Reference in New Issue
Block a user