| from fastapi import FastAPI, HTTPException, UploadFile, File, Query, Response |
| from fastapi.responses import JSONResponse |
| from typing import Dict, List, Optional |
| import itertools |
| import math |
|
|
| app = FastAPI( |
| title="Fake Evaluation Management API", |
| version="0.1.0", |
| openapi_url="/api/v1/openapi.json", |
| docs_url="/api/v1/docs", |
| ) |
|
|
| |
|
|
|
|
| evaluation_objects: List[Dict] = [ |
| {"id": 1, "code": "CNTDG", "name": "Ca nhan tu danh gia", "status": "active"}, |
| {"id": 2, "code": "QLTT", "name": "Quan ly truc tiep", "status": "active"}, |
| {"id": 3, "code": "DN", "name": "Doanh nghiep", "status": "inactive"}, |
| ] |
|
|
| evaluation_object_counter = itertools.count(start=len(evaluation_objects) + 1) |
|
|
| organization_tree: List[Dict] = [ |
| { |
| "id": 1, |
| "name": "Ban Giam doc", |
| "isExpanded": True, |
| "children": [ |
| { |
| "id": 2, |
| "name": "Phong Phat trien", |
| "isExpanded": True, |
| "children": [], |
| "individuals": [ |
| {"id": 101, "code": "EMP001", "name": "Tran Van Long", "selectedObjectId": 1}, |
| {"id": 102, "code": "EMP002", "name": "Le Thi Mai", "selectedObjectId": 2}, |
| ], |
| } |
| ], |
| "individuals": [], |
| } |
| ] |
|
|
| criteria_sets: List[Dict] = [ |
| { |
| "id": 1, |
| "name": "Bo tieu chi nang luc chung", |
| "applicableObjects": ["Ca nhan tu danh gia", "Quan ly truc tiep"], |
| "applicationPeriods": [{"month": 1, "year": 2024}, {"month": 6, "year": 2024}], |
| "criteriaTree": [ |
| {"id": 1, "code": "NL01", "name": "Nang luc chuyen mon", "maxScore": 50, "weight": 0.5}, |
| {"id": 2, "code": "NL02", "name": "Hieu biet san pham", "maxScore": 50, "weight": 0.5}, |
| ], |
| "classificationLevels": [ |
| {"id": 1, "name": "Xuat sac", "fromScore": 90, "toScore": 100, "color": "green"}, |
| {"id": 2, "name": "Hoan thanh tot", "fromScore": 75, "toScore": 89, "color": "blue"}, |
| ], |
| }, |
| { |
| "id": 2, |
| "name": "Bo tieu chi ABC", |
| "applicableObjects": ["Doanh nghiep"], |
| "applicationPeriods": [{"month": 7, "year": 2024}], |
| "criteriaTree": [], |
| "classificationLevels": [], |
| }, |
| ] |
|
|
| criteria_set_counter = itertools.count(start=len(criteria_sets) + 1) |
|
|
| evaluation_flows: List[Dict] = [ |
| { |
| "id": 1, |
| "code": "LDG-NVCT", |
| "name": "Luong danh gia nhan vien chinh thuc", |
| "applicableDepartments": ["Khoi Kinh doanh"], |
| "status": "active", |
| "roleHierarchy": [ |
| { |
| "id": 1, |
| "name": "Nhan vien", |
| "isExpanded": True, |
| "children": [ |
| {"id": 2, "name": "Truong phong", "isExpanded": False, "children": []}, |
| ], |
| } |
| ], |
| }, |
| { |
| "id": 2, |
| "code": "LDG-TV", |
| "name": "Luong danh gia thu viec", |
| "applicableDepartments": ["Toan cong ty"], |
| "status": "active", |
| "roleHierarchy": [], |
| }, |
| ] |
|
|
| evaluation_flow_counter = itertools.count(start=len(evaluation_flows) + 1) |
|
|
| evaluations: List[Dict] = [ |
| { |
| "id": 1, |
| "criteriaSetId": 1, |
| "fullName": "Nguyen Van A", |
| "evaluationPeriod": "Thang 6/2024", |
| "department": "Phong Phat trien", |
| "selfScore": None, |
| "managerScore": None, |
| "status": "Draft", |
| "statusColor": "gray", |
| "period": "Thang 6", |
| "year": 2024, |
| "tab": "self", |
| "scores": {}, |
| "managerScores": {}, |
| }, |
| { |
| "id": 2, |
| "criteriaSetId": 1, |
| "fullName": "Tran Thi B", |
| "evaluationPeriod": "Thang 6/2024", |
| "department": "Khoi Kinh doanh", |
| "selfScore": 90, |
| "managerScore": 92, |
| "status": "Approved", |
| "statusColor": "green", |
| "period": "Thang 6", |
| "year": 2024, |
| "tab": "manager", |
| "scores": {"2": 20}, |
| "managerScores": {"2": 21}, |
| }, |
| { |
| "id": 3, |
| "criteriaSetId": 2, |
| "fullName": "Doanh nghiep X", |
| "evaluationPeriod": "Thang 7/2024", |
| "department": "Phong Bao tri", |
| "selfScore": None, |
| "managerScore": None, |
| "status": "Draft", |
| "statusColor": "gray", |
| "period": "Thang 7", |
| "year": 2024, |
| "tab": "results", |
| "scores": {}, |
| "managerScores": {}, |
| }, |
| ] |
|
|
| evaluation_counter = itertools.count(start=len(evaluations) + 1) |
|
|
| evaluation_chats: Dict[int, List[Dict]] = { |
| 1: [ |
| { |
| "id": 1, |
| "sender": "Lieu Huu Hoang - Pho Truong phong", |
| "avatar": "person", |
| "message": "Em xem lai muc tieu nang luc chuyen mon.", |
| "timestamp": "1 ngay truoc", |
| "replyToMessageId": None, |
| } |
| ] |
| } |
|
|
| report_types: List[Dict] = [ |
| {"id": 1, "code": "RPT001", "name": "Bao cao thang", "status": "active"}, |
| {"id": 2, "code": "RPT002", "name": "Bao cao phong ban", "status": "inactive"}, |
| ] |
|
|
| report_type_counter = itertools.count(start=len(report_types) + 1) |
|
|
| reports_payload: List[Dict] = [ |
| { |
| "stt": 1, |
| "canBo": "Pham Van Tien", |
| "donVi": "Phong 5", |
| "diemCaNhan": 100, |
| "diemDonVi": 100, |
| "phanLoaiCaNhan": "Hoan thanh xuat sac nhiem vu", |
| "phanLoaiDonVi": "Hoan thanh xuat sac nhiem vu", |
| } |
| ] |
|
|
| reports_info = { |
| "title": "DANH GIA, CHAM DIEM VA PHAN LOAI", |
| "subtitle": "Ap dung doi voi can bo, chien si", |
| "date": "Thang 07 Nam 2025", |
| } |
|
|
| dashboard_stats = {"totalEvaluations": 125, "pendingEvaluations": 12} |
|
|
| managers = [ |
| {"id": 1, "name": "Lieu Huu Hoang", "title": "Pho Truong phong"}, |
| {"id": 2, "name": "Tran Minh Tuan", "title": "Truong phong"}, |
| ] |
|
|
| departments = [ |
| { |
| "id": 1, |
| "code": "CTY", |
| "name": "Toan cong ty", |
| "isExpanded": True, |
| "selection": "unchecked", |
| "children": [ |
| {"id": 2, "code": "KD", "name": "Khoi Kinh doanh", "isExpanded": False, "selection": "partial", "children": []}, |
| {"id": 3, "code": "PT", "name": "Phong Phat trien", "isExpanded": False, "selection": "checked", "children": []}, |
| ], |
| } |
| ] |
|
|
|
|
| def paginate_list(items: List[Dict], page: int, limit: int) -> Dict: |
| start = max(page - 1, 0) * limit |
| end = start + limit |
| total_items = len(items) |
| total_pages = max(1, math.ceil(total_items / limit)) if total_items else 0 |
| return { |
| "data": items[start:end], |
| "pagination": { |
| "currentPage": page, |
| "totalPages": total_pages, |
| "totalItems": total_items, |
| "itemsPerPage": limit, |
| }, |
| } |
|
|
|
|
| def find_by_id(items: List[Dict], item_id: int) -> Optional[Dict]: |
| return next((item for item in items if item.get("id") == item_id), None) |
|
|
|
|
| @app.get("/") |
| def root() -> Dict: |
| return {"message": "Fake Evaluation Management API. Explore /api/v1/docs"} |
|
|
|
|
| |
|
|
|
|
| @app.post("/api/v1/auth/login") |
| def login(credentials: Dict) -> JSONResponse: |
| username = credentials.get("username") |
| password = credentials.get("password") |
| if username == "admin" and password == "admin": |
| return JSONResponse( |
| { |
| "token": "fake-jwt-token", |
| "user": {"id": 1, "username": "admin", "fullName": "Administrator"}, |
| } |
| ) |
| return JSONResponse(status_code=401, content={"error": "Invalid username or password"}) |
|
|
|
|
| @app.post("/api/v1/auth/logout") |
| def logout() -> Response: |
| return Response(status_code=204) |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/evaluation-objects") |
| def list_evaluation_objects( |
| page: int = 1, |
| limit: int = 5, |
| search: Optional[str] = None, |
| ): |
| filtered = evaluation_objects |
| if search: |
| term = search.lower() |
| filtered = [ |
| item |
| for item in evaluation_objects |
| if term in item["code"].lower() or term in item["name"].lower() |
| ] |
| return paginate_list(filtered, page, limit) |
|
|
|
|
| @app.post("/api/v1/evaluation-objects") |
| def create_evaluation_object(payload: Dict): |
| existing_code = next((o for o in evaluation_objects if o["code"] == payload.get("code")), None) |
| if existing_code: |
| raise HTTPException(status_code=400, detail="Code must be unique") |
| new_obj = { |
| "id": next(evaluation_object_counter), |
| "code": payload.get("code"), |
| "name": payload.get("name"), |
| "status": payload.get("status", "inactive"), |
| } |
| evaluation_objects.append(new_obj) |
| return new_obj |
|
|
|
|
| @app.get("/api/v1/evaluation-objects/{item_id}") |
| def get_evaluation_object(item_id: int): |
| obj = find_by_id(evaluation_objects, item_id) |
| if not obj: |
| raise HTTPException(status_code=404, detail="Evaluation object not found") |
| return obj |
|
|
|
|
| @app.put("/api/v1/evaluation-objects/{item_id}") |
| def update_evaluation_object(item_id: int, payload: Dict): |
| obj = find_by_id(evaluation_objects, item_id) |
| if not obj: |
| raise HTTPException(status_code=404, detail="Evaluation object not found") |
| obj.update({"name": payload.get("name", obj["name"]), "status": payload.get("status", obj["status"])}) |
| return obj |
|
|
|
|
| @app.delete("/api/v1/evaluation-objects/{item_id}") |
| def delete_evaluation_object(item_id: int) -> Response: |
| obj = find_by_id(evaluation_objects, item_id) |
| if not obj: |
| raise HTTPException(status_code=404, detail="Evaluation object not found") |
| evaluation_objects.remove(obj) |
| return Response(status_code=204) |
|
|
|
|
| @app.get("/api/v1/organization-tree") |
| def get_organization_tree(): |
| return organization_tree |
|
|
|
|
| @app.put("/api/v1/organization-tree/roles") |
| def save_organization_roles(payload: List[Dict]) -> Response: |
| return Response(status_code=204) |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/criteria-sets") |
| def list_criteria_sets( |
| page: int = 1, |
| limit: int = 5, |
| search: Optional[str] = None, |
| object: Optional[str] = Query(None, alias="object"), |
| year: Optional[int] = None, |
| month: Optional[int] = None, |
| ): |
| filtered = criteria_sets |
| if search: |
| term = search.lower() |
| filtered = [c for c in filtered if term in c["name"].lower()] |
| if object: |
| term = object.lower() |
| filtered = [c for c in filtered if any(term in obj.lower() for obj in c.get("applicableObjects", []))] |
| if year: |
| filtered = [ |
| c |
| for c in filtered |
| if any(period.get("year") == year for period in c.get("applicationPeriods", [])) |
| ] |
| if month: |
| filtered = [ |
| c |
| for c in filtered |
| if any(period.get("month") == month for period in c.get("applicationPeriods", [])) |
| ] |
| return paginate_list(filtered, page, limit) |
|
|
|
|
| @app.post("/api/v1/criteria-sets") |
| def create_criteria_set(payload: Dict): |
| new_set = payload.copy() |
| new_set["id"] = next(criteria_set_counter) |
| new_set.setdefault("criteriaTree", []) |
| new_set.setdefault("classificationLevels", []) |
| new_set.setdefault("applicationPeriods", []) |
| new_set.setdefault("applicableObjects", []) |
| criteria_sets.append(new_set) |
| return new_set |
|
|
|
|
| @app.get("/api/v1/criteria-sets/{item_id}") |
| def get_criteria_set(item_id: int): |
| result = find_by_id(criteria_sets, item_id) |
| if not result: |
| raise HTTPException(status_code=404, detail="Criteria set not found") |
| return result |
|
|
|
|
| @app.put("/api/v1/criteria-sets/{item_id}") |
| def update_criteria_set(item_id: int, payload: Dict): |
| result = find_by_id(criteria_sets, item_id) |
| if not result: |
| raise HTTPException(status_code=404, detail="Criteria set not found") |
| result.update(payload) |
| return result |
|
|
|
|
| @app.delete("/api/v1/criteria-sets") |
| def delete_criteria_sets(payload: Dict) -> Response: |
| ids = payload.get("ids", []) |
| for item_id in ids: |
| existing = find_by_id(criteria_sets, item_id) |
| if existing: |
| criteria_sets.remove(existing) |
| return Response(status_code=204) |
|
|
|
|
| @app.get("/api/v1/criteria-sets/export") |
| def export_criteria_sets(format: str = Query(..., pattern="^(pdf|xlsx)$")): |
| return {"message": f"Generated {format} export for criteria sets"} |
|
|
|
|
| @app.get("/api/v1/criteria-sets/template") |
| def download_criteria_template(): |
| return {"template": "criteria-template.xlsx", "message": "Template generated"} |
|
|
|
|
| @app.post("/api/v1/criteria-sets/import") |
| def import_criteria_set(file: UploadFile = File(...)): |
| return {"message": "Import successful", "filename": file.filename} |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/evaluation-flows") |
| def list_evaluation_flows( |
| page: int = 1, |
| limit: int = 5, |
| search: Optional[str] = None, |
| ): |
| filtered = evaluation_flows |
| if search: |
| term = search.lower() |
| filtered = [ |
| f |
| for f in evaluation_flows |
| if term in f["code"].lower() or term in f["name"].lower() or any(term in dep.lower() for dep in f.get("applicableDepartments", [])) |
| ] |
| return paginate_list(filtered, page, limit) |
|
|
|
|
| @app.post("/api/v1/evaluation-flows") |
| def create_evaluation_flow(payload: Dict): |
| existing_code = next((f for f in evaluation_flows if f["code"] == payload.get("code")), None) |
| if existing_code: |
| raise HTTPException(status_code=400, detail="Code must be unique") |
| new_flow = { |
| "id": next(evaluation_flow_counter), |
| "code": payload.get("code"), |
| "name": payload.get("name"), |
| "applicableDepartments": payload.get("applicableDepartments", []), |
| "status": payload.get("status", "inactive"), |
| "roleHierarchy": payload.get("roleHierarchy", []), |
| } |
| evaluation_flows.append(new_flow) |
| return new_flow |
|
|
|
|
| @app.get("/api/v1/evaluation-flows/{item_id}") |
| def get_evaluation_flow(item_id: int): |
| flow = find_by_id(evaluation_flows, item_id) |
| if not flow: |
| raise HTTPException(status_code=404, detail="Evaluation flow not found") |
| return flow |
|
|
|
|
| @app.put("/api/v1/evaluation-flows/{item_id}") |
| def update_evaluation_flow(item_id: int, payload: Dict): |
| flow = find_by_id(evaluation_flows, item_id) |
| if not flow: |
| raise HTTPException(status_code=404, detail="Evaluation flow not found") |
| if "code" in payload and payload["code"] != flow["code"]: |
| raise HTTPException(status_code=400, detail="Code is immutable") |
| flow.update({ |
| "name": payload.get("name", flow["name"]), |
| "applicableDepartments": payload.get("applicableDepartments", flow.get("applicableDepartments", [])), |
| "status": payload.get("status", flow["status"]), |
| "roleHierarchy": payload.get("roleHierarchy", flow.get("roleHierarchy", [])), |
| }) |
| return flow |
|
|
|
|
| @app.delete("/api/v1/evaluation-flows") |
| def delete_evaluation_flows(payload: Dict) -> Response: |
| ids = payload.get("ids", []) |
| for item_id in ids: |
| existing = find_by_id(evaluation_flows, item_id) |
| if existing: |
| evaluation_flows.remove(existing) |
| return Response(status_code=204) |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/evaluations") |
| def list_evaluations( |
| tab: str = Query(..., description="self | manager | results"), |
| page: int = 1, |
| limit: int = 5, |
| search: Optional[str] = None, |
| status: Optional[str] = None, |
| period: Optional[str] = None, |
| year: Optional[int] = None, |
| ): |
| filtered = [ev for ev in evaluations if ev.get("tab") == tab] |
| if search: |
| term = search.lower() |
| filtered = [ev for ev in filtered if term in ev["fullName"].lower() or term in ev["department"].lower()] |
| if status: |
| filtered = [ev for ev in filtered if ev.get("status", "").lower() == status.lower()] |
| if period: |
| filtered = [ev for ev in filtered if ev.get("period") == period] |
| if year: |
| filtered = [ev for ev in filtered if ev.get("year") == year] |
| return paginate_list(filtered, page, limit) |
|
|
|
|
| @app.post("/api/v1/evaluations") |
| def create_evaluation(payload: Dict): |
| criteria_set_id = payload.get("criteriaSetId") |
| period = payload.get("period") |
| year = payload.get("year") |
| new_eval = { |
| "id": next(evaluation_counter), |
| "criteriaSetId": criteria_set_id, |
| "fullName": payload.get("fullName", "Nguoi dung moi"), |
| "evaluationPeriod": f"{period}/{year}", |
| "department": payload.get("department", "Phong moi"), |
| "selfScore": None, |
| "managerScore": None, |
| "status": "Draft", |
| "statusColor": "gray", |
| "period": period, |
| "year": year, |
| "tab": payload.get("tab", "self"), |
| "scores": payload.get("scores", {}), |
| "managerScores": payload.get("managerScores", {}), |
| } |
| evaluations.append(new_eval) |
| return new_eval |
|
|
|
|
| @app.get("/api/v1/evaluations/{item_id}") |
| def get_evaluation(item_id: int): |
| evaluation = find_by_id(evaluations, item_id) |
| if not evaluation: |
| raise HTTPException(status_code=404, detail="Evaluation not found") |
| return evaluation |
|
|
|
|
| @app.put("/api/v1/evaluations/{item_id}") |
| def update_evaluation(item_id: int, payload: Dict): |
| evaluation = find_by_id(evaluations, item_id) |
| if not evaluation: |
| raise HTTPException(status_code=404, detail="Evaluation not found") |
| evaluation.update({ |
| "evaluationPeriod": payload.get("evaluationPeriod", evaluation["evaluationPeriod"]), |
| "scores": payload.get("scores", evaluation.get("scores", {})), |
| "managerScores": payload.get("managerScores", evaluation.get("managerScores", {})), |
| "status": payload.get("status", evaluation["status"]), |
| }) |
| return evaluation |
|
|
|
|
| @app.delete("/api/v1/evaluations") |
| def delete_evaluations(payload: Dict) -> Response: |
| ids = payload.get("ids", []) |
| for item_id in ids: |
| existing = find_by_id(evaluations, item_id) |
| if existing: |
| evaluations.remove(existing) |
| return Response(status_code=204) |
|
|
|
|
| @app.post("/api/v1/evaluations/{item_id}/submit") |
| def submit_evaluation(item_id: int, payload: Dict): |
| evaluation = find_by_id(evaluations, item_id) |
| if not evaluation: |
| raise HTTPException(status_code=404, detail="Evaluation not found") |
| evaluation["status"] = "Submitted" |
| evaluation["managerId"] = payload.get("managerId") |
| evaluation["comments"] = payload.get("comments") |
| return evaluation |
|
|
|
|
| @app.post("/api/v1/evaluations/{item_id}/withdraw") |
| def withdraw_evaluation(item_id: int): |
| evaluation = find_by_id(evaluations, item_id) |
| if not evaluation: |
| raise HTTPException(status_code=404, detail="Evaluation not found") |
| evaluation["status"] = "Draft" |
| return evaluation |
|
|
|
|
| @app.post("/api/v1/evaluations/{item_id}/return") |
| def return_evaluation(item_id: int): |
| evaluation = find_by_id(evaluations, item_id) |
| if not evaluation: |
| raise HTTPException(status_code=404, detail="Evaluation not found") |
| evaluation["status"] = "Draft" |
| return evaluation |
|
|
|
|
| @app.get("/api/v1/evaluations/{item_id}/chat") |
| def list_evaluation_chat(item_id: int): |
| return evaluation_chats.get(item_id, []) |
|
|
|
|
| @app.post("/api/v1/evaluations/{item_id}/chat") |
| def post_evaluation_chat(item_id: int, payload: Dict): |
| chat_thread = evaluation_chats.setdefault(item_id, []) |
| new_message = { |
| "id": len(chat_thread) + 1, |
| "sender": payload.get("sender", "Nguoi dung"), |
| "avatar": payload.get("avatar", "person"), |
| "message": payload.get("message"), |
| "timestamp": payload.get("timestamp", "vua xong"), |
| "replyToMessageId": payload.get("replyToMessageId"), |
| "attachments": payload.get("attachments", []), |
| } |
| chat_thread.append(new_message) |
| return new_message |
|
|
|
|
| @app.get("/api/v1/evaluations/export") |
| def export_evaluations(format: str = Query(..., pattern="^(pdf|xlsx)$")): |
| return {"message": f"Generated {format} export for evaluations"} |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/reports") |
| def get_reports( |
| reportType: str = Query(..., alias="reportType"), |
| period: int = Query(..., ge=1, le=12), |
| year: int = Query(..., ge=2000), |
| search: Optional[str] = None, |
| ): |
| filtered = reports_payload |
| if search: |
| term = search.lower() |
| filtered = [row for row in reports_payload if term in row["canBo"].lower() or term in row["donVi"].lower()] |
| return {"data": filtered, "reportInfo": reports_info} |
|
|
|
|
| @app.get("/api/v1/reports/export") |
| def export_reports( |
| reportType: str = Query(..., alias="reportType"), |
| period: int = Query(..., ge=1, le=12), |
| year: int = Query(..., ge=2000), |
| format: str = Query(..., pattern="^(pdf|xlsx|docx)$"), |
| ): |
| return {"message": f"Generated {format} export for {reportType} {period}/{year}"} |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/report-types") |
| def list_report_types(page: int = 1, limit: int = 5, search: Optional[str] = None): |
| filtered = report_types |
| if search: |
| term = search.lower() |
| filtered = [r for r in report_types if term in r["code"].lower() or term in r["name"].lower()] |
| return paginate_list(filtered, page, limit) |
|
|
|
|
| @app.post("/api/v1/report-types") |
| def create_report_type(payload: Dict): |
| new_type = { |
| "id": next(report_type_counter), |
| "code": payload.get("code"), |
| "name": payload.get("name"), |
| "status": payload.get("status", "inactive"), |
| } |
| report_types.append(new_type) |
| return new_type |
|
|
|
|
| @app.put("/api/v1/report-types/{item_id}") |
| def update_report_type(item_id: int, payload: Dict): |
| report_type = find_by_id(report_types, item_id) |
| if not report_type: |
| raise HTTPException(status_code=404, detail="Report type not found") |
| report_type.update({ |
| "name": payload.get("name", report_type["name"]), |
| "status": payload.get("status", report_type["status"]), |
| }) |
| return report_type |
|
|
|
|
| @app.delete("/api/v1/report-types/{item_id}") |
| def delete_report_type(item_id: int) -> Response: |
| report_type = find_by_id(report_types, item_id) |
| if not report_type: |
| raise HTTPException(status_code=404, detail="Report type not found") |
| report_types.remove(report_type) |
| return Response(status_code=204) |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/dashboard/stats") |
| def get_dashboard_stats(): |
| return dashboard_stats |
|
|
|
|
| @app.get("/api/v1/dashboard/charts") |
| def get_dashboard_charts(year: int = Query(..., ge=2000), criteria: str = Query(...)): |
| monthly_breakdown = [{"month": month, "score": 80 + (month % 5)} for month in range(1, 13)] |
| pie_data = [ |
| {"label": "Xuat sac", "value": 40}, |
| {"label": "Tot", "value": 35}, |
| {"label": "Trung binh", "value": 20}, |
| {"label": "Can cai thien", "value": 5}, |
| ] |
| return {"monthlyBreakdown": monthly_breakdown, "pieData": pie_data} |
|
|
|
|
| @app.get("/api/v1/dashboard/filters") |
| def get_dashboard_filters(): |
| return {"years": [2024, 2023], "criteria": ["Bo tieu chi nang luc chung", "Bo tieu chi ABC"]} |
|
|
|
|
| |
|
|
|
|
| @app.get("/api/v1/users/managers") |
| def get_managers(): |
| return managers |
|
|
|
|
| @app.get("/api/v1/departments") |
| def get_departments(): |
| return departments |
|
|
|
|
| @app.get("/api/v1/dashboard") |
| def dashboard_root(): |
| return {"message": "Dashboard data ready"} |
|
|