diff --git a/app/api/routers/misc.py b/app/api/routers/misc.py index bb203dd..aed7579 100644 --- a/app/api/routers/misc.py +++ b/app/api/routers/misc.py @@ -486,3 +486,270 @@ def dashboard_stats(project_id: int = None, db: Session = Depends(get_db)): by_priority = {p: query.filter(models.Issue.priority == p).count() for p in ["low", "medium", "high", "critical"]} return {"total": total, "by_status": by_status, "by_type": by_type, "by_priority": by_priority} + +# ============ Tasks ============ + +@router.get("/tasks/{project_code}/{milestone_id}", tags=["Tasks"]) +def list_tasks(project_code: str, milestone_id: int, db: Session = Depends(get_db)): + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + issues = db.query(models.Issue).filter( + models.Issue.project_id == project.id, + models.Issue.milestone_id == milestone_id, + models.Issue.issue_type == "task" + ).all() + + return [{ + "id": i.id, + "title": i.title, + "description": i.description, + "status": i.status.value if hasattr(i.status, 'value') else i.status, + "priority": i.priority.value if hasattr(i.priority, 'value') else i.priority, + "task_code": i.task_code, + "task_status": i.task_status, + "estimated_effort": i.estimated_effort, + "estimated_working_time": str(i.estimated_working_time) if i.estimated_working_time else None, + "started_on": i.started_on, + "finished_on": i.finished_on, + "depend_on": i.depend_on, + "related_tasks": i.related_tasks, + "assignee_id": i.assignee_id, + "created_at": i.created_at, + } for i in issues] + + +@router.post("/tasks/{project_code}/{milestone_id}", status_code=status.HTTP_201_CREATED, tags=["Tasks"]) +def create_task(project_code: str, milestone_id: int, task_data: dict, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey)): + from datetime import datetime, time + + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + ms = db.query(MilestoneModel).filter(MilestoneModel.id == milestone_id).first() + if not ms: + raise HTTPException(status_code=404, detail="Milestone not found") + + if ms.status and hasattr(ms.status, 'value') and ms.status.value == "progressing": + raise HTTPException(status_code=400, detail="Cannot add items to a milestone that is in_progress") + + max_issue = db.query(models.Issue).filter(models.Issue.project_id == project.id).order_by(models.Issue.id.desc()).first() + next_id = (max_issue.id + 1) if max_issue else 1 + task_code = f"i_{project_code}_{next_id:06x}" + + est_time = None + if task_data.get("estimated_working_time"): + try: + est_time = datetime.strptime(task_data["estimated_working_time"], "%H:%M").time() + except: + pass + + issue = models.Issue( + title=task_data.get("title"), + description=task_data.get("description"), + issue_type="task", + status=models.IssueStatus.OPEN, + priority=models.IssuePriority.MEDIUM, + project_id=project.id, + milestone_id=milestone_id, + reporter_id=current_user.id, + task_code=task_code, + estimated_effort=task_data.get("estimated_effort"), + estimated_working_time=est_time, + task_status="open", + created_by_id=current_user.id, + ) + db.add(issue) + db.commit() + db.refresh(issue) + + return { + "id": issue.id, + "title": issue.title, + "description": issue.description, + "task_code": issue.task_code, + "status": issue.status.value if hasattr(issue.status, 'value') else issue.status, + "priority": issue.priority.value if hasattr(issue.priority, 'value') else issue.priority, + "created_at": issue.created_at, + } + + +@router.get("/tasks/{project_code}/{milestone_id}/{task_id}", tags=["Tasks"]) +def get_task(project_code: str, milestone_id: int, task_id: int, db: Session = Depends(get_db)): + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + issue = db.query(models.Issue).filter( + models.Issue.id == task_id, + models.Issue.project_id == project.id, + models.Issue.milestone_id == milestone_id, + models.Issue.issue_type == "task" + ).first() + if not issue: + raise HTTPException(status_code=404, detail="Task not found") + + return { + "id": issue.id, + "title": issue.title, + "description": issue.description, + "status": issue.status.value if hasattr(issue.status, 'value') else issue.status, + "priority": issue.priority.value if hasattr(issue.priority, 'value') else issue.priority, + "task_code": issue.task_code, + "task_status": issue.task_status, + "estimated_effort": issue.estimated_effort, + "estimated_working_time": str(issue.estimated_working_time) if issue.estimated_working_time else None, + "started_on": issue.started_on, + "finished_on": issue.finished_on, + "depend_on": issue.depend_on, + "related_tasks": issue.related_tasks, + "assignee_id": issue.assignee_id, + "created_at": issue.created_at, + } + + +@router.patch("/tasks/{project_code}/{milestone_id}/{task_id}", tags=["Tasks"]) +def update_task(project_code: str, milestone_id: int, task_id: int, task_data: dict, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey)): + from datetime import datetime + + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + issue = db.query(models.Issue).filter( + models.Issue.id == task_id, + models.Issue.project_id == project.id, + models.Issue.milestone_id == milestone_id, + models.Issue.issue_type == "task" + ).first() + if not issue: + raise HTTPException(status_code=404, detail="Task not found") + + if "title" in task_data: + issue.title = task_data["title"] + if "description" in task_data: + issue.description = task_data["description"] + if "task_status" in task_data: + issue.task_status = task_data["task_status"] + if task_data["task_status"] == "progressing" and not issue.started_on: + issue.started_on = datetime.now() + if task_data["task_status"] == "closed" and not issue.finished_on: + issue.finished_on = datetime.now() + if "estimated_effort" in task_data: + issue.estimated_effort = task_data["estimated_effort"] + if "assignee_id" in task_data: + issue.assignee_id = task_data["assignee_id"] + if "status" in task_data: + issue.status = models.IssueStatus[task_data["status"].upper()] if task_data["status"].upper() in [s.name for s in models.IssueStatus] else models.IssueStatus.OPEN + + db.commit() + db.refresh(issue) + + return issue + +# ============ Supports ============ + +@router.get("/supports/{project_code}/{milestone_id}", tags=["Supports"]) +def list_supports(project_code: str, milestone_id: int, db: Session = Depends(get_db)): + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + issues = db.query(models.Issue).filter( + models.Issue.project_id == project.id, + models.Issue.milestone_id == milestone_id, + models.Issue.issue_type == "support" + ).all() + + return [{ + "id": i.id, + "title": i.title, + "description": i.description, + "status": i.status.value if hasattr(i.status, 'value') else i.status, + "priority": i.priority.value if hasattr(i.priority, 'value') else i.priority, + "created_at": i.created_at, + } for i in issues] + + +@router.post("/supports/{project_code}/{milestone_id}", status_code=status.HTTP_201_CREATED, tags=["Supports"]) +def create_support(project_code: str, milestone_id: int, support_data: dict, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey)): + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + ms = db.query(MilestoneModel).filter(MilestoneModel.id == milestone_id).first() + if not ms: + raise HTTPException(status_code=404, detail="Milestone not found") + + if ms.status and hasattr(ms.status, 'value') and ms.status.value == "progressing": + raise HTTPException(status_code=400, detail="Cannot add items to a milestone that is in_progress") + + issue = models.Issue( + title=support_data.get("title"), + description=support_data.get("description"), + issue_type="support", + status=models.IssueStatus.OPEN, + priority=models.IssuePriority.MEDIUM, + project_id=project.id, + milestone_id=milestone_id, + reporter_id=current_user.id, + ) + db.add(issue) + db.commit() + db.refresh(issue) + return issue + + +# ============ Meetings ============ + +@router.get("/meetings/{project_code}/{milestone_id}", tags=["Meetings"]) +def list_meetings(project_code: str, milestone_id: int, db: Session = Depends(get_db)): + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + issues = db.query(models.Issue).filter( + models.Issue.project_id == project.id, + models.Issue.milestone_id == milestone_id, + models.Issue.issue_type == "meeting" + ).all() + + return [{ + "id": i.id, + "title": i.title, + "description": i.description, + "status": i.status.value if hasattr(i.status, 'value') else i.status, + "priority": i.priority.value if hasattr(i.priority, 'value') else i.priority, + "created_at": i.created_at, + } for i in issues] + + +@router.post("/meetings/{project_code}/{milestone_id}", status_code=status.HTTP_201_CREATED, tags=["Meetings"]) +def create_meeting(project_code: str, milestone_id: int, meeting_data: dict, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user_or_apikey)): + project = db.query(models.Project).filter(models.Project.project_code == project_code).first() + if not project: + raise HTTPException(status_code=404, detail="Project not found") + + ms = db.query(MilestoneModel).filter(MilestoneModel.id == milestone_id).first() + if not ms: + raise HTTPException(status_code=404, detail="Milestone not found") + + if ms.status and hasattr(ms.status, 'value') and ms.status.value == "progressing": + raise HTTPException(status_code=400, detail="Cannot add items to a milestone that is in_progress") + + issue = models.Issue( + title=meeting_data.get("title"), + description=meeting_data.get("description"), + issue_type="meeting", + status=models.IssueStatus.OPEN, + priority=models.IssuePriority.MEDIUM, + project_id=project.id, + milestone_id=milestone_id, + reporter_id=current_user.id, + ) + db.add(issue) + db.commit() + db.refresh(issue) + return issue