Enables multi-user project collaboration mirroring the shared calendar pattern. Includes ProjectMember model with permission levels, task assignment with auto-membership, optimistic locking, field allowlist for assignees, disconnect cascade, delta polling for projects and calendars, and full frontend integration with share sheet, assignment picker, permission gating, and notification handling. Migrations: 057 (indexes + version + comment user_id), 058 (project_members), 059 (project_task_assignments) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
37 lines
1.2 KiB
Python
37 lines
1.2 KiB
Python
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
from datetime import datetime
|
|
|
|
|
|
class TaskCommentCreate(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
content: str = Field(min_length=1, max_length=10000)
|
|
|
|
|
|
class TaskCommentResponse(BaseModel):
|
|
id: int
|
|
task_id: int
|
|
user_id: int | None = None
|
|
author_name: str | None = None
|
|
content: str
|
|
created_at: datetime
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
@model_validator(mode="before")
|
|
@classmethod
|
|
def resolve_author_name(cls, data): # type: ignore[override]
|
|
"""Populate author_name from eagerly loaded user relationship."""
|
|
if hasattr(data, "user") and data.user is not None:
|
|
if not getattr(data, "author_name", None):
|
|
# Use username as fallback — preferred_name is on Settings, not User
|
|
data = dict(
|
|
id=data.id,
|
|
task_id=data.task_id,
|
|
user_id=data.user_id,
|
|
author_name=data.user.username,
|
|
content=data.content,
|
|
created_at=data.created_at,
|
|
)
|
|
return data
|