/
NickLasher
/
AutomatedTempScanner
Обзор
Документация
Войти
/
NickLasher
/
AutomatedTempScanner
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
main.py
503 строки
17 KB
NickLasher
upload files
01 фев 2026, 22:40
Верифицирован
01 фев 2026, 22:40
0732dad
Код
Авторство
О чём код?
from fastapi import FastAPI, Query, HTTPException, Depends, status from fastapi.middleware.cors import CORSMiddleware from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import pyodbc import uvicorn from datetime import datetime, timedelta from typing import List, Dict, Any, Optional from pydantic import BaseModel import jwt app = FastAPI(title="Temperature Monitoring System") # CORS app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) security = HTTPBearer() SECRET_KEY = "ppis" ALGORITHM = "HS256" # Database connection class DB: def __init__(self): self.server = "DESKTOP-3B548LN" self.database = "temper_db" self.conn_str = f""" Driver={{SQL Server}}; Server={self.server}; Database={self.database}; Trusted_Connection=yes; """ self.conn = pyodbc.connect(self.conn_str) self.cursor = self.conn.cursor() db = DB() # Pydantic models class TokenRequest(BaseModel): username: str password: str class DeviceCreate(BaseModel): name: str location: str device_type: str min_temp: Optional[float] = None max_temp: Optional[float] = None class DeviceResponse(BaseModel): id: int name: str location: str device_type: str min_temp: Optional[float] max_temp: Optional[float] status: str created_at: datetime class TemperatureMeasure(BaseModel): device_id: int temperature: float humidity: Optional[float] = None timestamp: Optional[datetime] = None class TemperatureResponse(BaseModel): id: int device_id: int temperature: float humidity: Optional[float] timestamp: datetime is_anomaly: bool class NotificationRequest(BaseModel): user_id: int message: str notification_type: str = "email" # email, telegram, sms class NotificationResponse(BaseModel): id: int user_id: int message: str notification_type: str status: str created_at: datetime class AnalysisResult(BaseModel): device_id: int avg_temperature: float min_temperature: float max_temperature: float anomaly_count: int analysis_time: datetime class UserCreate(BaseModel): username: str password: str email: Optional[str] = None telegram_id: Optional[str] = None class UserResponse(BaseModel): id: int username: str email: Optional[str] telegram_id: Optional[str] created_at: datetime # Utility functions def create_access_token(data: dict, expires_delta: timedelta = None): to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=30) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def verify_password(plain_password: str, hashed_password: str) -> bool: return plain_password == hashed_password def get_password_hash(password: str) -> str: return password def check_temperature_anomaly(temp: float, min_temp: float, max_temp: float) -> bool: """Check if temperature is outside normal range""" if min_temp is not None and temp < min_temp: return True if max_temp is not None and temp > max_temp: return True return False # Authentication dependency async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)): token = credentials.credentials try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise HTTPException(status_code=401, detail="Invalid token") except jwt.PyJWTError: raise HTTPException(status_code=401, detail="Invalid token") db.cursor.execute("SELECT id, username, email FROM users WHERE username = ?", username) user = db.cursor.fetchone() if user is None: raise HTTPException(status_code=401, detail="User not found") return {"id": user[0], "username": user[1], "email": user[2]} # ==================== AUTH ENDPOINTS ==================== @app.post("/api/token") async def login(request: TokenRequest): """Authenticate user and return JWT token""" db.cursor.execute( "SELECT id, username, password FROM users WHERE username = ?", request.username ) user = db.cursor.fetchone() if not user or not verify_password(request.password, user[2]): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials", headers={"WWW-Authenticate": "Bearer"}, ) access_token = create_access_token(data={"sub": user[1]}) return {"access_token": access_token, "token_type": "bearer"} @app.post("/api/users/", response_model=UserResponse) async def create_user(user: UserCreate): """Create a new user""" db.cursor.execute( "SELECT id FROM users WHERE username = ?", user.username ) if db.cursor.fetchone(): raise HTTPException(status_code=400, detail="Username already registered") hashed_password = get_password_hash(user.password) db.cursor.execute( """INSERT INTO users (username, password, email, telegram_id, created_at) VALUES (?, ?, ?, ?, ?)""", user.username, hashed_password, user.email, user.telegram_id, datetime.now() ) db.conn.commit() db.cursor.execute("SELECT SCOPE_IDENTITY()") user_id = db.cursor.fetchone()[0] return UserResponse( id=user_id, username=user.username, email=user.email, telegram_id=user.telegram_id, created_at=datetime.now() ) # ==================== DEVICE ENDPOINTS ==================== @app.post("/api/devices/", response_model=DeviceResponse) async def create_device(device: DeviceCreate, current_user: dict = Depends(get_current_user)): """Register a new device""" db.cursor.execute( """INSERT INTO devices (name, location, device_type, min_temp, max_temp, status, created_at) VALUES (?, ?, ?, ?, ?, 'active', ?)""", device.name, device.location, device.device_type, device.min_temp, device.max_temp, datetime.now() ) db.conn.commit() db.cursor.execute("SELECT SCOPE_IDENTITY()") device_id = db.cursor.fetchone()[0] return DeviceResponse( id=device_id, name=device.name, location=device.location, device_type=device.device_type, min_temp=device.min_temp, max_temp=device.max_temp, status="active", created_at=datetime.now() ) @app.get("/api/devices/") async def get_devices(current_user: dict = Depends(get_current_user)): """Get all devices""" db.cursor.execute( "SELECT id, name, location, device_type, min_temp, max_temp, status, created_at FROM devices" ) columns = [column[0] for column in db.cursor.description] results = [] for row in db.cursor.fetchall(): results.append(dict(zip(columns, row))) return results @app.get("/api/devices/{device_id}") async def get_device(device_id: int, current_user: dict = Depends(get_current_user)): """Get a specific device""" db.cursor.execute( """SELECT id, name, location, device_type, min_temp, max_temp, status, created_at FROM devices WHERE id = ?""", device_id ) row = db.cursor.fetchone() if not row: raise HTTPException(status_code=404, detail="Device not found") columns = [column[0] for column in db.cursor.description] return dict(zip(columns, row)) @app.put("/api/devices/{device_id}") async def update_device(device_id: int, device: DeviceCreate, current_user: dict = Depends(get_current_user)): """Update a device""" db.cursor.execute( """UPDATE devices SET name = ?, location = ?, device_type = ?, min_temp = ?, max_temp = ? WHERE id = ?""", device.name, device.location, device.device_type, device.min_temp, device.max_temp, device_id ) db.conn.commit() if db.cursor.rowcount == 0: raise HTTPException(status_code=404, detail="Device not found") return {"message": "Device updated successfully"} @app.delete("/api/devices/{device_id}") async def delete_device(device_id: int, current_user: dict = Depends(get_current_user)): """Delete a device""" db.cursor.execute("DELETE FROM devices WHERE id = ?", device_id) db.conn.commit() if db.cursor.rowcount == 0: raise HTTPException(status_code=404, detail="Device not found") return {"message": "Device deleted successfully"} # ==================== TEMPERATURE ENDPOINTS ==================== @app.post("/api/temperature/measure", response_model=TemperatureResponse) async def measure_temperature(data: TemperatureMeasure, current_user: dict = Depends(get_current_user)): """Record temperature measurement""" # Get device info db.cursor.execute( "SELECT min_temp, max_temp FROM devices WHERE id = ?", data.device_id ) device = db.cursor.fetchone() if not device: raise HTTPException(status_code=404, detail="Device not found") min_temp, max_temp = device[0], device[1] is_anomaly = check_temperature_anomaly(data.temperature, min_temp, max_temp) timestamp = data.timestamp or datetime.now() db.cursor.execute( """INSERT INTO temperature_readings (device_id, temperature, humidity, timestamp, is_anomaly) VALUES (?, ?, ?, ?, ?)""", data.device_id, data.temperature, data.humidity, timestamp, is_anomaly ) db.conn.commit() db.cursor.execute("SELECT SCOPE_IDENTITY()") reading_id = db.cursor.fetchone()[0] # Create notification if anomaly detected if is_anomaly: db.cursor.execute( """INSERT INTO notifications (user_id, message, notification_type, status, created_at) VALUES (?, ?, 'warning', 'pending', ?)""", current_user['id'], f"Temperature anomaly detected! Device {data.device_id}: {data.temperature}°C", datetime.now() ) db.conn.commit() return TemperatureResponse( id=reading_id, device_id=data.device_id, temperature=data.temperature, humidity=data.humidity, timestamp=timestamp, is_anomaly=is_anomaly ) @app.get("/api/temperature/{device_id}") async def get_temperature_history( device_id: int, start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, limit: int = Query(100, ge=1, le=1000), current_user: dict = Depends(get_current_user) ): """Get temperature history for a device""" query = """SELECT id, device_id, temperature, humidity, timestamp, is_anomaly FROM temperature_readings WHERE device_id = ?""" params = [device_id] if start_date: query += " AND timestamp >= ?" params.append(start_date) if end_date: query += " AND timestamp <= ?" params.append(end_date) query += " ORDER BY timestamp DESC OFFSET 0 ROWS FETCH NEXT ? ROWS ONLY" params.append(limit) db.cursor.execute(query, params) columns = [column[0] for column in db.cursor.description] results = [] for row in db.cursor.fetchall(): results.append(dict(zip(columns, row))) return results @app.get("/api/temperature/{device_id}/analysis") async def analyze_temperature( device_id: int, period_hours: int = Query(24, ge=1, le=168), current_user: dict = Depends(get_current_user) ): """Analyze temperature data for a device""" from_time = datetime.now() - timedelta(hours=period_hours) db.cursor.execute( """SELECT AVG(temperature) as avg_temperature, MIN(temperature) as min_temperature, MAX(temperature) as max_temperature, COUNT(CASE WHEN is_anomaly = 1 THEN 1 END) as anomaly_count FROM temperature_readings WHERE device_id = ? AND timestamp >= ?""", device_id, from_time ) row = db.cursor.fetchone() if not row or row[0] is None: raise HTTPException(status_code=404, detail="No data found for analysis") return AnalysisResult( device_id=device_id, avg_temperature=round(row[0], 2), min_temperature=row[1], max_temperature=row[2], anomaly_count=row[3] or 0, analysis_time=datetime.now() ) # ==================== NOTIFICATION ENDPOINTS ==================== @app.post("/api/notifications/", response_model=NotificationResponse) async def create_notification(notification: NotificationRequest): """Create a notification""" db.cursor.execute( """INSERT INTO notifications (user_id, message, notification_type, status, created_at) VALUES (?, ?, ?, 'pending', ?)""", notification.user_id, notification.message, notification.notification_type, datetime.now() ) db.conn.commit() db.cursor.execute("SELECT SCOPE_IDENTITY()") notification_id = db.cursor.fetchone()[0] return NotificationResponse( id=notification_id, user_id=notification.user_id, message=notification.message, notification_type=notification.notification_type, status="pending", created_at=datetime.now() ) @app.get("/api/notifications/") async def get_notifications( user_id: Optional[int] = None, status: Optional[str] = None, limit: int = Query(50, ge=1, le=100), current_user: dict = Depends(get_current_user) ): """Get notifications""" query = """SELECT id, user_id, message, notification_type, status, created_at FROM notifications WHERE 1=1""" params = [] if user_id: query += " AND user_id = ?" params.append(user_id) if status: query += " AND status = ?" params.append(status) query += f" ORDER BY created_at DESC OFFSET 0 ROWS FETCH NEXT {limit} ROWS ONLY" db.cursor.execute(query, params) columns = [column[0] for column in db.cursor.description] results = [] for row in db.cursor.fetchall(): results.append(dict(zip(columns, row))) return results @app.put("/api/notifications/{notification_id}") async def update_notification_status( notification_id: int, new_status: str, current_user: dict = Depends(get_current_user) ): """Update notification status""" db.cursor.execute( "UPDATE notifications SET status = ? WHERE id = ?", new_status, notification_id ) db.conn.commit() if db.cursor.rowcount == 0: raise HTTPException(status_code=404, detail="Notification not found") return {"message": "Notification status updated"} @app.post("/api/notifications/{notification_id}/send") async def send_notification(notification_id: int, current_user: dict = Depends(get_current_user)): """Send a notification (email, telegram, etc.)""" db.cursor.execute( """SELECT n.message, n.notification_type, u.email, u.telegram_id FROM notifications n JOIN users u ON n.user_id = u.id WHERE n.id = ?""", notification_id ) row = db.cursor.fetchone() if not row: raise HTTPException(status_code=404, detail="Notification not found") message, notif_type, email, telegram_id = row # In a real implementation, you would send the notification here # For now, just update the status sent_successfully = True # Simulated if sent_successfully: db.cursor.execute( "UPDATE notifications SET status = 'sent' WHERE id = ?", notification_id ) db.conn.commit() return {"message": f"Notification sent via {notif_type}"} else: db.cursor.execute( "UPDATE notifications SET status = 'failed' WHERE id = ?", notification_id ) db.conn.commit() raise HTTPException(status_code=500, detail="Failed to send notification") @app.get("/") async def root(): return {"message": "Temperature Monitoring System API работает"} if __name__ == "__main__": uvicorn.run(app, host="127.0.0.1", port=8000)