b255a0f90e
Renames src/fabledassistant -> src/scribe and all imports, plus the default DB name and DB user/password (fabled -> scribe) in config + compose. 952 refs / 154 files. Reverses the old 'internal name stays fabledassistant' convention. Code-only: live databases are still physically named 'fabledassistant'. Deployed environments must set POSTGRES_DB / POSTGRES_USER (or rename the DB) since the defaults now resolve to 'scribe'. Repo (FabledScribe), git host (fabledsword), MCP (fabled-git) and the image name (fabledscribe) are intentionally unchanged. ruff check src/ clean locally; CI (typecheck + pytest) is the gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
85 lines
2.5 KiB
Python
85 lines
2.5 KiB
Python
"""Task work log service."""
|
|
import logging
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import select
|
|
|
|
from scribe.models import async_session
|
|
from scribe.models.task_log import TaskLog
|
|
from scribe.models.note import Note
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_UNSET = object()
|
|
|
|
|
|
async def create_log(
|
|
user_id: int,
|
|
task_id: int,
|
|
content: str,
|
|
duration_minutes: int | None = None,
|
|
) -> TaskLog:
|
|
async with async_session() as session:
|
|
# Verify task exists and belongs to user
|
|
result = await session.execute(
|
|
select(Note).where(Note.id == task_id, Note.user_id == user_id)
|
|
)
|
|
if result.scalars().first() is None:
|
|
raise ValueError(f"Task {task_id} not found")
|
|
log = TaskLog(
|
|
task_id=task_id,
|
|
user_id=user_id,
|
|
content=content,
|
|
duration_minutes=duration_minutes,
|
|
)
|
|
session.add(log)
|
|
await session.commit()
|
|
await session.refresh(log)
|
|
return log
|
|
|
|
|
|
async def list_logs(user_id: int, task_id: int) -> list[TaskLog]:
|
|
async with async_session() as session:
|
|
result = await session.execute(
|
|
select(TaskLog)
|
|
.where(TaskLog.task_id == task_id, TaskLog.user_id == user_id)
|
|
.order_by(TaskLog.created_at.asc())
|
|
)
|
|
return list(result.scalars().all())
|
|
|
|
|
|
async def update_log(
|
|
user_id: int,
|
|
log_id: int,
|
|
content: str | None = None,
|
|
duration_minutes: object = _UNSET,
|
|
) -> TaskLog | None:
|
|
async with async_session() as session:
|
|
result = await session.execute(
|
|
select(TaskLog).where(TaskLog.id == log_id, TaskLog.user_id == user_id)
|
|
)
|
|
log = result.scalars().first()
|
|
if log is None:
|
|
return None
|
|
if content is not None:
|
|
log.content = content
|
|
if duration_minutes is not _UNSET:
|
|
log.duration_minutes = duration_minutes # type: ignore[assignment]
|
|
log.updated_at = datetime.now(timezone.utc)
|
|
await session.commit()
|
|
await session.refresh(log)
|
|
return log
|
|
|
|
|
|
async def delete_log(user_id: int, log_id: int) -> bool:
|
|
async with async_session() as session:
|
|
result = await session.execute(
|
|
select(TaskLog).where(TaskLog.id == log_id, TaskLog.user_id == user_id)
|
|
)
|
|
log = result.scalars().first()
|
|
if log is None:
|
|
return False
|
|
await session.delete(log)
|
|
await session.commit()
|
|
return True
|