fakeData / app.py
huy00001's picture
Update app.py
985a0d6 verified
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",
)
# In-memory seed data
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"}
# Authentication
@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)
# Evaluation Objects
@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)
# Criteria Sets
@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}
# Evaluation Flows
@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)
# Evaluations
@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"}
# Reports
@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}"}
# Report Types
@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)
# Dashboard
@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"]}
# Users and Departments
@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"}