Initial SFERA platform baseline
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TaskStatus(str, Enum):
|
||||
OPEN = "OPEN"
|
||||
IN_PROGRESS = "IN_PROGRESS"
|
||||
DONE = "DONE"
|
||||
CANCELED = "CANCELED"
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
user_id: str
|
||||
display_name: str
|
||||
email: str | None = None
|
||||
|
||||
|
||||
class Workspace(BaseModel):
|
||||
workspace_id: str
|
||||
name: str
|
||||
owner_user_id: str | None = None
|
||||
|
||||
|
||||
class Project(BaseModel):
|
||||
project_id: str
|
||||
workspace_id: str
|
||||
name: str
|
||||
|
||||
|
||||
class Task(BaseModel):
|
||||
task_id: str
|
||||
project_id: str
|
||||
title: str
|
||||
status: TaskStatus = TaskStatus.OPEN
|
||||
assignee_user_id: str | None = None
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
|
||||
|
||||
class ChangeSession(BaseModel):
|
||||
session_id: str
|
||||
task_id: str
|
||||
user_id: str
|
||||
started_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
finished_at: datetime | None = None
|
||||
|
||||
|
||||
class Comment(BaseModel):
|
||||
comment_id: str
|
||||
project_id: str
|
||||
target_id: str
|
||||
user_id: str
|
||||
body: str
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
|
||||
|
||||
class ActivityEvent(BaseModel):
|
||||
event_id: str
|
||||
project_id: str
|
||||
actor_user_id: str
|
||||
verb: str
|
||||
target_id: str
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
attributes: dict = Field(default_factory=dict)
|
||||
|
||||
|
||||
class Ownership(BaseModel):
|
||||
owner_user_id: str
|
||||
project_id: str
|
||||
target_id: str
|
||||
role: str = "OWNER"
|
||||
assigned_by: str | None = None
|
||||
assigned_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
||||
attributes: dict = Field(default_factory=dict)
|
||||
|
||||
|
||||
class InMemoryCollaborationStore:
|
||||
def __init__(self) -> None:
|
||||
self.users: dict[str, User] = {}
|
||||
self.workspaces: dict[str, Workspace] = {}
|
||||
self.projects: dict[str, Project] = {}
|
||||
self.tasks: dict[str, Task] = {}
|
||||
self.sessions: dict[str, ChangeSession] = {}
|
||||
self.comments: dict[str, Comment] = {}
|
||||
self.activities: list[ActivityEvent] = []
|
||||
self.ownership: dict[str, Ownership] = {}
|
||||
|
||||
def upsert_user(self, user: User) -> User:
|
||||
self.users[user.user_id] = user
|
||||
return user
|
||||
|
||||
def upsert_workspace(self, workspace: Workspace) -> Workspace:
|
||||
self.workspaces[workspace.workspace_id] = workspace
|
||||
return workspace
|
||||
|
||||
def upsert_project(self, project: Project) -> Project:
|
||||
self.projects[project.project_id] = project
|
||||
return project
|
||||
|
||||
def upsert_task(self, task: Task) -> Task:
|
||||
self.tasks[task.task_id] = task
|
||||
return task
|
||||
|
||||
def start_session(self, session: ChangeSession) -> ChangeSession:
|
||||
if session.task_id not in self.tasks:
|
||||
raise KeyError(f"Unknown task: {session.task_id}")
|
||||
if session.user_id not in self.users:
|
||||
raise KeyError(f"Unknown user: {session.user_id}")
|
||||
self.sessions[session.session_id] = session
|
||||
return session
|
||||
|
||||
def finish_session(self, session_id: str) -> ChangeSession:
|
||||
session = self.sessions.get(session_id)
|
||||
if session is None:
|
||||
raise KeyError(f"Unknown session: {session_id}")
|
||||
if session.finished_at is None:
|
||||
session = session.model_copy(update={"finished_at": datetime.now(timezone.utc)})
|
||||
self.sessions[session_id] = session
|
||||
return session
|
||||
|
||||
def add_comment(self, comment: Comment) -> Comment:
|
||||
if comment.user_id not in self.users:
|
||||
raise KeyError(f"Unknown user: {comment.user_id}")
|
||||
self.comments[comment.comment_id] = comment
|
||||
return comment
|
||||
|
||||
def comments_for_project(self, project_id: str) -> list[Comment]:
|
||||
return sorted(
|
||||
[
|
||||
comment
|
||||
for comment in self.comments.values()
|
||||
if comment.project_id == project_id
|
||||
],
|
||||
key=lambda item: item.created_at,
|
||||
)
|
||||
|
||||
def comments_for_target(self, project_id: str, target_id: str) -> list[Comment]:
|
||||
return [
|
||||
comment
|
||||
for comment in self.comments_for_project(project_id)
|
||||
if comment.target_id == target_id
|
||||
]
|
||||
|
||||
def add_activity(self, event: ActivityEvent) -> ActivityEvent:
|
||||
self.activities.append(event)
|
||||
return event
|
||||
|
||||
def assign_owner(self, ownership: Ownership) -> Ownership:
|
||||
if ownership.owner_user_id not in self.users:
|
||||
raise KeyError(f"Unknown user: {ownership.owner_user_id}")
|
||||
self.ownership[self._ownership_key(ownership)] = ownership
|
||||
return ownership
|
||||
|
||||
def owners_for_project(self, project_id: str) -> list[Ownership]:
|
||||
return sorted(
|
||||
[
|
||||
ownership
|
||||
for ownership in self.ownership.values()
|
||||
if ownership.project_id == project_id
|
||||
],
|
||||
key=lambda item: (item.target_id, item.role, item.owner_user_id),
|
||||
)
|
||||
|
||||
def owners_for_target(self, project_id: str, target_id: str) -> list[Ownership]:
|
||||
return [
|
||||
ownership
|
||||
for ownership in self.owners_for_project(project_id)
|
||||
if ownership.target_id == target_id
|
||||
]
|
||||
|
||||
def activity_feed(self, project_id: str) -> list[ActivityEvent]:
|
||||
return [
|
||||
event
|
||||
for event in sorted(self.activities, key=lambda item: item.created_at, reverse=True)
|
||||
if event.project_id == project_id
|
||||
]
|
||||
|
||||
def _ownership_key(self, ownership: Ownership) -> str:
|
||||
return f"{ownership.project_id}:{ownership.target_id}:{ownership.role}:{ownership.owner_user_id}"
|
||||
|
||||
|
||||
__all__ = [
|
||||
"ActivityEvent",
|
||||
"ChangeSession",
|
||||
"Comment",
|
||||
"InMemoryCollaborationStore",
|
||||
"Ownership",
|
||||
"Project",
|
||||
"Task",
|
||||
"TaskStatus",
|
||||
"User",
|
||||
"Workspace",
|
||||
]
|
||||
Reference in New Issue
Block a user