Spaces:
Paused
Paused
| # app/api/dispatcher_routes.py - TO'LIQ YANGILANGAN (CLINIC/DOCTOR APIlar BILAN) | |
| """ | |
| Dispatcher API endpoints | |
| AUTH BUTUNLAY O'CHIRILDI - Parolsiz kirish | |
| YANGILANISHLAR: | |
| - Clinic APIlar qo'shildi | |
| - Doctor APIlar qo'shildi | |
| - Xarita uchun endpointlar | |
| """ | |
| from fastapi import APIRouter, HTTPException, WebSocket, WebSocketDisconnect, Query | |
| from fastapi.responses import JSONResponse | |
| from typing import Optional, List, Set | |
| import logging | |
| import json | |
| from app.core.database import db | |
| from app.models.schemas import ( | |
| CaseResponse, CaseUpdate, MessageResponse, | |
| SuccessResponse, ErrorResponse, | |
| BrigadeLocation, PatientHistoryResponse, | |
| ClinicResponse, ClinicStatistics, | |
| DoctorResponse | |
| ) | |
| from app.core.connections import dispatcher_connections, active_connections | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter(prefix="/api", tags=["Dispatcher"]) | |
| # ==================== CASES APIs ==================== | |
| async def get_all_cases(status: Optional[str] = None): | |
| """ | |
| Barcha caselarni olish | |
| Query params: | |
| status: "yangi" | "qabul_qilindi" | "brigada_junatildi" | "klinika_tavsiya_qilindi" | "operator_kutilmoqda" | |
| """ | |
| try: | |
| cases = db.get_all_cases(status=status) | |
| logger.info(f"π {len(cases)} ta case qaytarildi") | |
| return cases | |
| except Exception as e: | |
| logger.error(f"β Cases olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def get_case_by_id(case_id: str): | |
| """ | |
| Bitta case ma'lumotlari | |
| Args: | |
| case_id: Case ID | |
| Returns: | |
| Case ma'lumotlari | |
| """ | |
| try: | |
| case = db.get_case(case_id) | |
| if not case: | |
| raise HTTPException(status_code=404, detail="Case topilmadi") | |
| return case | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"β Case olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def update_case_by_id(case_id: str, updates: CaseUpdate): | |
| """ | |
| Case ni yangilash | |
| Args: | |
| case_id: Case ID | |
| updates: Yangilanish ma'lumotlari | |
| Returns: | |
| Yangilangan case | |
| """ | |
| try: | |
| success = db.update_case(case_id, updates.dict(exclude_unset=True)) | |
| if not success: | |
| raise HTTPException(status_code=404, detail="Case topilmadi") | |
| # Yangilangan caseni olish | |
| updated_case = db.get_case(case_id) | |
| # WebSocket orqali xabar yuborish | |
| await notify_dispatchers({ | |
| "type": "case_updated", | |
| "case": updated_case | |
| }) | |
| return updated_case | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"β Case yangilashda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def get_case_messages(case_id: str): | |
| """ | |
| Case ning barcha xabarlarini olish | |
| Args: | |
| case_id: Case ID | |
| Returns: | |
| List of messages | |
| """ | |
| try: | |
| messages = db.get_messages(case_id) | |
| logger.info(f"π¬ {len(messages)} ta xabar qaytarildi") | |
| return messages | |
| except Exception as e: | |
| logger.error(f"β Messages olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| # ==================== PATIENT HISTORY ==================== | |
| async def get_patient_history(full_name: str): | |
| """ | |
| Bemorning oldingi murojatlari tarixini olish | |
| Args: | |
| full_name: Bemorning to'liq ismi | |
| Returns: | |
| PatientHistoryResponse | |
| """ | |
| try: | |
| history = db.get_patient_history(full_name) | |
| if not history: | |
| raise HTTPException(status_code=404, detail="Bemor topilmadi") | |
| logger.info(f"π Bemor tarixi qaytarildi: {full_name}") | |
| return history | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"β Patient history olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| # ==================== π CLINICS APIs ==================== | |
| async def get_all_clinics_endpoint( | |
| type: Optional[str] = Query(None, description="davlat | xususiy"), | |
| district: Optional[str] = Query(None, description="Tuman nomi"), | |
| specialty: Optional[str] = Query(None, description="Mutaxassislik"), | |
| min_rating: Optional[float] = Query(None, description="Minimal rating") | |
| ): | |
| """ | |
| Barcha klinikalarni olish (xarita va ro'yxat uchun) | |
| Query parametrlari: | |
| - type: "davlat" | "xususiy" (optional) | |
| - district: "Chilonzor tumani" (optional) | |
| - specialty: "Kardiologiya" (optional) | |
| - min_rating: 4.0 (optional) | |
| Returns: | |
| List[ClinicResponse] | |
| Example: | |
| GET /api/clinics?type=xususiy&district=Chilonzor tumani | |
| """ | |
| try: | |
| # Agar specialty yoki rating filtrlari bo'lsa, search ishlatamiz | |
| if specialty or min_rating: | |
| clinics = db.search_clinics( | |
| specialty=specialty, | |
| min_rating=min_rating or 0.0 | |
| ) | |
| else: | |
| # Oddiy type va district filtrlari | |
| clinics = db.get_all_clinics( | |
| clinic_type=type, | |
| district=district | |
| ) | |
| logger.info(f"π {len(clinics)} ta klinika qaytarildi") | |
| return clinics | |
| except Exception as e: | |
| logger.error(f"β Clinics olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def get_clinic_by_id_endpoint(clinic_id: str): | |
| """ | |
| Bitta klinikaning batafsil ma'lumotlari | |
| Args: | |
| clinic_id: "clinic_001" | |
| Returns: | |
| ClinicResponse (doktorlar bilan birga) | |
| """ | |
| try: | |
| clinic = db.get_clinic_by_id(clinic_id) | |
| if not clinic: | |
| raise HTTPException(status_code=404, detail="Klinika topilmadi") | |
| # Klinikadagi doktorlarni ham qo'shamiz | |
| doctors = db.get_doctors_by_clinic(clinic_id) | |
| clinic['doctors'] = doctors | |
| logger.info(f"β Klinika qaytarildi: {clinic.get('name')}") | |
| return clinic | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"β Clinic olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def get_clinic_statistics_endpoint(): | |
| """ | |
| Klinikalar statistikasi (dashboard uchun) | |
| Returns: | |
| ClinicStatistics: { | |
| "total": 15, | |
| "davlat": 8, | |
| "xususiy": 7, | |
| "by_district": {...} | |
| } | |
| """ | |
| try: | |
| stats = db.get_clinic_statistics() | |
| return stats | |
| except Exception as e: | |
| logger.error(f"β Clinic statistics xatoligi: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| # ==================== π DOCTORS APIs ==================== | |
| async def get_all_doctors_endpoint( | |
| clinic_id: Optional[str] = Query(None, description="Klinika ID"), | |
| specialty: Optional[str] = Query(None, description="Mutaxassislik") | |
| ): | |
| """ | |
| Barcha doktorlarni olish | |
| Query parametrlari: | |
| - clinic_id: "clinic_001" (optional) | |
| - specialty: "Kardiolog" (optional) | |
| Returns: | |
| List[DoctorResponse] | |
| """ | |
| try: | |
| doctors = db.get_all_doctors( | |
| clinic_id=clinic_id, | |
| specialty=specialty | |
| ) | |
| logger.info(f"π¨ββοΈ {len(doctors)} ta doktor qaytarildi") | |
| return doctors | |
| except Exception as e: | |
| logger.error(f"β Doctors olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def get_doctors_by_clinic_endpoint(clinic_id: str): | |
| """ | |
| Klinikadagi barcha doktorlar | |
| Args: | |
| clinic_id: "clinic_001" | |
| Returns: | |
| List[DoctorResponse] | |
| """ | |
| try: | |
| # Avval klinikani tekshiramiz | |
| clinic = db.get_clinic_by_id(clinic_id) | |
| if not clinic: | |
| raise HTTPException(status_code=404, detail="Klinika topilmadi") | |
| doctors = db.get_doctors_by_clinic(clinic_id) | |
| logger.info(f"π¨ββοΈ {len(doctors)} ta doktor qaytarildi ({clinic.get('name')})") | |
| return doctors | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"β Doctors olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def get_doctor_by_id_endpoint(doctor_id: str): | |
| """ | |
| Bitta doktor ma'lumotlari | |
| Args: | |
| doctor_id: "doc_001" | |
| Returns: | |
| DoctorResponse | |
| """ | |
| try: | |
| doctor = db.get_doctor_by_id(doctor_id) | |
| if not doctor: | |
| raise HTTPException(status_code=404, detail="Doktor topilmadi") | |
| logger.info(f"β Doktor qaytarildi: {doctor.get('full_name')}") | |
| return doctor | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"β Doctor olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| # ==================== BRIGADES APIs ==================== | |
| async def get_all_brigades(): | |
| """ | |
| Barcha brigadalarni olish | |
| Returns: | |
| List[BrigadeLocation] | |
| """ | |
| try: | |
| from app.services.brigade_matcher import load_brigades | |
| brigades = load_brigades() | |
| logger.info(f"π {len(brigades)} ta brigada qaytarildi") | |
| return brigades | |
| except Exception as e: | |
| logger.error(f"β Brigadalarni olishda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| # ==================== WEBSOCKET ==================== | |
| async def websocket_dispatcher(websocket: WebSocket): | |
| """ | |
| Dispatcher uchun WebSocket (Real-time yangilanishlar) | |
| """ | |
| await websocket.accept() | |
| dispatcher_connections.add(websocket) | |
| logger.info(f"π Dispatcher WebSocket ulanish o'rnatildi") | |
| try: | |
| while True: | |
| # Faqat ping-pong uchun | |
| data = await websocket.receive_text() | |
| if data == "ping": | |
| await websocket.send_text("pong") | |
| except WebSocketDisconnect: | |
| dispatcher_connections.discard(websocket) | |
| logger.info(f"π Dispatcher WebSocket uzildi") | |
| except Exception as e: | |
| logger.error(f"β Dispatcher WebSocket xatoligi: {e}") | |
| dispatcher_connections.discard(websocket) | |
| async def notify_dispatchers(message: dict): | |
| """ | |
| Barcha dispatcherlarga xabar yuborish | |
| Args: | |
| message: Yuborilishi kerak bo'lgan xabar | |
| """ | |
| if not dispatcher_connections: | |
| return | |
| disconnected = set() | |
| for connection in dispatcher_connections: | |
| try: | |
| await connection.send_json(message) | |
| logger.info(f"π€ Dispatcherga xabar yuborildi: {message.get('type')}") | |
| except Exception as e: | |
| logger.error(f"β Dispatcherga xabar yuborishda xatolik: {e}") | |
| disconnected.add(connection) | |
| # Uzilgan connectionlarni o'chirish | |
| for conn in disconnected: | |
| dispatcher_connections.discard(conn) | |
| # ==================== STATISTICS ==================== | |
| async def get_dashboard_statistics(): | |
| """ | |
| Dashboard uchun umumiy statistika | |
| Returns: | |
| { | |
| "total_cases": 10, | |
| "emergency": 3, | |
| "clinic": 5, | |
| "uncertain": 2, | |
| "total_clinics": 8, | |
| "total_doctors": 12 | |
| } | |
| """ | |
| try: | |
| cases = db.get_all_cases() | |
| clinics = db.get_all_clinics() | |
| doctors = db.get_all_doctors() | |
| stats = { | |
| "total_cases": len(cases), | |
| "emergency": len([c for c in cases if c.get('type') == 'emergency']), | |
| "clinic": len([c for c in cases if c.get('type') in ['public_clinic', 'private_clinic']]), | |
| "uncertain": len([c for c in cases if c.get('type') == 'uncertain']), | |
| "total_clinics": len(clinics), | |
| "total_doctors": len(doctors) | |
| } | |
| return stats | |
| except Exception as e: | |
| logger.error(f"β Statistics xatoligi: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| # app/api/dispatcher_routes.py - OXIRIGA QO'SHING (Brigades APIs bo'limiga) | |
| # ==================== π BRIGADES LIVE TRACKING ==================== | |
| async def get_live_brigades(): | |
| """ | |
| Brigadalarning real-time koordinatalarini olish | |
| Returns: | |
| List[Dict]: { | |
| brigade_id, name, current_lat, current_lon, | |
| target_lat, target_lon, current_status, speed_kmh | |
| } | |
| """ | |
| try: | |
| brigades = db.get_all_brigades() | |
| # Faqat kerakli ma'lumotlarni qaytarish | |
| live_data = [] | |
| for brigade in brigades: | |
| live_data.append({ | |
| "brigade_id": brigade.get("brigade_id"), | |
| "name": brigade.get("name"), | |
| "current_lat": brigade.get("current_lat"), | |
| "current_lon": brigade.get("current_lon"), | |
| "target_lat": brigade.get("target_lat"), | |
| "target_lon": brigade.get("target_lon"), | |
| "current_status": brigade.get("current_status"), | |
| "speed_kmh": brigade.get("speed_kmh", 60), | |
| "phone": brigade.get("phone"), | |
| "assigned_case_id": brigade.get("assigned_case_id") | |
| }) | |
| logger.info(f"π {len(live_data)} ta brigade koordinatasi qaytarildi") | |
| return live_data | |
| except Exception as e: | |
| logger.error(f"β Brigade live tracking xatoligi: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| async def update_brigade_status(brigade_id: str, status: str): | |
| """ | |
| Brigadaning statusini yangilash | |
| Args: | |
| brigade_id: "brigade_001" | |
| status: "available" | "busy" | "offline" | |
| """ | |
| try: | |
| success = db.update_brigade(brigade_id, { | |
| "current_status": status | |
| }) | |
| if not success: | |
| raise HTTPException(status_code=404, detail="Brigade topilmadi") | |
| return {"success": True, "message": "Status yangilandi"} | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"β Brigade status yangilashda xatolik: {e}") | |
| raise HTTPException(status_code=500, detail="Server xatoligi") | |
| # MUHIM: Bu kodni dispatcher_routes.py faylining OXIRIGA qo'shing | |
| # Mavjud @router.get("/brigades") funksiyasidan KEYIN |