chore(pm): vendor the Aramesh/PM platform under pm/ (squash of Aramesh/PM @ 91f3271)
The PM panel (PmPanelPage -> iframe /pm/) needs the project-management platform served at /pm/ on the ERP domain. Source: org repo Aramesh/PM, branch feature/project-management-platform. Update via: git subtree pull --prefix=pm aramesh-pm feature/project-management-platform --squash
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
"""Response serializers — the {"data": ...} envelope lives in the routers."""
|
||||
from datetime import date, datetime
|
||||
|
||||
from app.utils.jalali import to_jalali_str
|
||||
|
||||
|
||||
def _iso(value: datetime | date | None) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value.isoformat()
|
||||
return value.isoformat()
|
||||
|
||||
|
||||
def user_payload(ctx) -> dict:
|
||||
"""The authenticated user as the frontend sees them: identity, role and the
|
||||
permissions/scope the UI derives its navigation from (see frontend
|
||||
src/lib/roles.ts). The API enforces the same values server-side."""
|
||||
user = ctx.user
|
||||
return {
|
||||
"id": str(user.id),
|
||||
"username": user.username,
|
||||
"display_name": user.display_name,
|
||||
**ctx.as_dict(),
|
||||
}
|
||||
|
||||
|
||||
def project_dict(project, member_count: int = 0, task_stats: dict | None = None) -> dict:
|
||||
return {
|
||||
"id": str(project.id),
|
||||
"name": project.name,
|
||||
"description": project.description or "",
|
||||
"location": project.location or "",
|
||||
"project_manager": project.project_manager or "",
|
||||
"planned_start": _iso(project.planned_start),
|
||||
"planned_end": _iso(project.planned_end),
|
||||
"actual_start": _iso(project.actual_start),
|
||||
"actual_end": _iso(project.actual_end),
|
||||
"planned_start_jalali": to_jalali_str(project.planned_start),
|
||||
"planned_end_jalali": to_jalali_str(project.planned_end),
|
||||
"actual_start_jalali": to_jalali_str(project.actual_start),
|
||||
"actual_end_jalali": to_jalali_str(project.actual_end),
|
||||
"status": project.status,
|
||||
"progress": round(project.progress or 0.0, 1),
|
||||
"member_count": member_count,
|
||||
"stats": task_stats or {"total": 0, "completed": 0, "in_progress": 0, "blocked": 0, "overdue": 0},
|
||||
"created_at": _iso(project.created_at),
|
||||
"updated_at": _iso(project.updated_at),
|
||||
}
|
||||
|
||||
|
||||
def project_member_dict(member) -> dict:
|
||||
username = ""
|
||||
display_name = ""
|
||||
try:
|
||||
from sqlalchemy.inspection import inspect
|
||||
insp = inspect(member)
|
||||
if "user" in insp.attrs and insp.attrs.user.loaded and member.user:
|
||||
username = member.user.username
|
||||
display_name = member.user.display_name
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
"id": str(member.id),
|
||||
"project_id": str(member.project_id),
|
||||
"user_id": str(member.user_id),
|
||||
"username": username,
|
||||
"display_name": display_name,
|
||||
"role": member.role,
|
||||
"created_at": _iso(member.created_at),
|
||||
}
|
||||
|
||||
|
||||
def native_task_dict(task, project_name: str | None = None, children_count: int = 0) -> dict:
|
||||
assignee_name = task.assignee_name
|
||||
if not assignee_name:
|
||||
try:
|
||||
from sqlalchemy.inspection import inspect
|
||||
insp = inspect(task)
|
||||
if "assignee" in insp.attrs and "assignee" not in insp.unloaded and task.assignee:
|
||||
assignee_name = task.assignee.display_name
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
progress_logs_data = []
|
||||
try:
|
||||
from sqlalchemy.inspection import inspect
|
||||
insp = inspect(task)
|
||||
if "progress_logs" in insp.attrs and "progress_logs" not in insp.unloaded and task.progress_logs:
|
||||
progress_logs_data = [
|
||||
{
|
||||
"id": str(log.id),
|
||||
"progress_before": log.progress_before,
|
||||
"progress_after": log.progress_after,
|
||||
"human_resources_used": log.human_resources_used or [],
|
||||
"materials_used": log.materials_used or [],
|
||||
"description": log.description or "",
|
||||
"logged_by": log.logged_by or "",
|
||||
"created_at": _iso(log.created_at),
|
||||
}
|
||||
for log in task.progress_logs
|
||||
]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
"id": str(task.id),
|
||||
"project_id": str(task.project_id),
|
||||
"parent_id": str(task.parent_id) if task.parent_id else None,
|
||||
"title": task.title,
|
||||
"description": task.description or "",
|
||||
"status": task.status,
|
||||
"priority": task.priority,
|
||||
"assignee_id": str(task.assignee_id) if task.assignee_id else None,
|
||||
"assignee_name": assignee_name,
|
||||
"planned_start": _iso(task.planned_start),
|
||||
"planned_end": _iso(task.planned_end),
|
||||
"actual_start": _iso(task.actual_start),
|
||||
"actual_end": _iso(task.actual_end),
|
||||
"planned_start_jalali": to_jalali_str(task.planned_start),
|
||||
"planned_end_jalali": to_jalali_str(task.planned_end),
|
||||
"actual_start_jalali": to_jalali_str(task.actual_start),
|
||||
"actual_end_jalali": to_jalali_str(task.actual_end),
|
||||
"duration": task.duration or 1,
|
||||
"progress": task.progress or 0,
|
||||
"qc_status": task.qc_status or "none",
|
||||
"project_name": project_name,
|
||||
"human_resources": getattr(task, "human_resources", None) or [],
|
||||
"materials": getattr(task, "materials", None) or [],
|
||||
"progress_logs": progress_logs_data,
|
||||
"created_at": _iso(task.created_at),
|
||||
"updated_at": _iso(task.updated_at),
|
||||
}
|
||||
|
||||
|
||||
def task_relation_dict(rel, from_title: str = "", to_title: str = "") -> dict:
|
||||
from_title_val = from_title
|
||||
to_title_val = to_title
|
||||
try:
|
||||
from sqlalchemy.inspection import inspect
|
||||
insp = inspect(rel)
|
||||
if not from_title_val and "from_task" in insp.attrs and insp.attrs.from_task.loaded and rel.from_task:
|
||||
from_title_val = rel.from_task.title
|
||||
if not to_title_val and "to_task" in insp.attrs and insp.attrs.to_task.loaded and rel.to_task:
|
||||
to_title_val = rel.to_task.title
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
"id": str(rel.id),
|
||||
"project_id": str(rel.project_id),
|
||||
"from_task_id": str(rel.from_task_id),
|
||||
"to_task_id": str(rel.to_task_id),
|
||||
"from_task_title": from_title_val,
|
||||
"to_task_title": to_title_val,
|
||||
"relation_type": rel.relation_type,
|
||||
"lag_days": rel.lag_days,
|
||||
"created_at": _iso(rel.created_at),
|
||||
}
|
||||
Reference in New Issue
Block a user