""" =============================================================================== FILE: modules/web_api/routers/remote_workers.py ROLE: REST API реестра удаленщиков (CRUD, редактирование сроков, автоочистка). =============================================================================== """ import os from datetime import datetime import pandas as pd from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from typing import Optional, List, Dict, Any from routers.auth import get_current_user from config import BASE_DIR, DATA_DIR router = APIRouter(prefix="/api/v1/remote-workers", tags=["RemoteWorkers"]) CSV_PATH = os.path.join(DATA_DIR, "static_reason_workers.csv") def _load_workers() -> List[Dict[str, Any]]: """Читает CSV, гарантирует структуру колонок и удаляет просроченные записи.""" if not os.path.exists(CSV_PATH): return [] try: df = pd.read_csv(CSV_PATH, dtype=str, on_bad_lines='skip').fillna("") for col in ["fio", "department", "reason", "date_from", "date_to"]: if col not in df.columns: df[col] = "" today_date = datetime.now().date() valid_workers = [] has_expired = False for row in df.to_dict(orient="records"): fio = str(row.get("fio", "")).strip() if not fio: continue d_to_str = str(row.get("date_to", "")).strip() if d_to_str and d_to_str.lower() not in ["nan", "none", ""]: try: d_to = datetime.strptime(d_to_str.replace('_', '.'), "%d.%m.%Y").date() # Если срок завершился вчера или ранее — запись удаляется из файла if today_date > d_to: has_expired = True continue except ValueError: pass valid_workers.append(row) # Синхронная перезапись файла при обнаружении истекших сроков if has_expired: _save_workers(valid_workers) return valid_workers except Exception: return [] def _save_workers(workers: List[Dict[str, Any]]): """Сохраняет актуальный список в CSV с сохранением колонок.""" df = pd.DataFrame(workers) for col in ["fio", "department", "reason", "date_from", "date_to"]: if col not in df.columns: df[col] = "" os.makedirs(os.path.dirname(CSV_PATH), exist_ok=True) df.to_csv(CSV_PATH, index=False, encoding="utf-8") class RemoteWorkerItem(BaseModel): fio: str department: Optional[str] = "Все" reason: Optional[str] = "Удаленная работа" date_from: Optional[str] = "" date_to: Optional[str] = "" class UpdateDatesRequest(BaseModel): fio: str date_from: Optional[str] = "" date_to: Optional[str] = "" @router.get("") def api_get_remote_workers(current_user = Depends(get_current_user)): return {"workers": _load_workers()} @router.post("") def api_add_remote_worker(item: RemoteWorkerItem, current_user = Depends(get_current_user)): workers = _load_workers() fio_clean = item.fio.strip() if not fio_clean: raise HTTPException(status_code=400, detail="ФИО не может быть пустым") # Если начало не указано — берем сегодня today_str = datetime.now().strftime("%d.%m.%Y") date_from = item.date_from.strip() if item.date_from and item.date_from.strip() else today_str date_to = item.date_to.strip() if item.date_to else "" # Проверка на совпадение ФИО (обновление существующей записи) for w in workers: if str(w.get("fio", "")).strip().lower() == fio_clean.lower(): w["department"] = item.department.strip() if item.department else (w.get("department") or "Все") w["reason"] = item.reason.strip() if item.reason else (w.get("reason") or "Удаленная работа") w["date_from"] = date_from w["date_to"] = date_to _save_workers(workers) return {"status": "success", "message": "Срок удаленки обновлен", "workers": workers} workers.append({ "fio": fio_clean, "department": item.department.strip() if item.department else "Все", "reason": item.reason.strip() if item.reason else "Удаленная работа", "date_from": date_from, "date_to": date_to }) _save_workers(workers) return {"status": "success", "workers": workers} @router.put("") def api_update_worker_dates(req: UpdateDatesRequest, current_user = Depends(get_current_user)): """Редактирование срока удаленки (продление или сокращение).""" workers = _load_workers() target_fio = req.fio.strip().lower() found = False for w in workers: if str(w.get("fio", "")).strip().lower() == target_fio: w["date_from"] = req.date_from.strip() if req.date_from is not None else w.get("date_from", "") w["date_to"] = req.date_to.strip() if req.date_to is not None else w.get("date_to", "") found = True break if not found: raise HTTPException(status_code=404, detail="Сотрудник не найден в списке") _save_workers(workers) return {"status": "success", "message": "Сроки успешно изменены", "workers": workers} @router.delete("") def api_delete_remote_worker(fio: str, current_user = Depends(get_current_user)): workers = _load_workers() fio_clean = fio.strip().lower() initial_len = len(workers) workers = [w for w in workers if str(w.get("fio", "")).strip().lower() != fio_clean] if len(workers) == initial_len: raise HTTPException(status_code=404, detail="Сотрудник не найден") _save_workers(workers) return {"status": "success", "workers": workers}