v0.4.0 - merge from claude/dev-015sto5mf2MpPCE57TbNKtaF #1

Merged
serversdown merged 32 commits from claude/dev-015sto5mf2MpPCE57TbNKtaF into main 2026-01-02 16:10:54 -05:00
5 changed files with 168 additions and 0 deletions
Showing only changes of commit f976e4e893 - Show all commits

22
database.py Normal file
View 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
View 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
View 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
View 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
View 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