1+ """Database models for praisonai-platform."""
2+
3+ import uuid
4+ from datetime import datetime , timezone
5+ from typing import Any , Dict , List , Optional
6+
7+ from sqlalchemy import (
8+ DateTime ,
9+ ForeignKey ,
10+ Integer ,
11+ JSON ,
12+ String ,
13+ Text ,
14+ UniqueConstraint ,
15+ )
16+ from sqlalchemy .orm import Mapped , mapped_column , relationship
17+
18+ from .base import Base
19+
20+
21+ def _uuid () -> str :
22+ """Generate a new UUID string."""
23+ return str (uuid .uuid4 ())
24+
25+
26+ def _utcnow () -> datetime :
27+ """Get current UTC datetime."""
28+ return datetime .now (timezone .utc )
29+
30+
31+ class User (Base ):
32+ """User model for authentication."""
33+ __tablename__ = "users"
34+
35+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
36+ name : Mapped [str ] = mapped_column (String , unique = True , nullable = False )
37+ email : Mapped [str ] = mapped_column (String , unique = True , nullable = False )
38+ password_hash : Mapped [str ] = mapped_column (String , nullable = False )
39+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
40+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
41+
42+ # Relationships
43+ memberships : Mapped [List ["Member" ]] = relationship ("Member" , back_populates = "user" )
44+
45+
46+ class Workspace (Base ):
47+ """Workspace model for organizing work."""
48+ __tablename__ = "workspaces"
49+
50+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
51+ name : Mapped [str ] = mapped_column (String , nullable = False )
52+ description : Mapped [Optional [str ]] = mapped_column (Text , nullable = True )
53+ settings : Mapped [Optional [Dict [str , Any ]]] = mapped_column (JSON , nullable = True , default = dict )
54+ issue_prefix : Mapped [str ] = mapped_column (String , default = "ISSUE" )
55+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
56+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
57+
58+ # Relationships
59+ members : Mapped [List ["Member" ]] = relationship ("Member" , back_populates = "workspace" )
60+ projects : Mapped [List ["Project" ]] = relationship ("Project" , back_populates = "workspace" )
61+ issues : Mapped [List ["Issue" ]] = relationship ("Issue" , back_populates = "workspace" )
62+ agents : Mapped [List ["Agent" ]] = relationship ("Agent" , back_populates = "workspace" )
63+
64+
65+ class Member (Base ):
66+ """Workspace membership model."""
67+ __tablename__ = "members"
68+
69+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
70+ user_id : Mapped [str ] = mapped_column (String , ForeignKey ("users.id" ), nullable = False )
71+ workspace_id : Mapped [str ] = mapped_column (String , ForeignKey ("workspaces.id" ), nullable = False )
72+ role : Mapped [str ] = mapped_column (String , nullable = False , default = "member" ) # owner, admin, member
73+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
74+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
75+
76+ # Relationships
77+ user : Mapped ["User" ] = relationship ("User" , back_populates = "memberships" )
78+ workspace : Mapped ["Workspace" ] = relationship ("Workspace" , back_populates = "members" )
79+
80+ __table_args__ = (
81+ UniqueConstraint ("user_id" , "workspace_id" , name = "uq_member_user_workspace" ),
82+ )
83+
84+
85+ class Project (Base ):
86+ """Project model for organizing issues."""
87+ __tablename__ = "projects"
88+
89+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
90+ workspace_id : Mapped [str ] = mapped_column (String , ForeignKey ("workspaces.id" ), nullable = False )
91+ name : Mapped [str ] = mapped_column (String , nullable = False )
92+ description : Mapped [Optional [str ]] = mapped_column (Text , nullable = True )
93+ settings : Mapped [Optional [Dict [str , Any ]]] = mapped_column (JSON , nullable = True , default = dict )
94+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
95+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
96+
97+ # Relationships
98+ workspace : Mapped ["Workspace" ] = relationship ("Workspace" , back_populates = "projects" )
99+ issues : Mapped [List ["Issue" ]] = relationship ("Issue" , back_populates = "project" )
100+
101+
102+ class Issue (Base ):
103+ """Issue model for tracking work items."""
104+ __tablename__ = "issues"
105+
106+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
107+ workspace_id : Mapped [str ] = mapped_column (String , ForeignKey ("workspaces.id" ), nullable = False )
108+ project_id : Mapped [Optional [str ]] = mapped_column (String , ForeignKey ("projects.id" ), nullable = True )
109+ title : Mapped [str ] = mapped_column (String , nullable = False )
110+ description : Mapped [Optional [str ]] = mapped_column (Text , nullable = True )
111+ status : Mapped [str ] = mapped_column (String , nullable = False , default = "backlog" )
112+ priority : Mapped [str ] = mapped_column (String , nullable = False , default = "medium" )
113+ assignee_id : Mapped [Optional [str ]] = mapped_column (String , ForeignKey ("users.id" ), nullable = True )
114+ created_by_id : Mapped [Optional [str ]] = mapped_column (String , ForeignKey ("users.id" ), nullable = True )
115+ issue_number : Mapped [Optional [int ]] = mapped_column (Integer , nullable = True )
116+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
117+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
118+
119+ # Relationships
120+ workspace : Mapped ["Workspace" ] = relationship ("Workspace" , back_populates = "issues" )
121+ project : Mapped [Optional ["Project" ]] = relationship ("Project" , back_populates = "issues" )
122+ comments : Mapped [List ["Comment" ]] = relationship ("Comment" , back_populates = "issue" )
123+ labels : Mapped [List ["IssueLabelLink" ]] = relationship ("IssueLabelLink" , back_populates = "issue" )
124+
125+
126+ class Comment (Base ):
127+ """Comment model for issue discussions."""
128+ __tablename__ = "comments"
129+
130+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
131+ issue_id : Mapped [str ] = mapped_column (String , ForeignKey ("issues.id" ), nullable = False )
132+ author_id : Mapped [Optional [str ]] = mapped_column (String , ForeignKey ("users.id" ), nullable = True )
133+ content : Mapped [str ] = mapped_column (Text , nullable = False )
134+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
135+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
136+
137+ # Relationships
138+ issue : Mapped ["Issue" ] = relationship ("Issue" , back_populates = "comments" )
139+
140+
141+ class Agent (Base ):
142+ """Agent model for AI agents in workspace."""
143+ __tablename__ = "agents"
144+
145+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
146+ workspace_id : Mapped [str ] = mapped_column (String , ForeignKey ("workspaces.id" ), nullable = False )
147+ name : Mapped [str ] = mapped_column (String , nullable = False )
148+ description : Mapped [Optional [str ]] = mapped_column (Text , nullable = True )
149+ status : Mapped [str ] = mapped_column (String , nullable = False , default = "idle" )
150+ config : Mapped [Optional [Dict [str , Any ]]] = mapped_column (JSON , nullable = True , default = dict )
151+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
152+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
153+
154+ # Relationships
155+ workspace : Mapped ["Workspace" ] = relationship ("Workspace" , back_populates = "agents" )
156+
157+
158+ class IssueLabel (Base ):
159+ """Label definitions for issues."""
160+ __tablename__ = "issue_labels"
161+
162+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
163+ workspace_id : Mapped [str ] = mapped_column (String , ForeignKey ("workspaces.id" ), nullable = False )
164+ name : Mapped [str ] = mapped_column (String , nullable = False )
165+ color : Mapped [str ] = mapped_column (String , nullable = False , default = "#000000" )
166+ description : Mapped [Optional [str ]] = mapped_column (Text , nullable = True )
167+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
168+ updated_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow , onupdate = _utcnow )
169+
170+ # Relationships
171+ issues : Mapped [List ["IssueLabelLink" ]] = relationship ("IssueLabelLink" , back_populates = "label" )
172+
173+
174+ class IssueLabelLink (Base ):
175+ """Many-to-many relationship between issues and labels."""
176+ __tablename__ = "issue_label_links"
177+
178+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
179+ issue_id : Mapped [str ] = mapped_column (String , ForeignKey ("issues.id" ), nullable = False )
180+ label_id : Mapped [str ] = mapped_column (String , ForeignKey ("issue_labels.id" ), nullable = False )
181+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
182+
183+ # Relationships
184+ issue : Mapped ["Issue" ] = relationship ("Issue" , back_populates = "labels" )
185+ label : Mapped ["IssueLabel" ] = relationship ("IssueLabel" , back_populates = "issues" )
186+
187+ __table_args__ = (
188+ UniqueConstraint ("issue_id" , "label_id" , name = "uq_issue_label" ),
189+ )
190+
191+
192+ class IssueDependency (Base ):
193+ """Issue dependency relationships."""
194+ __tablename__ = "issue_dependencies"
195+
196+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
197+ issue_id : Mapped [str ] = mapped_column (String , ForeignKey ("issues.id" ), nullable = False )
198+ depends_on_id : Mapped [str ] = mapped_column (String , ForeignKey ("issues.id" ), nullable = False )
199+ dependency_type : Mapped [str ] = mapped_column (String , nullable = False ) # blocks, blocked_by, related
200+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
201+
202+ __table_args__ = (
203+ UniqueConstraint ("issue_id" , "depends_on_id" , "dependency_type" , name = "uq_issue_dependency" ),
204+ )
205+
206+
207+ class ActivityLog (Base ):
208+ """Activity log for tracking changes."""
209+ __tablename__ = "activity_logs"
210+
211+ id : Mapped [str ] = mapped_column (String , primary_key = True , default = _uuid )
212+ workspace_id : Mapped [str ] = mapped_column (String , ForeignKey ("workspaces.id" ), nullable = False )
213+ entity_type : Mapped [str ] = mapped_column (String , nullable = False ) # issue, project, workspace, etc.
214+ entity_id : Mapped [str ] = mapped_column (String , nullable = False )
215+ action : Mapped [str ] = mapped_column (String , nullable = False ) # created, updated, deleted, etc.
216+ actor_id : Mapped [Optional [str ]] = mapped_column (String , ForeignKey ("users.id" ), nullable = True )
217+ details : Mapped [Optional [Dict [str , Any ]]] = mapped_column (JSON , nullable = True , default = dict )
218+ created_at : Mapped [datetime ] = mapped_column (DateTime (timezone = True ), default = _utcnow )
0 commit comments