""" Admin export endpoints for inventory snapshot and audit trail exports. Supports CSV and Excel formats. """ from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, Query from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session from backend.database import get_db from backend.auth import get_current_admin from backend.models import Item, AuditLog from backend.services.export_service import ( InventorySnapshotExporter, AuditTrailExporter, get_export_filename, ) router = APIRouter(prefix="/admin", tags=["admin-exports"]) def validate_export_format(format_str: str) -> str: """Validate and normalize export format.""" format_lower = format_str.lower() if format_str else "csv" if format_lower not in ("csv", "xlsx"): raise HTTPException( status_code=400, detail="Invalid format. Use 'csv' or 'xlsx'." ) return format_lower @router.post("/inventory-snapshot") async def export_inventory_snapshot( format: str = Query("csv", description="Export format: csv or xlsx"), db: Session = Depends(get_db), admin_user=Depends(get_current_admin), ): """ Export inventory snapshot in CSV or Excel format. Requires admin authorization. Returns file download with timestamp in filename. """ format_type = validate_export_format(format) timestamp = datetime.now().strftime("%Y-%m-%d") # Fetch all items items = db.query(Item).all() # Generate export if format_type == "csv": content = InventorySnapshotExporter.to_csv(items, timestamp) media_type = "text/csv; charset=utf-8" else: content = InventorySnapshotExporter.to_excel(items, timestamp) media_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" filename = get_export_filename("inventory_snapshot", format_type, timestamp) # Log export action from backend.models import AuditLog audit_entry = AuditLog( user_id=admin_user.sub, action="EXPORT_INVENTORY_SNAPSHOT", details=f"Exported in {format_type} format", ) db.add(audit_entry) db.commit() # Return file response if format_type == "csv": return StreamingResponse( io.BytesIO(content.encode("utf-8")), media_type=media_type, headers={"Content-Disposition": f"attachment; filename={filename}"} ) else: return StreamingResponse( io.BytesIO(content), media_type=media_type, headers={"Content-Disposition": f"attachment; filename={filename}"} ) @router.post("/audit-trail") async def export_audit_trail( format: str = Query("csv", description="Export format: csv or xlsx"), db: Session = Depends(get_db), admin_user=Depends(get_current_admin), ): """ Export audit trail in CSV or Excel format. Requires admin authorization. Returns file download with timestamp in filename. """ format_type = validate_export_format(format) timestamp = datetime.now().strftime("%Y-%m-%d") # Fetch all audit logs logs = db.query(AuditLog).all() # Generate export if format_type == "csv": content = AuditTrailExporter.to_csv(logs, timestamp) media_type = "text/csv; charset=utf-8" else: content = AuditTrailExporter.to_excel(logs, timestamp) media_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" filename = get_export_filename("audit_trail", format_type, timestamp) # Log export action audit_entry = AuditLog( user_id=admin_user.sub, action="EXPORT_AUDIT_TRAIL", details=f"Exported in {format_type} format", ) db.add(audit_entry) db.commit() # Return file response if format_type == "csv": return StreamingResponse( io.BytesIO(content.encode("utf-8")), media_type=media_type, headers={"Content-Disposition": f"attachment; filename={filename}"} ) else: return StreamingResponse( io.BytesIO(content), media_type=media_type, headers={"Content-Disposition": f"attachment; filename={filename}"} ) @router.get("/reports/export") async def export_db( format: str = Query("csv", description="Export format: csv or xlsx"), type: str = Query("inventory", description="Export type: inventory, audit, or combined"), db: Session = Depends(get_db), admin_user=Depends(get_current_admin), ): """ Combined export endpoint for frontend. Supports inventory snapshot and audit trail exports. Parameters: - format: 'csv' or 'xlsx' - type: 'inventory', 'audit', or 'combined' Requires admin authorization. """ import io format_type = validate_export_format(format) timestamp = datetime.now().strftime("%Y-%m-%d") if type not in ("inventory", "audit", "combined"): raise HTTPException( status_code=400, detail="Invalid type. Use 'inventory', 'audit', or 'combined'." ) # Determine what to export export_inventory = type in ("inventory", "combined") export_audit = type in ("audit", "combined") # For combined or single exports if export_inventory and not export_audit: # Inventory only items = db.query(Item).all() if format_type == "csv": content = InventorySnapshotExporter.to_csv(items, timestamp) media_type = "text/csv; charset=utf-8" else: content = InventorySnapshotExporter.to_excel(items, timestamp) media_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" filename = get_export_filename("inventory_snapshot", format_type, timestamp) elif export_audit and not export_inventory: # Audit only logs = db.query(AuditLog).all() if format_type == "csv": content = AuditTrailExporter.to_csv(logs, timestamp) media_type = "text/csv; charset=utf-8" else: content = AuditTrailExporter.to_excel(logs, timestamp) media_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" filename = get_export_filename("audit_trail", format_type, timestamp) else: # Combined - inventory + audit trail items = db.query(Item).all() logs = db.query(AuditLog).all() if format_type == "csv": inv_content = InventorySnapshotExporter.to_csv(items, timestamp) audit_content = AuditTrailExporter.to_csv(logs, timestamp) # Combine with separator content = f"{inv_content}\n\n--- AUDIT TRAIL ---\n\n{audit_content}" media_type = "text/csv; charset=utf-8" else: # For Excel, we'd need to create multi-sheet workbook (openpyxl) # For now, just export inventory content = InventorySnapshotExporter.to_excel(items, timestamp) media_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" filename = get_export_filename("inventory_and_audit", format_type, timestamp) # Log export action audit_entry = AuditLog( user_id=admin_user.sub, action="EXPORT_DB", details=f"Exported {type} in {format_type} format", ) db.add(audit_entry) db.commit() # Return file response if format_type == "csv": return StreamingResponse( io.BytesIO(content.encode("utf-8")), media_type=media_type, headers={"Content-Disposition": f"attachment; filename={filename}"} ) else: return StreamingResponse( io.BytesIO(content), media_type=media_type, headers={"Content-Disposition": f"attachment; filename={filename}"} )