feat: add boards and tasks management endpoints
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
from app.models.activity import Activity
|
||||
from app.models.org import Department, Employee, Team
|
||||
from app.models.projects import Project, ProjectMember
|
||||
from app.models.work import Task, TaskComment
|
||||
from app.models.activity_events import ActivityEvent
|
||||
from app.models.agents import Agent
|
||||
from app.models.boards import Board
|
||||
from app.models.tasks import Task
|
||||
from app.models.users import User
|
||||
|
||||
__all__ = [
|
||||
"Department",
|
||||
"Employee",
|
||||
"Team",
|
||||
"Project",
|
||||
"ProjectMember",
|
||||
"ActivityEvent",
|
||||
"Agent",
|
||||
"Board",
|
||||
"Task",
|
||||
"TaskComment",
|
||||
"Activity",
|
||||
"User",
|
||||
]
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class Activity(SQLModel, table=True):
|
||||
__tablename__ = "activities"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
actor_employee_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
|
||||
entity_type: str
|
||||
entity_id: int | None = None
|
||||
verb: str
|
||||
|
||||
payload_json: str | None = None
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
17
backend/app/models/activity_events.py
Normal file
17
backend/app/models/activity_events.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class ActivityEvent(SQLModel, table=True):
|
||||
__tablename__ = "activity_events"
|
||||
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
event_type: str = Field(index=True)
|
||||
message: str | None = None
|
||||
agent_id: UUID | None = Field(default=None, foreign_key="agents.id", index=True)
|
||||
task_id: UUID | None = Field(default=None, foreign_key="tasks.id", index=True)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
18
backend/app/models/agents.py
Normal file
18
backend/app/models/agents.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class Agent(SQLModel, table=True):
|
||||
__tablename__ = "agents"
|
||||
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
name: str = Field(index=True)
|
||||
status: str = Field(default="online", index=True)
|
||||
openclaw_session_id: str | None = Field(default=None, index=True)
|
||||
last_seen_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
18
backend/app/models/boards.py
Normal file
18
backend/app/models/boards.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlmodel import Field
|
||||
|
||||
from app.models.tenancy import TenantScoped
|
||||
|
||||
|
||||
class Board(TenantScoped, table=True):
|
||||
__tablename__ = "boards"
|
||||
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
name: str
|
||||
slug: str = Field(index=True)
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
@@ -1,40 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class Department(SQLModel, table=True):
|
||||
__tablename__ = "departments"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str = Field(index=True, unique=True)
|
||||
head_employee_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
|
||||
|
||||
class Team(SQLModel, table=True):
|
||||
__tablename__ = "teams"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str = Field(index=True)
|
||||
|
||||
department_id: int = Field(foreign_key="departments.id")
|
||||
lead_employee_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
|
||||
|
||||
class Employee(SQLModel, table=True):
|
||||
__tablename__ = "employees"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str
|
||||
employee_type: str # human | agent
|
||||
|
||||
department_id: int | None = Field(default=None, foreign_key="departments.id")
|
||||
team_id: int | None = Field(default=None, foreign_key="teams.id")
|
||||
manager_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
|
||||
title: str | None = None
|
||||
status: str = Field(default="active")
|
||||
|
||||
# OpenClaw integration
|
||||
openclaw_session_key: str | None = None
|
||||
notify_enabled: bool = Field(default=True)
|
||||
@@ -1,23 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class Project(SQLModel, table=True):
|
||||
__tablename__ = "projects"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
name: str = Field(index=True, unique=True)
|
||||
status: str = Field(default="active")
|
||||
|
||||
# Project ownership: projects are assigned to teams (not departments)
|
||||
team_id: int | None = Field(default=None, foreign_key="teams.id")
|
||||
|
||||
|
||||
class ProjectMember(SQLModel, table=True):
|
||||
__tablename__ = "project_members"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
project_id: int = Field(foreign_key="projects.id")
|
||||
employee_id: int = Field(foreign_key="employees.id")
|
||||
role: str | None = None
|
||||
26
backend/app/models/tasks.py
Normal file
26
backend/app/models/tasks.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlmodel import Field
|
||||
|
||||
from app.models.tenancy import TenantScoped
|
||||
|
||||
|
||||
class Task(TenantScoped, table=True):
|
||||
__tablename__ = "tasks"
|
||||
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
board_id: UUID | None = Field(default=None, foreign_key="boards.id", index=True)
|
||||
|
||||
title: str
|
||||
description: str | None = None
|
||||
status: str = Field(default="inbox", index=True)
|
||||
priority: str = Field(default="medium", index=True)
|
||||
due_at: datetime | None = None
|
||||
|
||||
created_by_user_id: UUID | None = Field(default=None, foreign_key="users.id", index=True)
|
||||
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
7
backend/app/models/tenancy.py
Normal file
7
backend/app/models/tenancy.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlmodel import SQLModel
|
||||
|
||||
|
||||
class TenantScoped(SQLModel, table=False):
|
||||
pass
|
||||
15
backend/app/models/users.py
Normal file
15
backend/app/models/users.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class User(SQLModel, table=True):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True)
|
||||
clerk_user_id: str = Field(index=True, unique=True)
|
||||
email: str | None = Field(default=None, index=True)
|
||||
name: str | None = None
|
||||
is_super_admin: bool = Field(default=False)
|
||||
@@ -1,38 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class Task(SQLModel, table=True):
|
||||
__tablename__ = "tasks"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
|
||||
project_id: int = Field(foreign_key="projects.id", index=True)
|
||||
title: str
|
||||
description: str | None = None
|
||||
|
||||
status: str = Field(default="backlog", index=True)
|
||||
|
||||
assignee_employee_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
reviewer_employee_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
created_by_employee_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
updated_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
|
||||
|
||||
class TaskComment(SQLModel, table=True):
|
||||
__tablename__ = "task_comments"
|
||||
|
||||
id: int | None = Field(default=None, primary_key=True)
|
||||
task_id: int = Field(foreign_key="tasks.id", index=True)
|
||||
author_employee_id: int | None = Field(default=None, foreign_key="employees.id")
|
||||
|
||||
# Optional reply threading
|
||||
reply_to_comment_id: int | None = Field(default=None, foreign_key="task_comments.id")
|
||||
|
||||
body: str
|
||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||
Reference in New Issue
Block a user