|
1 | 1 | import uuid |
| 2 | +import datetime |
| 3 | +from enum import Enum |
2 | 4 |
|
3 | 5 | from pydantic import EmailStr |
4 | 6 | from sqlmodel import Field, Relationship, SQLModel |
@@ -44,6 +46,12 @@ class User(UserBase, table=True): |
44 | 46 | id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) |
45 | 47 | hashed_password: str |
46 | 48 | items: list["Item"] = Relationship(back_populates="owner", cascade_delete=True) |
| 49 | + created_characters: list["Character"] = Relationship( |
| 50 | + back_populates="creator", cascade_delete=True |
| 51 | + ) |
| 52 | + conversations: list["Conversation"] = Relationship( |
| 53 | + back_populates="user", cascade_delete=True |
| 54 | + ) |
47 | 55 |
|
48 | 56 |
|
49 | 57 | # Properties to return via API, id is always required |
@@ -111,3 +119,126 @@ class TokenPayload(SQLModel): |
111 | 119 | class NewPassword(SQLModel): |
112 | 120 | token: str |
113 | 121 | new_password: str = Field(min_length=8, max_length=40) |
| 122 | + |
| 123 | + |
| 124 | +# Shared properties |
| 125 | +class CharacterBase(SQLModel): |
| 126 | + name: str = Field(index=True, max_length=100) |
| 127 | + description: str | None = Field(default=None, max_length=1000) |
| 128 | + image_url: str | None = Field(default=None, max_length=255) |
| 129 | + greeting_message: str | None = Field(default=None, max_length=1000) |
| 130 | + |
| 131 | + |
| 132 | +class CharacterStatus(str, Enum): |
| 133 | + PENDING = "pending" |
| 134 | + APPROVED = "approved" |
| 135 | + REJECTED = "rejected" |
| 136 | + |
| 137 | + |
| 138 | +# Properties to receive via API on creation (user submission) |
| 139 | +class CharacterCreate(CharacterBase): |
| 140 | + pass |
| 141 | + |
| 142 | + |
| 143 | +# Properties to receive via API on update (admin only) |
| 144 | +class CharacterUpdate(CharacterBase): |
| 145 | + name: str | None = Field(default=None, max_length=100) |
| 146 | + description: str | None = Field(default=None, max_length=1000) |
| 147 | + image_url: str | None = Field(default=None, max_length=255) |
| 148 | + greeting_message: str | None = Field(default=None, max_length=1000) |
| 149 | + status: CharacterStatus | None = None |
| 150 | + |
| 151 | + |
| 152 | +# Database model |
| 153 | +class Character(CharacterBase, table=True): |
| 154 | + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) |
| 155 | + status: CharacterStatus = Field(default=CharacterStatus.PENDING) |
| 156 | + creator_id: uuid.UUID = Field(foreign_key="user.id", nullable=False) |
| 157 | + |
| 158 | + creator: User = Relationship(back_populates="created_characters") |
| 159 | + conversations: list["Conversation"] = Relationship(back_populates="character") |
| 160 | + |
| 161 | + |
| 162 | +# Properties to return via API |
| 163 | +class CharacterPublic(CharacterBase): |
| 164 | + id: uuid.UUID |
| 165 | + status: CharacterStatus |
| 166 | + creator_id: uuid.UUID |
| 167 | + |
| 168 | + |
| 169 | +class CharactersPublic(SQLModel): |
| 170 | + data: list[CharacterPublic] |
| 171 | + count: int |
| 172 | + |
| 173 | + |
| 174 | +# ---------------- Conversation Models ---------------- |
| 175 | + |
| 176 | + |
| 177 | +class ConversationBase(SQLModel): |
| 178 | + pass # No shared fields initially, maybe add title later? |
| 179 | + |
| 180 | + |
| 181 | +class ConversationCreate(SQLModel): |
| 182 | + character_id: uuid.UUID |
| 183 | + |
| 184 | + |
| 185 | +# Database model |
| 186 | +class Conversation(ConversationBase, table=True): |
| 187 | + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) |
| 188 | + user_id: uuid.UUID = Field(foreign_key="user.id", nullable=False) |
| 189 | + character_id: uuid.UUID = Field(foreign_key="character.id", nullable=False) |
| 190 | + created_at: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) |
| 191 | + |
| 192 | + user: User = Relationship(back_populates="conversations") |
| 193 | + character: Character = Relationship(back_populates="conversations") |
| 194 | + messages: list["Message"] = Relationship(back_populates="conversation") |
| 195 | + |
| 196 | + |
| 197 | +class ConversationPublic(ConversationBase): |
| 198 | + id: uuid.UUID |
| 199 | + user_id: uuid.UUID |
| 200 | + character_id: uuid.UUID |
| 201 | + created_at: datetime.datetime |
| 202 | + |
| 203 | + |
| 204 | +class ConversationsPublic(SQLModel): |
| 205 | + data: list[ConversationPublic] |
| 206 | + count: int |
| 207 | + |
| 208 | + |
| 209 | +# ---------------- Message Models ---------------- |
| 210 | + |
| 211 | + |
| 212 | +class MessageSender(str, Enum): |
| 213 | + USER = "user" |
| 214 | + AI = "ai" |
| 215 | + |
| 216 | + |
| 217 | +class MessageBase(SQLModel): |
| 218 | + content: str = Field(max_length=5000) # Limit message length |
| 219 | + |
| 220 | + |
| 221 | +class MessageCreate(MessageBase): |
| 222 | + pass # Content is the main input |
| 223 | + |
| 224 | + |
| 225 | +# Database model |
| 226 | +class Message(MessageBase, table=True): |
| 227 | + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) |
| 228 | + conversation_id: uuid.UUID = Field(foreign_key="conversation.id", nullable=False) |
| 229 | + sender: MessageSender |
| 230 | + timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow) |
| 231 | + |
| 232 | + conversation: Conversation = Relationship(back_populates="messages") |
| 233 | + |
| 234 | + |
| 235 | +class MessagePublic(MessageBase): |
| 236 | + id: uuid.UUID |
| 237 | + conversation_id: uuid.UUID |
| 238 | + sender: MessageSender |
| 239 | + timestamp: datetime.datetime |
| 240 | + |
| 241 | + |
| 242 | +class MessagesPublic(SQLModel): |
| 243 | + data: list[MessagePublic] |
| 244 | + count: int |
0 commit comments