refactor: rename package fabledassistant -> scribe (code-only)
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>
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
from fabledassistant.config import Config
|
||||
|
||||
engine = create_async_engine(
|
||||
Config.DATABASE_URL,
|
||||
echo=False,
|
||||
pool_pre_ping=True,
|
||||
pool_recycle=1800,
|
||||
)
|
||||
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
from fabledassistant.models.base import CreatedAtMixin, TimestampMixin # noqa: E402, F401
|
||||
|
||||
|
||||
from fabledassistant.models.note import Note, TaskPriority, TaskStatus # noqa: E402, F401
|
||||
from fabledassistant.models.setting import Setting # noqa: E402, F401
|
||||
from fabledassistant.models.user import User # noqa: E402, F401
|
||||
from fabledassistant.models.app_log import AppLog # noqa: E402, F401
|
||||
from fabledassistant.models.password_reset import PasswordResetToken # noqa: E402, F401
|
||||
from fabledassistant.models.invitation import InvitationToken # noqa: E402, F401
|
||||
from fabledassistant.models.embedding import NoteEmbedding # noqa: E402, F401
|
||||
from fabledassistant.models.project import Project # noqa: E402, F401
|
||||
from fabledassistant.models.event import Event # noqa: E402, F401
|
||||
from fabledassistant.models.milestone import Milestone # noqa: E402, F401
|
||||
from fabledassistant.models.task_log import TaskLog # noqa: E402, F401
|
||||
from fabledassistant.models.note_draft import NoteDraft # noqa: E402, F401
|
||||
from fabledassistant.models.note_version import NoteVersion # noqa: E402, F401
|
||||
from fabledassistant.models.group import Group, GroupMembership # noqa: E402, F401
|
||||
from fabledassistant.models.share import NoteShare, ProjectShare # noqa: E402, F401
|
||||
from fabledassistant.models.notification import Notification # noqa: E402, F401
|
||||
from fabledassistant.models.api_key import ApiKey # noqa: E402, F401
|
||||
from fabledassistant.models.user_profile import UserProfile # noqa: E402, F401
|
||||
from fabledassistant.models.rulebook import ( # noqa: E402, F401
|
||||
Rulebook, RulebookTopic, Rule, project_rulebook_subscriptions,
|
||||
)
|
||||
@@ -5,30 +5,30 @@ from pathlib import Path
|
||||
|
||||
from quart import Quart, g, jsonify, make_response, request, send_from_directory
|
||||
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.routes.admin import admin_bp
|
||||
from fabledassistant.routes.api import api
|
||||
from fabledassistant.routes.auth import auth_bp
|
||||
from fabledassistant.routes.export import export_bp
|
||||
from fabledassistant.routes.notes import notes_bp
|
||||
from fabledassistant.routes.milestones import milestones_bp
|
||||
from fabledassistant.routes.task_logs import task_logs_bp
|
||||
from fabledassistant.routes.projects import projects_bp
|
||||
from fabledassistant.routes.settings import settings_bp
|
||||
from fabledassistant.routes.tasks import tasks_bp
|
||||
from fabledassistant.routes.groups import groups_bp
|
||||
from fabledassistant.routes.shares import shares_bp
|
||||
from fabledassistant.routes.in_app_notifications import notifications_bp
|
||||
from fabledassistant.routes.users import users_bp
|
||||
from fabledassistant.routes.api_keys import api_keys_bp
|
||||
from fabledassistant.routes.events import events_bp
|
||||
from fabledassistant.routes.search import search_bp
|
||||
from fabledassistant.routes.profile import profile_bp
|
||||
from fabledassistant.routes.knowledge import knowledge_bp
|
||||
from fabledassistant.routes.rulebooks import rulebooks_bp
|
||||
from fabledassistant.routes.trash import trash_bp
|
||||
from fabledassistant.routes.dashboard import dashboard_bp
|
||||
from fabledassistant.mcp import mount_mcp
|
||||
from scribe.config import Config
|
||||
from scribe.routes.admin import admin_bp
|
||||
from scribe.routes.api import api
|
||||
from scribe.routes.auth import auth_bp
|
||||
from scribe.routes.export import export_bp
|
||||
from scribe.routes.notes import notes_bp
|
||||
from scribe.routes.milestones import milestones_bp
|
||||
from scribe.routes.task_logs import task_logs_bp
|
||||
from scribe.routes.projects import projects_bp
|
||||
from scribe.routes.settings import settings_bp
|
||||
from scribe.routes.tasks import tasks_bp
|
||||
from scribe.routes.groups import groups_bp
|
||||
from scribe.routes.shares import shares_bp
|
||||
from scribe.routes.in_app_notifications import notifications_bp
|
||||
from scribe.routes.users import users_bp
|
||||
from scribe.routes.api_keys import api_keys_bp
|
||||
from scribe.routes.events import events_bp
|
||||
from scribe.routes.search import search_bp
|
||||
from scribe.routes.profile import profile_bp
|
||||
from scribe.routes.knowledge import knowledge_bp
|
||||
from scribe.routes.rulebooks import rulebooks_bp
|
||||
from scribe.routes.trash import trash_bp
|
||||
from scribe.routes.dashboard import dashboard_bp
|
||||
from scribe.mcp import mount_mcp
|
||||
|
||||
STATIC_DIR = Path(__file__).parent / "static"
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -115,7 +115,7 @@ def create_app() -> Quart:
|
||||
# Log usage for API requests (skip logs endpoint to avoid recursion)
|
||||
if request.path.startswith("/api/") and not request.path.startswith("/api/admin/logs"):
|
||||
try:
|
||||
from fabledassistant.services.logging import log_usage
|
||||
from scribe.services.logging import log_usage
|
||||
|
||||
user = getattr(g, "user", None)
|
||||
await log_usage(
|
||||
@@ -150,10 +150,10 @@ def create_app() -> Quart:
|
||||
async def startup():
|
||||
import asyncio
|
||||
|
||||
from fabledassistant.services.auth import start_auth_token_retention_loop
|
||||
from fabledassistant.services.embeddings import backfill_note_embeddings
|
||||
from fabledassistant.services.logging import start_log_retention_loop
|
||||
from fabledassistant.services.notifications import start_notification_loop
|
||||
from scribe.services.auth import start_auth_token_retention_loop
|
||||
from scribe.services.embeddings import backfill_note_embeddings
|
||||
from scribe.services.logging import start_log_retention_loop
|
||||
from scribe.services.notifications import start_notification_loop
|
||||
|
||||
start_log_retention_loop()
|
||||
start_notification_loop()
|
||||
@@ -170,36 +170,36 @@ def create_app() -> Quart:
|
||||
asyncio.create_task(_delayed_backfill())
|
||||
|
||||
# Event scheduler (reminders + CalDAV pull sync)
|
||||
from fabledassistant.services.event_scheduler import start_event_scheduler
|
||||
from scribe.services.event_scheduler import start_event_scheduler
|
||||
start_event_scheduler(asyncio.get_running_loop())
|
||||
|
||||
# Version-pinning scheduler (daily auto-pin scan at 03:00 UTC)
|
||||
from fabledassistant.services.version_pinning_scheduler import (
|
||||
from scribe.services.version_pinning_scheduler import (
|
||||
start_version_pinning_scheduler,
|
||||
)
|
||||
start_version_pinning_scheduler(asyncio.get_running_loop())
|
||||
|
||||
# Trash retention scheduler (daily expired-trash purge at 03:30 UTC)
|
||||
from fabledassistant.services.trash_scheduler import start_trash_scheduler
|
||||
from scribe.services.trash_scheduler import start_trash_scheduler
|
||||
start_trash_scheduler(asyncio.get_running_loop())
|
||||
|
||||
# Diagnostic instrumentation — heartbeat, signal handlers, asyncio
|
||||
# exception hook. Cheap (~1 log line/min), high diagnostic value when
|
||||
# the app crashes mysteriously. See services/diagnostics.py.
|
||||
from fabledassistant.services.diagnostics import start_diagnostics
|
||||
from scribe.services.diagnostics import start_diagnostics
|
||||
start_diagnostics(asyncio.get_running_loop())
|
||||
|
||||
@app.after_serving
|
||||
async def shutdown():
|
||||
from fabledassistant.services.event_scheduler import stop_event_scheduler
|
||||
from scribe.services.event_scheduler import stop_event_scheduler
|
||||
stop_event_scheduler()
|
||||
from fabledassistant.services.version_pinning_scheduler import (
|
||||
from scribe.services.version_pinning_scheduler import (
|
||||
stop_version_pinning_scheduler,
|
||||
)
|
||||
stop_version_pinning_scheduler()
|
||||
from fabledassistant.services.trash_scheduler import stop_trash_scheduler
|
||||
from scribe.services.trash_scheduler import stop_trash_scheduler
|
||||
stop_trash_scheduler()
|
||||
from fabledassistant.services.diagnostics import stop_diagnostics
|
||||
from scribe.services.diagnostics import stop_diagnostics
|
||||
stop_diagnostics()
|
||||
|
||||
@app.route("/")
|
||||
@@ -249,7 +249,7 @@ def create_app() -> Quart:
|
||||
logger.exception("Internal server error on %s %s", request.method, request.path)
|
||||
|
||||
try:
|
||||
from fabledassistant.services.logging import log_error
|
||||
from scribe.services.logging import log_error
|
||||
|
||||
user = getattr(g, "user", None)
|
||||
await log_error(
|
||||
@@ -2,8 +2,8 @@ import functools
|
||||
|
||||
from quart import g, jsonify, request, session
|
||||
|
||||
from fabledassistant.services.auth import get_user_by_id
|
||||
from fabledassistant.services.api_keys import lookup_key
|
||||
from scribe.services.auth import get_user_by_id
|
||||
from scribe.services.api_keys import lookup_key
|
||||
|
||||
|
||||
def _check_auth(f, required_role: str | None = None):
|
||||
@@ -20,7 +20,7 @@ class Config:
|
||||
DATABASE_URL: str = _read_secret(
|
||||
"DATABASE_URL",
|
||||
"DATABASE_URL_FILE",
|
||||
"postgresql+asyncpg://fabled:fabled@localhost:5432/fabledassistant",
|
||||
"postgresql+asyncpg://scribe:scribe@localhost:5432/scribe",
|
||||
)
|
||||
SECRET_KEY: str = _read_secret("SECRET_KEY", "SECRET_KEY_FILE", "dev-secret-change-me")
|
||||
SECURE_COOKIES: bool = os.environ.get("SECURE_COOKIES", "").lower() in ("1", "true", "yes")
|
||||
@@ -3,6 +3,6 @@
|
||||
Auth uses the existing `api_keys` table: a Bearer token in the Authorization
|
||||
header resolves to a user, and every tool call acts on that user's data only.
|
||||
"""
|
||||
from fabledassistant.mcp.server import build_mcp_server, mount_mcp
|
||||
from scribe.mcp.server import build_mcp_server, mount_mcp
|
||||
|
||||
__all__ = ["build_mcp_server", "mount_mcp"]
|
||||
@@ -1,7 +1,7 @@
|
||||
"""MCP-side Bearer token resolution. Reuses the existing api_keys infrastructure."""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.services.api_keys import lookup_key
|
||||
from scribe.services.api_keys import lookup_key
|
||||
|
||||
|
||||
async def resolve_bearer_to_user_id(auth_header: str | None) -> int | None:
|
||||
@@ -220,7 +220,7 @@ def build_mcp_server() -> FastMCP:
|
||||
enable_dns_rebinding_protection=False,
|
||||
),
|
||||
)
|
||||
from fabledassistant.mcp.tools import register_all
|
||||
from scribe.mcp.tools import register_all
|
||||
register_all(mcp)
|
||||
return mcp
|
||||
|
||||
@@ -239,7 +239,7 @@ def mount_mcp(app: Quart) -> None:
|
||||
inside Quart, we hook the session manager's `run()` async context manager
|
||||
into Quart's serving lifecycle (before_serving / after_serving).
|
||||
"""
|
||||
from fabledassistant.mcp.auth import resolve_bearer
|
||||
from scribe.mcp.auth import resolve_bearer
|
||||
|
||||
mcp = build_mcp_server()
|
||||
mcp_asgi = mcp.streamable_http_app()
|
||||
@@ -299,7 +299,7 @@ def mount_mcp(app: Quart) -> None:
|
||||
return
|
||||
|
||||
scope["scribe_user_id"] = user_id
|
||||
from fabledassistant.mcp._context import _user_id_ctx
|
||||
from scribe.mcp._context import _user_id_ctx
|
||||
token = _user_id_ctx.set(user_id)
|
||||
try:
|
||||
await mcp_asgi(scope, receive, send)
|
||||
@@ -4,7 +4,7 @@ Each tool module exposes a `register(mcp)` function that attaches its tools
|
||||
to a FastMCP instance. `register_all(mcp)` is the single entry point called
|
||||
from `mcp.server.build_mcp_server`.
|
||||
"""
|
||||
from fabledassistant.mcp.tools import (
|
||||
from scribe.mcp.tools import (
|
||||
entities, events, milestones, notes, processes, projects, recent, rulebooks, search, tags, tasks, trash,
|
||||
)
|
||||
|
||||
@@ -14,9 +14,9 @@ of plain strings for ergonomics; checked-state is reset to False on each call.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import knowledge as knowledge_svc
|
||||
from fabledassistant.services import notes as notes_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import knowledge as knowledge_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
|
||||
|
||||
async def _list_by_type(note_type: str, q: str, tag: str, limit: int) -> dict:
|
||||
@@ -19,9 +19,9 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import events as events_svc
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import events as events_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
|
||||
|
||||
def _combine(start_date: str, start_time: str) -> datetime:
|
||||
@@ -11,9 +11,9 @@ Sentinels:
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import milestones as milestones_svc
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import milestones as milestones_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
|
||||
|
||||
async def list_milestones(project_id: int) -> dict:
|
||||
@@ -13,9 +13,9 @@ Sentinel conventions (inherited from existing fable-mcp tools):
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import notes as notes_svc
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
|
||||
|
||||
async def list_notes(
|
||||
@@ -6,9 +6,9 @@ get_process is the fire mechanism: it returns the full prompt for Claude to run.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import knowledge as knowledge_svc
|
||||
from fabledassistant.services import notes as notes_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import knowledge as knowledge_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
|
||||
|
||||
async def list_processes(q: str = "", tag: str = "", limit: int = 50) -> dict:
|
||||
@@ -16,12 +16,12 @@ keeps working.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import milestones as milestones_svc
|
||||
from fabledassistant.services import notes as notes_svc
|
||||
from fabledassistant.services import projects as projects_svc
|
||||
from fabledassistant.services import rulebooks as rulebooks_svc
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import milestones as milestones_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import projects as projects_svc
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
|
||||
|
||||
async def list_projects() -> dict:
|
||||
@@ -13,11 +13,11 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.event import Event
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.models import async_session
|
||||
from scribe.models.event import Event
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.project import Project
|
||||
|
||||
|
||||
async def get_recent(days: int = 7, limit: int = 25) -> dict:
|
||||
@@ -9,9 +9,9 @@ spec.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import rulebooks as rulebooks_svc
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
|
||||
|
||||
# ── Rulebook CRUD ───────────────────────────────────────────────────────
|
||||
@@ -7,8 +7,8 @@ working. Differences from fable-mcp:
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services.embeddings import semantic_search_notes
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services.embeddings import semantic_search_notes
|
||||
|
||||
|
||||
async def search(q: str, content_type: str = "all", limit: int = 10) -> dict:
|
||||
@@ -8,9 +8,9 @@ from __future__ import annotations
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
|
||||
|
||||
def _aggregate_tag_counts(tag_lists) -> dict[str, int]:
|
||||
@@ -18,12 +18,12 @@ Sentinels (preserved from existing fable-mcp):
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import notes as notes_svc
|
||||
from fabledassistant.services import planning as planning_svc
|
||||
from fabledassistant.services import rulebooks as rulebooks_svc
|
||||
from fabledassistant.services import task_logs as task_logs_svc
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import planning as planning_svc
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import task_logs as task_logs_svc
|
||||
from scribe.services import trash as trash_svc
|
||||
|
||||
|
||||
async def list_tasks(
|
||||
@@ -6,8 +6,8 @@ the deleted_batch_id that a delete operation returns.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.mcp._context import current_user_id
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from scribe.mcp._context import current_user_id
|
||||
from scribe.services import trash as trash_svc
|
||||
|
||||
|
||||
async def list_trash() -> dict:
|
||||
@@ -0,0 +1,42 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
from scribe.config import Config
|
||||
|
||||
engine = create_async_engine(
|
||||
Config.DATABASE_URL,
|
||||
echo=False,
|
||||
pool_pre_ping=True,
|
||||
pool_recycle=1800,
|
||||
)
|
||||
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
from scribe.models.base import CreatedAtMixin, TimestampMixin # noqa: E402, F401
|
||||
|
||||
|
||||
from scribe.models.note import Note, TaskPriority, TaskStatus # noqa: E402, F401
|
||||
from scribe.models.setting import Setting # noqa: E402, F401
|
||||
from scribe.models.user import User # noqa: E402, F401
|
||||
from scribe.models.app_log import AppLog # noqa: E402, F401
|
||||
from scribe.models.password_reset import PasswordResetToken # noqa: E402, F401
|
||||
from scribe.models.invitation import InvitationToken # noqa: E402, F401
|
||||
from scribe.models.embedding import NoteEmbedding # noqa: E402, F401
|
||||
from scribe.models.project import Project # noqa: E402, F401
|
||||
from scribe.models.event import Event # noqa: E402, F401
|
||||
from scribe.models.milestone import Milestone # noqa: E402, F401
|
||||
from scribe.models.task_log import TaskLog # noqa: E402, F401
|
||||
from scribe.models.note_draft import NoteDraft # noqa: E402, F401
|
||||
from scribe.models.note_version import NoteVersion # noqa: E402, F401
|
||||
from scribe.models.group import Group, GroupMembership # noqa: E402, F401
|
||||
from scribe.models.share import NoteShare, ProjectShare # noqa: E402, F401
|
||||
from scribe.models.notification import Notification # noqa: E402, F401
|
||||
from scribe.models.api_key import ApiKey # noqa: E402, F401
|
||||
from scribe.models.user_profile import UserProfile # noqa: E402, F401
|
||||
from scribe.models.rulebook import ( # noqa: E402, F401
|
||||
Rulebook, RulebookTopic, Rule, project_rulebook_subscriptions,
|
||||
)
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime
|
||||
from sqlalchemy import DateTime, ForeignKey, Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import CreatedAtMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin
|
||||
|
||||
|
||||
class ApiKey(Base, CreatedAtMixin):
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import DateTime, Float, Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from scribe.models import Base
|
||||
|
||||
|
||||
class AppLog(Base):
|
||||
@@ -4,7 +4,7 @@ from sqlalchemy import DateTime, ForeignKey, Integer
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from scribe.models import Base
|
||||
|
||||
|
||||
class NoteEmbedding(Base):
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timedelta, timezone
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import SoftDeleteMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import SoftDeleteMixin
|
||||
|
||||
|
||||
class Event(Base, SoftDeleteMixin):
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import ForeignKey, Integer, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import CreatedAtMixin, TimestampMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin, TimestampMixin
|
||||
|
||||
|
||||
class Group(Base, TimestampMixin):
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from scribe.models import Base
|
||||
|
||||
|
||||
class InvitationToken(Base):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, SoftDeleteMixin
|
||||
|
||||
|
||||
class Milestone(Base, TimestampMixin, SoftDeleteMixin):
|
||||
@@ -5,8 +5,8 @@ from sqlalchemy import Date, DateTime, ForeignKey, Index, Integer, Text
|
||||
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, SoftDeleteMixin
|
||||
|
||||
|
||||
class TaskStatus(str, enum.Enum):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import TimestampMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin
|
||||
|
||||
|
||||
class NoteDraft(Base, TimestampMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ARRAY, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import CreatedAtMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin
|
||||
|
||||
|
||||
class NoteVersion(Base, CreatedAtMixin):
|
||||
@@ -4,8 +4,8 @@ from sqlalchemy import DateTime, ForeignKey, Integer, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import CreatedAtMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin
|
||||
|
||||
|
||||
class Notification(Base, CreatedAtMixin):
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import Boolean, DateTime, ForeignKey, Index, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from scribe.models import Base
|
||||
|
||||
|
||||
class PasswordResetToken(Base):
|
||||
@@ -2,8 +2,8 @@ import enum
|
||||
from datetime import datetime
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import TimestampMixin, SoftDeleteMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin, SoftDeleteMixin
|
||||
|
||||
|
||||
class ProjectStatus(str, enum.Enum):
|
||||
@@ -3,8 +3,8 @@ from datetime import datetime, timezone
|
||||
from sqlalchemy import BigInteger, Boolean, Column, DateTime, ForeignKey, Index, Integer, Table, Text, text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import SoftDeleteMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import SoftDeleteMixin
|
||||
|
||||
|
||||
class Rulebook(Base, SoftDeleteMixin):
|
||||
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from scribe.models import Base
|
||||
|
||||
|
||||
class Setting(Base):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import CheckConstraint, ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import TimestampMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin
|
||||
|
||||
|
||||
class ProjectShare(Base, TimestampMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import TimestampMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin
|
||||
|
||||
|
||||
class TaskLog(Base, TimestampMixin):
|
||||
@@ -1,8 +1,8 @@
|
||||
from sqlalchemy import Index, Integer, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import CreatedAtMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import CreatedAtMixin
|
||||
|
||||
|
||||
class User(Base, CreatedAtMixin):
|
||||
@@ -2,8 +2,8 @@ from sqlalchemy import ForeignKey, Integer, Text
|
||||
from sqlalchemy.dialects.postgresql import ARRAY, JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from fabledassistant.models import Base
|
||||
from fabledassistant.models.base import TimestampMixin
|
||||
from scribe.models import Base
|
||||
from scribe.models.base import TimestampMixin
|
||||
|
||||
|
||||
class UserProfile(Base, TimestampMixin):
|
||||
@@ -3,8 +3,8 @@ import json
|
||||
|
||||
from quart import Blueprint, Response, g, jsonify, request
|
||||
|
||||
from fabledassistant.auth import admin_required, login_required, get_current_user_id
|
||||
from fabledassistant.services.auth import (
|
||||
from scribe.auth import admin_required, login_required, get_current_user_id
|
||||
from scribe.services.auth import (
|
||||
create_invitation,
|
||||
delete_user,
|
||||
is_registration_open,
|
||||
@@ -13,15 +13,15 @@ from fabledassistant.services.auth import (
|
||||
revoke_invitation,
|
||||
set_registration_open,
|
||||
)
|
||||
from fabledassistant.services.backup import (
|
||||
from scribe.services.backup import (
|
||||
export_full_backup,
|
||||
export_user_backup,
|
||||
restore_full_backup,
|
||||
)
|
||||
from fabledassistant.services.email import SMTP_SETTING_KEYS, get_base_url, get_smtp_config, is_smtp_configured, send_test_email
|
||||
from fabledassistant.services.logging import get_logs, get_log_stats, log_audit
|
||||
from fabledassistant.services.notifications import send_invitation_email
|
||||
from fabledassistant.services.settings import set_setting, set_settings_batch
|
||||
from scribe.services.email import SMTP_SETTING_KEYS, get_base_url, get_smtp_config, is_smtp_configured, send_test_email
|
||||
from scribe.services.logging import get_logs, get_log_stats, log_audit
|
||||
from scribe.services.notifications import send_invitation_email
|
||||
from scribe.services.settings import set_setting, set_settings_batch
|
||||
|
||||
admin_bp = Blueprint("admin", __name__, url_prefix="/api/admin")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.services.api_keys import create_api_key, list_api_keys, revoke_api_key
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.services.api_keys import create_api_key, list_api_keys, revoke_api_key
|
||||
|
||||
api_keys_bp = Blueprint("api_keys", __name__, url_prefix="/api/api-keys")
|
||||
|
||||
@@ -6,10 +6,10 @@ import secrets
|
||||
|
||||
from quart import Blueprint, g, jsonify, redirect, request, session
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.rate_limit import is_rate_limited
|
||||
from fabledassistant.services.auth import (
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.config import Config
|
||||
from scribe.rate_limit import is_rate_limited
|
||||
from scribe.services.auth import (
|
||||
authenticate,
|
||||
change_password,
|
||||
create_password_reset_token,
|
||||
@@ -26,14 +26,14 @@ from fabledassistant.services.auth import (
|
||||
validate_invitation_token,
|
||||
verify_password,
|
||||
)
|
||||
from fabledassistant.services.logging import log_audit
|
||||
from fabledassistant.services.notifications import (
|
||||
from scribe.services.logging import log_audit
|
||||
from scribe.services.notifications import (
|
||||
notify_security_event,
|
||||
send_password_reset_email,
|
||||
send_password_reset_success_email,
|
||||
)
|
||||
from fabledassistant.services.email import get_base_url, is_smtp_configured
|
||||
from fabledassistant.services.oauth import (
|
||||
from scribe.services.email import get_base_url, is_smtp_configured
|
||||
from scribe.services.oauth import (
|
||||
build_auth_url,
|
||||
exchange_code,
|
||||
get_userinfo,
|
||||
@@ -1,8 +1,8 @@
|
||||
"""Dashboard REST endpoint — the aggregated landing payload."""
|
||||
from quart import Blueprint, g, jsonify
|
||||
|
||||
from fabledassistant.auth import login_required
|
||||
from fabledassistant.services.dashboard import build_dashboard
|
||||
from scribe.auth import login_required
|
||||
from scribe.services.dashboard import build_dashboard
|
||||
|
||||
dashboard_bp = Blueprint("dashboard", __name__, url_prefix="/api/dashboard")
|
||||
|
||||
@@ -5,8 +5,8 @@ from datetime import datetime, timezone
|
||||
|
||||
from quart import Blueprint, g, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required
|
||||
import fabledassistant.services.events as events_svc
|
||||
from scribe.auth import login_required
|
||||
import scribe.services.events as events_svc
|
||||
|
||||
events_bp = Blueprint("events", __name__, url_prefix="/api/events")
|
||||
|
||||
@@ -126,7 +126,7 @@ async def update_event(event_id: int):
|
||||
@events_bp.delete("/<int:event_id>")
|
||||
@login_required
|
||||
async def delete_event(event_id: int):
|
||||
from fabledassistant.services.trash import delete as trash_delete
|
||||
from scribe.services.trash import delete as trash_delete
|
||||
batch = await trash_delete(_get_current_user_id(), "event", event_id)
|
||||
if batch is None:
|
||||
return jsonify({"error": "Event not found"}), 404
|
||||
@@ -137,6 +137,6 @@ async def delete_event(event_id: int):
|
||||
@login_required
|
||||
async def sync_caldav():
|
||||
"""Trigger a CalDAV pull sync for the current user."""
|
||||
from fabledassistant.services.caldav_sync import sync_user_events
|
||||
from scribe.services.caldav_sync import sync_user_events
|
||||
result = await sync_user_events(user_id=_get_current_user_id())
|
||||
return jsonify(result)
|
||||
@@ -6,9 +6,9 @@ from datetime import datetime, timezone
|
||||
|
||||
from quart import Blueprint, Response, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
from sqlalchemy import select
|
||||
|
||||
export_bp = Blueprint("export", __name__)
|
||||
@@ -83,7 +83,7 @@ async def export_data():
|
||||
status=200,
|
||||
headers={
|
||||
"Content-Type": "application/json; charset=utf-8",
|
||||
"Content-Disposition": f'attachment; filename="fabledassistant-{stamp}.json"',
|
||||
"Content-Disposition": f'attachment; filename="scribe-{stamp}.json"',
|
||||
},
|
||||
)
|
||||
|
||||
@@ -112,6 +112,6 @@ async def export_data():
|
||||
status=200,
|
||||
headers={
|
||||
"Content-Type": "application/zip",
|
||||
"Content-Disposition": f'attachment; filename="fabledassistant-{stamp}.zip"',
|
||||
"Content-Disposition": f'attachment; filename="scribe-{stamp}.zip"',
|
||||
},
|
||||
)
|
||||
@@ -2,9 +2,9 @@ import asyncio
|
||||
|
||||
from quart import Blueprint, g, jsonify, request
|
||||
|
||||
from fabledassistant.auth import get_current_user_id, login_required
|
||||
from fabledassistant.services import groups as group_svc
|
||||
from fabledassistant.services.notifications import notify_group_added
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.services import groups as group_svc
|
||||
from scribe.services.notifications import notify_group_added
|
||||
|
||||
groups_bp = Blueprint("groups", __name__, url_prefix="/api/groups")
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import get_current_user_id, login_required
|
||||
from fabledassistant.services.notifications import (
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.services.notifications import (
|
||||
list_in_app_notifications,
|
||||
mark_all_notifications_read,
|
||||
mark_notification_read,
|
||||
@@ -3,8 +3,8 @@ import logging
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import get_current_user_id, login_required
|
||||
from fabledassistant.routes.utils import parse_pagination
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.routes.utils import parse_pagination
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -42,7 +42,7 @@ async def list_knowledge():
|
||||
limit, offset = parse_pagination(default_limit=24, max_limit=100)
|
||||
page = max(1, int(request.args.get("page", 1)))
|
||||
|
||||
from fabledassistant.services.knowledge import query_knowledge
|
||||
from scribe.services.knowledge import query_knowledge
|
||||
items, total = await query_knowledge(
|
||||
user_id=uid,
|
||||
note_type=note_type,
|
||||
@@ -88,7 +88,7 @@ async def list_knowledge_ids():
|
||||
if note_type and note_type not in _VALID_TYPES:
|
||||
return jsonify({"error": "Invalid type"}), 400
|
||||
|
||||
from fabledassistant.services.knowledge import query_knowledge_ids
|
||||
from scribe.services.knowledge import query_knowledge_ids
|
||||
ids, total = await query_knowledge_ids(
|
||||
user_id=uid, note_type=note_type, tags=tags,
|
||||
sort=sort, q=q, limit=limit, offset=offset,
|
||||
@@ -114,7 +114,7 @@ async def get_knowledge_batch():
|
||||
if len(ids) > 100:
|
||||
return jsonify({"error": "Too many IDs (max 100)"}), 400
|
||||
|
||||
from fabledassistant.services.knowledge import get_knowledge_by_ids
|
||||
from scribe.services.knowledge import get_knowledge_by_ids
|
||||
items = await get_knowledge_by_ids(uid, ids)
|
||||
return jsonify({"items": items})
|
||||
|
||||
@@ -126,7 +126,7 @@ async def list_knowledge_tags():
|
||||
uid = get_current_user_id()
|
||||
note_type = request.args.get("type", "").strip().lower() or None
|
||||
|
||||
from fabledassistant.services.knowledge import get_knowledge_tags
|
||||
from scribe.services.knowledge import get_knowledge_tags
|
||||
tags = await get_knowledge_tags(uid, note_type=note_type)
|
||||
return jsonify({"tags": tags})
|
||||
|
||||
@@ -139,6 +139,6 @@ async def get_knowledge_counts():
|
||||
tags_raw = request.args.get("tags", "").strip()
|
||||
tags = [t.strip() for t in tags_raw.split(",") if t.strip()] if tags_raw else None
|
||||
|
||||
from fabledassistant.services.knowledge import get_knowledge_counts as _counts
|
||||
from scribe.services.knowledge import get_knowledge_counts as _counts
|
||||
counts = await _counts(uid, tags=tags)
|
||||
return jsonify(counts)
|
||||
@@ -3,10 +3,10 @@ import logging
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.routes.utils import not_found, parse_pagination
|
||||
from fabledassistant.services.access import can_write_project
|
||||
from fabledassistant.services.milestones import (
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.routes.utils import not_found, parse_pagination
|
||||
from scribe.services.access import can_write_project
|
||||
from scribe.services.milestones import (
|
||||
create_milestone,
|
||||
delete_milestone,
|
||||
get_milestone_in_project,
|
||||
@@ -14,8 +14,8 @@ from fabledassistant.services.milestones import (
|
||||
list_milestones,
|
||||
update_milestone,
|
||||
)
|
||||
from fabledassistant.services.notes import list_notes
|
||||
from fabledassistant.services.projects import get_project_for_user
|
||||
from scribe.services.notes import list_notes
|
||||
from scribe.services.projects import get_project_for_user
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -113,7 +113,7 @@ async def delete_milestone_route(project_id: int, milestone_id: int):
|
||||
milestone = await get_milestone_in_project(project_id, milestone_id)
|
||||
if milestone is None:
|
||||
return not_found("Milestone")
|
||||
from fabledassistant.services.trash import delete as trash_delete
|
||||
from scribe.services.trash import delete as trash_delete
|
||||
batch = await trash_delete(milestone.user_id, "milestone", milestone_id)
|
||||
if batch is None:
|
||||
return not_found("Milestone")
|
||||
@@ -2,14 +2,14 @@ import asyncio
|
||||
import logging
|
||||
import re
|
||||
|
||||
from fabledassistant.services.embeddings import upsert_note_embedding
|
||||
from scribe.services.embeddings import upsert_note_embedding
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.routes.utils import not_found, parse_iso_date, parse_pagination
|
||||
from fabledassistant.services.access import can_write_note
|
||||
from fabledassistant.services.notes import (
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.routes.utils import not_found, parse_iso_date, parse_pagination
|
||||
from scribe.services.access import can_write_note
|
||||
from scribe.services.notes import (
|
||||
build_note_graph,
|
||||
convert_note_to_task,
|
||||
convert_task_to_note,
|
||||
@@ -23,8 +23,8 @@ from fabledassistant.services.notes import (
|
||||
list_notes,
|
||||
update_note,
|
||||
)
|
||||
from fabledassistant.services.note_drafts import upsert_draft, get_draft, delete_draft
|
||||
from fabledassistant.services.note_versions import list_versions, get_version
|
||||
from scribe.services.note_drafts import upsert_draft, get_draft, delete_draft
|
||||
from scribe.services.note_versions import list_versions, get_version
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -89,7 +89,7 @@ async def create_note_route():
|
||||
|
||||
project_id = data.get("project_id")
|
||||
if project_id is None and data.get("project"):
|
||||
from fabledassistant.services.projects import get_project_by_title as _gpbt
|
||||
from scribe.services.projects import get_project_by_title as _gpbt
|
||||
proj = await _gpbt(uid, data["project"])
|
||||
if proj:
|
||||
project_id = proj.id
|
||||
@@ -282,7 +282,7 @@ async def delete_note_route(note_id: int):
|
||||
note_obj, _ = result
|
||||
if not await can_write_note(uid, note_id):
|
||||
return jsonify({"error": "Permission denied"}), 403
|
||||
from fabledassistant.services.trash import delete as trash_delete
|
||||
from scribe.services.trash import delete as trash_delete
|
||||
batch = await trash_delete(note_obj.user_id, "note", note_id)
|
||||
if batch is None:
|
||||
return not_found("Note")
|
||||
@@ -445,7 +445,7 @@ async def pin_version_route(note_id: int, version_id: int):
|
||||
label = data.get("label")
|
||||
if label is not None and not isinstance(label, str):
|
||||
return jsonify({"error": "label must be a string or null"}), 400
|
||||
from fabledassistant.services.version_pinning import pin_version
|
||||
from scribe.services.version_pinning import pin_version
|
||||
try:
|
||||
version = await pin_version(uid, note_id, version_id, label=label)
|
||||
except ValueError as e:
|
||||
@@ -462,7 +462,7 @@ async def pin_version_route(note_id: int, version_id: int):
|
||||
async def unpin_version_route(note_id: int, version_id: int):
|
||||
"""Downgrade a manually-pinned version back to rolling."""
|
||||
uid = get_current_user_id()
|
||||
from fabledassistant.services.version_pinning import unpin_version
|
||||
from scribe.services.version_pinning import unpin_version
|
||||
version = await unpin_version(uid, note_id, version_id)
|
||||
if version is None:
|
||||
return not_found("Version")
|
||||
@@ -1,7 +1,7 @@
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import get_current_user_id, login_required
|
||||
from fabledassistant.services.user_profile import (
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.services.user_profile import (
|
||||
VALID_EXPERTISE,
|
||||
VALID_STYLES,
|
||||
VALID_TONES,
|
||||
@@ -4,11 +4,11 @@ import logging
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.routes.utils import not_found, parse_pagination
|
||||
from fabledassistant.services.milestones import list_milestones
|
||||
from fabledassistant.services.notes import list_notes
|
||||
from fabledassistant.services.projects import (
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.routes.utils import not_found, parse_pagination
|
||||
from scribe.services.milestones import list_milestones
|
||||
from scribe.services.notes import list_notes
|
||||
from scribe.services.projects import (
|
||||
create_project,
|
||||
delete_project,
|
||||
get_project,
|
||||
@@ -101,7 +101,7 @@ async def update_project_route(project_id: int):
|
||||
@login_required
|
||||
async def delete_project_route(project_id: int):
|
||||
uid = get_current_user_id()
|
||||
from fabledassistant.services.trash import delete as trash_delete
|
||||
from scribe.services.trash import delete as trash_delete
|
||||
batch = await trash_delete(uid, "project", project_id)
|
||||
if batch is None:
|
||||
return not_found("Project")
|
||||
@@ -7,9 +7,9 @@ from __future__ import annotations
|
||||
|
||||
from quart import Blueprint, g, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required
|
||||
import fabledassistant.services.rulebooks as rulebooks_svc
|
||||
from fabledassistant.services.trash import delete as trash_delete
|
||||
from scribe.auth import login_required
|
||||
import scribe.services.rulebooks as rulebooks_svc
|
||||
from scribe.services.trash import delete as trash_delete
|
||||
|
||||
rulebooks_bp = Blueprint("rulebooks", __name__, url_prefix="/api")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.services.embeddings import semantic_search_notes
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.services.embeddings import semantic_search_notes
|
||||
|
||||
search_bp = Blueprint("search", __name__, url_prefix="/api/search")
|
||||
|
||||
@@ -10,10 +10,10 @@ from urllib.parse import urlparse
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.services.caldav import CALDAV_SETTING_KEYS, get_caldav_config, test_connection
|
||||
from fabledassistant.services.settings import delete_setting, get_all_settings, get_setting, set_settings_batch
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.config import Config
|
||||
from scribe.services.caldav import CALDAV_SETTING_KEYS, get_caldav_config, test_connection
|
||||
from scribe.services.settings import delete_setting, get_all_settings, get_setting, set_settings_batch
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -2,11 +2,11 @@ import asyncio
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import get_current_user_id, login_required
|
||||
from fabledassistant.services import sharing as share_svc
|
||||
from fabledassistant.services.access import can_admin_project, can_write_note
|
||||
from fabledassistant.services.notifications import notify_note_shared, notify_project_shared
|
||||
from fabledassistant.services.sharing import list_shared_with_me
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.services import sharing as share_svc
|
||||
from scribe.services.access import can_admin_project, can_write_note
|
||||
from scribe.services.notifications import notify_note_shared, notify_project_shared
|
||||
from scribe.services.sharing import list_shared_with_me
|
||||
|
||||
shares_bp = Blueprint("shares", __name__)
|
||||
|
||||
@@ -3,8 +3,8 @@ import logging
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import get_current_user_id, login_required
|
||||
from fabledassistant.services.task_logs import create_log, delete_log, list_logs, update_log
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.services.task_logs import create_log, delete_log, list_logs, update_log
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -49,7 +49,7 @@ async def update_log_route(task_id: int, log_id: int):
|
||||
if content is not None:
|
||||
content = content.strip() or None
|
||||
|
||||
from fabledassistant.services.task_logs import _UNSET
|
||||
from scribe.services.task_logs import _UNSET
|
||||
duration_minutes = data.get("duration_minutes", _UNSET)
|
||||
|
||||
log = await update_log(uid, log_id, content=content, duration_minutes=duration_minutes)
|
||||
@@ -3,20 +3,20 @@ from datetime import date
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.models.note import TaskPriority, TaskStatus
|
||||
from fabledassistant.routes.utils import not_found, parse_iso_date, parse_pagination
|
||||
from fabledassistant.services.access import can_write_note
|
||||
from fabledassistant.services.embeddings import upsert_note_embedding
|
||||
from fabledassistant.services.notes import (
|
||||
from scribe.auth import login_required, get_current_user_id
|
||||
from scribe.models.note import TaskPriority, TaskStatus
|
||||
from scribe.routes.utils import not_found, parse_iso_date, parse_pagination
|
||||
from scribe.services.access import can_write_note
|
||||
from scribe.services.embeddings import upsert_note_embedding
|
||||
from scribe.services.notes import (
|
||||
create_note,
|
||||
get_note,
|
||||
get_note_for_user,
|
||||
list_notes,
|
||||
update_note,
|
||||
)
|
||||
from fabledassistant.services.planning import start_planning as svc_start_planning
|
||||
from fabledassistant.services.recurrence import calculate_next_due, validate_recurrence_rule
|
||||
from scribe.services.planning import start_planning as svc_start_planning
|
||||
from scribe.services.recurrence import calculate_next_due, validate_recurrence_rule
|
||||
|
||||
tasks_bp = Blueprint("tasks", __name__, url_prefix="/api/tasks")
|
||||
|
||||
@@ -117,7 +117,7 @@ async def create_task_route():
|
||||
|
||||
project_id = data.get("project_id")
|
||||
if project_id is None and data.get("project"):
|
||||
from fabledassistant.services.projects import get_project_by_title as _gpbt
|
||||
from scribe.services.projects import get_project_by_title as _gpbt
|
||||
proj = await _gpbt(uid, data["project"])
|
||||
if proj:
|
||||
project_id = proj.id
|
||||
@@ -299,7 +299,7 @@ async def delete_task_route(task_id: int):
|
||||
task_note, _ = result
|
||||
if not await can_write_note(uid, task_id):
|
||||
return jsonify({"error": "Permission denied"}), 403
|
||||
from fabledassistant.services.trash import delete as trash_delete
|
||||
from scribe.services.trash import delete as trash_delete
|
||||
batch = await trash_delete(task_note.user_id, "task", task_id)
|
||||
if batch is None:
|
||||
return not_found("Task")
|
||||
@@ -3,8 +3,8 @@ from __future__ import annotations
|
||||
|
||||
from quart import Blueprint, g, jsonify
|
||||
|
||||
from fabledassistant.auth import login_required
|
||||
import fabledassistant.services.trash as trash_svc
|
||||
from scribe.auth import login_required
|
||||
import scribe.services.trash as trash_svc
|
||||
|
||||
trash_bp = Blueprint("trash", __name__, url_prefix="/api/trash")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from quart import Blueprint, jsonify, request
|
||||
from sqlalchemy import or_, select
|
||||
|
||||
from fabledassistant.auth import get_current_user_id, login_required
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.models import async_session
|
||||
from scribe.models.user import User
|
||||
|
||||
users_bp = Blueprint("users", __name__, url_prefix="/api/users")
|
||||
|
||||
@@ -11,11 +11,11 @@ import logging
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.group import GroupMembership
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.project import Project
|
||||
from fabledassistant.models.share import NoteShare, ProjectShare
|
||||
from scribe.models import async_session
|
||||
from scribe.models.group import GroupMembership
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.project import Project
|
||||
from scribe.models.share import NoteShare, ProjectShare
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -4,8 +4,8 @@ from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.api_key import ApiKey
|
||||
from scribe.models import async_session
|
||||
from scribe.models.api_key import ApiKey
|
||||
|
||||
|
||||
def generate_key() -> str:
|
||||
@@ -6,12 +6,12 @@ from datetime import datetime, timedelta, timezone
|
||||
import bcrypt
|
||||
from sqlalchemy import func, select, update
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.invitation import InvitationToken
|
||||
from fabledassistant.models.password_reset import PasswordResetToken
|
||||
from fabledassistant.models.setting import Setting
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.invitation import InvitationToken
|
||||
from scribe.models.password_reset import PasswordResetToken
|
||||
from scribe.models.setting import Setting
|
||||
from scribe.models.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -172,7 +172,7 @@ async def delete_user(user_id: int) -> bool:
|
||||
|
||||
|
||||
async def set_registration_open(admin_user_id: int, open: bool) -> None:
|
||||
from fabledassistant.services.settings import set_setting
|
||||
from scribe.services.settings import set_setting
|
||||
await set_setting(admin_user_id, "registration_open", "true" if open else "false")
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@ from datetime import date, datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.milestone import Milestone
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.note_draft import NoteDraft
|
||||
from fabledassistant.models.note_version import NoteVersion
|
||||
from fabledassistant.models.project import Project
|
||||
from fabledassistant.models.setting import Setting
|
||||
from fabledassistant.models.task_log import TaskLog
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.models import async_session
|
||||
from scribe.models.milestone import Milestone
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.note_draft import NoteDraft
|
||||
from scribe.models.note_version import NoteVersion
|
||||
from scribe.models.project import Project
|
||||
from scribe.models.setting import Setting
|
||||
from scribe.models.task_log import TaskLog
|
||||
from scribe.models.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -8,7 +8,7 @@ from zoneinfo import ZoneInfo
|
||||
import caldav
|
||||
import icalendar
|
||||
|
||||
from fabledassistant.services.settings import get_all_settings
|
||||
from scribe.services.settings import get_all_settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -194,7 +194,7 @@ async def create_event(
|
||||
tz = timezone or config.get("caldav_timezone") or None
|
||||
|
||||
cal = icalendar.Calendar()
|
||||
cal.add("prodid", "-//FabledAssistant//EN")
|
||||
cal.add("prodid", "-//Scribe//EN")
|
||||
cal.add("version", "2.0")
|
||||
|
||||
event = icalendar.Event()
|
||||
@@ -415,7 +415,7 @@ async def update_event(
|
||||
|
||||
# Rebuild ical data and save
|
||||
cal_data = icalendar.Calendar()
|
||||
cal_data.add("prodid", "-//FabledAssistant//EN")
|
||||
cal_data.add("prodid", "-//Scribe//EN")
|
||||
cal_data.add("version", "2.0")
|
||||
cal_data.add_component(component)
|
||||
event_obj.data = cal_data.to_ical().decode("utf-8")
|
||||
@@ -690,7 +690,7 @@ async def update_todo(
|
||||
|
||||
# Rebuild ical data and save
|
||||
cal_data = icalendar.Calendar()
|
||||
cal_data.add("prodid", "-//FabledAssistant//EN")
|
||||
cal_data.add("prodid", "-//Scribe//EN")
|
||||
cal_data.add("version", "2.0")
|
||||
cal_data.add_component(component)
|
||||
todo_obj.data = cal_data.to_ical().decode("utf-8")
|
||||
@@ -13,8 +13,8 @@ from typing import Any
|
||||
|
||||
from sqlalchemy import select, update
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.event import Event
|
||||
from scribe.models import async_session
|
||||
from scribe.models.event import Event
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -112,7 +112,7 @@ async def sync_user_events(user_id: int) -> dict:
|
||||
|
||||
Returns a summary dict: {created, updated, unchanged}.
|
||||
"""
|
||||
from fabledassistant.services.caldav import get_caldav_config, is_caldav_configured # noqa: PLC0415
|
||||
from scribe.services.caldav import get_caldav_config, is_caldav_configured # noqa: PLC0415
|
||||
|
||||
if not await is_caldav_configured(user_id):
|
||||
return {"skipped": True, "reason": "CalDAV not configured"}
|
||||
@@ -234,7 +234,7 @@ async def sync_all_users() -> None:
|
||||
"""Pull CalDAV events for all users with CalDAV configured."""
|
||||
from sqlalchemy import select as sa_select # noqa: PLC0415
|
||||
|
||||
from fabledassistant.models.user import User # noqa: PLC0415
|
||||
from scribe.models.user import User # noqa: PLC0415
|
||||
|
||||
async with async_session() as session:
|
||||
result = await session.execute(sa_select(User.id))
|
||||
@@ -11,11 +11,11 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import case, func, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.project import Project
|
||||
from fabledassistant.models.milestone import Milestone
|
||||
from fabledassistant.services import milestones as milestones_svc
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.project import Project
|
||||
from scribe.models.milestone import Milestone
|
||||
from scribe.services import milestones as milestones_svc
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -145,7 +145,7 @@ async def _recently_completed(user_id: int) -> list[dict]:
|
||||
|
||||
|
||||
async def _upcoming_events(user_id: int) -> list[dict]:
|
||||
from fabledassistant.services import events as events_svc
|
||||
from scribe.services import events as events_svc
|
||||
now = datetime.now(timezone.utc)
|
||||
rows = await events_svc.list_events(user_id, now, now + timedelta(days=WINDOW_DAYS))
|
||||
out = []
|
||||
@@ -86,7 +86,7 @@ def _process_rss_mb() -> float | None:
|
||||
def _db_pool_stats() -> dict[str, Any]:
|
||||
"""Pool checked-in / checked-out / overflow. Direct from SQLAlchemy."""
|
||||
try:
|
||||
from fabledassistant.models import engine
|
||||
from scribe.models import engine
|
||||
pool = engine.pool
|
||||
# Async engines wrap a sync pool; .checkedin() / .checkedout() exist
|
||||
# on the underlying QueuePool. Attribute access is documented but
|
||||
@@ -172,7 +172,7 @@ def _setup_file_logger() -> logging.Logger:
|
||||
return _file_logger
|
||||
try:
|
||||
_DIAG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
flog = logging.getLogger("fabledassistant.diagnostics.persistent")
|
||||
flog = logging.getLogger("scribe.diagnostics.persistent")
|
||||
flog.setLevel(logging.INFO)
|
||||
flog.propagate = False # don't double-log into the root stdout stream
|
||||
# Don't re-add handlers across module reloads / re-runs.
|
||||
@@ -6,10 +6,10 @@ from email.message import EmailMessage
|
||||
import aiosmtplib
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.setting import Setting
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.config import Config
|
||||
from scribe.models import async_session
|
||||
from scribe.models.setting import Setting
|
||||
from scribe.models.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -16,9 +16,9 @@ import os
|
||||
|
||||
from sqlalchemy import delete, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.embedding import NoteEmbedding
|
||||
from fabledassistant.models.note import Note
|
||||
from scribe.models import async_session
|
||||
from scribe.models.embedding import NoteEmbedding
|
||||
from scribe.models.note import Note
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
+5
-5
@@ -19,8 +19,8 @@ from apscheduler.triggers.interval import IntervalTrigger
|
||||
from dateutil.rrule import rrulestr
|
||||
from sqlalchemy import and_, or_, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.event import Event
|
||||
from scribe.models import async_session
|
||||
from scribe.models.event import Event
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -84,7 +84,7 @@ async def _fire_reminders() -> None:
|
||||
return
|
||||
|
||||
# Deliver via the in-app notification feed (push was removed in Phase 8).
|
||||
from fabledassistant.services.notifications import create_in_app_notification
|
||||
from scribe.services.notifications import create_in_app_notification
|
||||
|
||||
async with async_session() as session:
|
||||
for event_id, occurrence_start in to_notify:
|
||||
@@ -116,7 +116,7 @@ def _run_reminders(loop: asyncio.AbstractEventLoop) -> None:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _run_caldav_sync() -> None:
|
||||
from fabledassistant.services.caldav_sync import sync_all_users # noqa: PLC0415
|
||||
from scribe.services.caldav_sync import sync_all_users # noqa: PLC0415
|
||||
try:
|
||||
await sync_all_users()
|
||||
except Exception:
|
||||
@@ -132,7 +132,7 @@ def _run_caldav_sync_threadsafe(loop: asyncio.AbstractEventLoop) -> None:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _run_recurrence_spawn() -> None:
|
||||
from fabledassistant.services.recurrence import spawn_recurring_tasks # noqa: PLC0415
|
||||
from scribe.services.recurrence import spawn_recurring_tasks # noqa: PLC0415
|
||||
try:
|
||||
await spawn_recurring_tasks()
|
||||
except Exception:
|
||||
@@ -18,8 +18,8 @@ from datetime import datetime, timedelta, timezone
|
||||
from dateutil.rrule import rrulestr
|
||||
from sqlalchemy import or_, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.event import Event
|
||||
from scribe.models import async_session
|
||||
from scribe.models.event import Event
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -76,7 +76,7 @@ async def _localize_naive(user_id: int, dt: datetime | None) -> datetime | None:
|
||||
asyncpg store the correct UTC instant, matching the REST/UI path.
|
||||
"""
|
||||
if dt is not None and dt.tzinfo is None:
|
||||
from fabledassistant.services.tz import get_user_tz # noqa: PLC0415
|
||||
from scribe.services.tz import get_user_tz # noqa: PLC0415
|
||||
return dt.replace(tzinfo=await get_user_tz(user_id))
|
||||
return dt
|
||||
|
||||
@@ -398,7 +398,7 @@ async def find_events_by_query(user_id: int, query: str) -> list[Event]:
|
||||
|
||||
async def _push_create(event: Event, user_id: int, extra: dict) -> None:
|
||||
try:
|
||||
from fabledassistant.services.caldav import (
|
||||
from scribe.services.caldav import (
|
||||
create_event as caldav_create,
|
||||
is_caldav_configured,
|
||||
)
|
||||
@@ -438,7 +438,7 @@ async def _push_update(event: Event, user_id: int, old_title: str = "") -> None:
|
||||
if not event.caldav_uid:
|
||||
return
|
||||
try:
|
||||
from fabledassistant.services.caldav import (
|
||||
from scribe.services.caldav import (
|
||||
update_event as caldav_update,
|
||||
is_caldav_configured,
|
||||
)
|
||||
@@ -466,7 +466,7 @@ async def _push_update(event: Event, user_id: int, old_title: str = "") -> None:
|
||||
async def _push_delete(caldav_uid: str, event_title: str, user_id: int) -> None:
|
||||
"""Push a delete to CalDAV. Uses event_title to locate the event by SUMMARY."""
|
||||
try:
|
||||
from fabledassistant.services.caldav import (
|
||||
from scribe.services.caldav import (
|
||||
delete_event as caldav_delete,
|
||||
is_caldav_configured,
|
||||
)
|
||||
@@ -5,9 +5,9 @@ import logging
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.group import Group, GroupMembership
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.models import async_session
|
||||
from scribe.models.group import Group, GroupMembership
|
||||
from scribe.models.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -3,8 +3,8 @@ import logging
|
||||
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -174,7 +174,7 @@ async def _semantic_knowledge_search(
|
||||
# 2. Semantic search — conceptual similarity
|
||||
semantic_notes: list[Note] = []
|
||||
try:
|
||||
from fabledassistant.services.embeddings import semantic_search_notes
|
||||
from scribe.services.embeddings import semantic_search_notes
|
||||
is_task_filter = True if note_type in ("task", "plan") else (False if note_type else None)
|
||||
candidates = await semantic_search_notes(
|
||||
user_id=user_id,
|
||||
@@ -8,9 +8,9 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import delete, func, select, text
|
||||
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.app_log import AppLog
|
||||
from scribe.config import Config
|
||||
from scribe.models import async_session
|
||||
from scribe.models.app_log import AppLog
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -4,9 +4,9 @@ from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.milestone import Milestone
|
||||
from fabledassistant.models.note import Note
|
||||
from scribe.models import async_session
|
||||
from scribe.models.milestone import Milestone
|
||||
from scribe.models.note import Note
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy import select, text
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note_draft import NoteDraft
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note_draft import NoteDraft
|
||||
|
||||
|
||||
async def upsert_draft(
|
||||
+2
-2
@@ -2,8 +2,8 @@ from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select, text
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note_version import NoteVersion
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note_version import NoteVersion
|
||||
|
||||
# Maximum snapshots retained per note.
|
||||
MAX_VERSIONS = 50
|
||||
@@ -4,8 +4,8 @@ from datetime import date, datetime, timezone
|
||||
|
||||
from sqlalchemy import func, or_, select, text
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note, TaskPriority, TaskStatus
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note, TaskPriority, TaskStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -35,7 +35,7 @@ def _strip_type_nouns(q: str) -> list[str]:
|
||||
|
||||
async def _maybe_reactivate_project(project_id: int) -> None:
|
||||
"""If a project is paused, reactivate it — activity indicates resumed work."""
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
try:
|
||||
async with async_session() as session:
|
||||
project = (await session.execute(
|
||||
@@ -82,7 +82,7 @@ async def create_note(
|
||||
|
||||
# Auto-populate project_id from milestone when not explicitly provided
|
||||
if milestone_id is not None and project_id is None:
|
||||
from fabledassistant.models.milestone import Milestone
|
||||
from scribe.models.milestone import Milestone
|
||||
async with async_session() as lookup:
|
||||
result = await lookup.execute(
|
||||
select(Milestone).where(Milestone.id == milestone_id, Milestone.user_id == user_id)
|
||||
@@ -227,7 +227,7 @@ async def list_notes(
|
||||
count_query = count_query.where(Note.project_id.is_(None))
|
||||
|
||||
if exclude_paused_projects:
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
paused_ids = (
|
||||
select(Project.id)
|
||||
.where(Project.user_id == user_id, Project.status == "paused")
|
||||
@@ -308,7 +308,7 @@ async def update_note(user_id: int, note_id: int, **fields: object) -> Note | No
|
||||
elif note.status in (TaskStatus.done.value, TaskStatus.cancelled.value):
|
||||
note.completed_at = _now
|
||||
if note.recurrence_rule:
|
||||
from fabledassistant.services.recurrence import calculate_next_due
|
||||
from scribe.services.recurrence import calculate_next_due
|
||||
base = note.due_date or _now.date()
|
||||
next_due = calculate_next_due(note.recurrence_rule, base)
|
||||
note.recurrence_next_spawn_at = datetime(
|
||||
@@ -324,7 +324,7 @@ async def update_note(user_id: int, note_id: int, **fields: object) -> Note | No
|
||||
|
||||
# Create a version snapshot when body actually changes
|
||||
if "body" in fields and fields["body"] != old_body:
|
||||
from fabledassistant.services.note_versions import create_version
|
||||
from scribe.services.note_versions import create_version
|
||||
await create_version(user_id, note_id, old_body, old_title, old_tags)
|
||||
|
||||
if note.project_id is not None:
|
||||
@@ -501,7 +501,7 @@ async def build_note_graph(
|
||||
project_ids = {n.project_id for n in notes if n.project_id is not None}
|
||||
project_colors: dict[int, str] = {}
|
||||
if project_ids:
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
async with async_session() as session:
|
||||
result = await session.execute(
|
||||
select(Project.id, Project.color).where(Project.id.in_(project_ids))
|
||||
@@ -572,7 +572,7 @@ async def get_note_for_user(
|
||||
accessing_user_id: int, note_id: int
|
||||
) -> tuple["Note", str] | None:
|
||||
"""Returns (note, permission) if user has any access, else None."""
|
||||
from fabledassistant.services.access import get_note_permission
|
||||
from scribe.services.access import get_note_permission
|
||||
perm = await get_note_permission(accessing_user_id, note_id)
|
||||
if perm is None:
|
||||
return None
|
||||
+17
-17
@@ -7,13 +7,13 @@ from datetime import date, datetime, timezone
|
||||
|
||||
from sqlalchemy import func, select, text
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.app_log import AppLog
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.setting import Setting
|
||||
from fabledassistant.models.user import User
|
||||
from fabledassistant.services.email import _email_html, is_smtp_configured, send_email
|
||||
from fabledassistant.services.logging import log_audit
|
||||
from scribe.models import async_session
|
||||
from scribe.models.app_log import AppLog
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.setting import Setting
|
||||
from scribe.models.user import User
|
||||
from scribe.services.email import _email_html, is_smtp_configured, send_email
|
||||
from scribe.services.logging import log_audit
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -245,7 +245,7 @@ async def purge_old_read_notifications(retention_days: int = _NOTIFICATION_RETEN
|
||||
"""Delete already-read in-app notifications older than retention_days."""
|
||||
from datetime import timedelta
|
||||
from sqlalchemy import delete
|
||||
from fabledassistant.models.notification import Notification
|
||||
from scribe.models.notification import Notification
|
||||
cutoff = datetime.now(timezone.utc) - timedelta(days=retention_days)
|
||||
async with async_session() as session:
|
||||
result = await session.execute(
|
||||
@@ -285,7 +285,7 @@ def start_notification_loop() -> None:
|
||||
|
||||
async def create_in_app_notification(user_id: int, notif_type: str, payload: dict):
|
||||
"""Create an in-app Notification record."""
|
||||
from fabledassistant.models.notification import Notification
|
||||
from scribe.models.notification import Notification
|
||||
async with async_session() as session:
|
||||
n = Notification(user_id=user_id, type=notif_type, payload=payload)
|
||||
session.add(n)
|
||||
@@ -308,7 +308,7 @@ async def _fire_share_email(user_id: int, subject: str, body_text: str) -> None:
|
||||
|
||||
|
||||
async def _group_member_ids(group_id: int) -> list[int]:
|
||||
from fabledassistant.models.group import GroupMembership
|
||||
from scribe.models.group import GroupMembership
|
||||
async with async_session() as session:
|
||||
rows = (await session.execute(
|
||||
select(GroupMembership.user_id).where(GroupMembership.group_id == group_id)
|
||||
@@ -323,7 +323,7 @@ async def notify_project_shared(
|
||||
target_user_id: int | None,
|
||||
target_group_id: int | None,
|
||||
) -> None:
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
async with async_session() as session:
|
||||
project = await session.get(Project, project_id)
|
||||
inviter = await session.get(User, invited_by_user_id)
|
||||
@@ -357,7 +357,7 @@ async def notify_note_shared(
|
||||
target_user_id: int | None,
|
||||
target_group_id: int | None,
|
||||
) -> None:
|
||||
from fabledassistant.models.note import Note
|
||||
from scribe.models.note import Note
|
||||
async with async_session() as session:
|
||||
note = await session.get(Note, note_id)
|
||||
inviter = await session.get(User, invited_by_user_id)
|
||||
@@ -388,7 +388,7 @@ async def notify_note_shared(
|
||||
async def notify_group_added(
|
||||
group_id: int, role: str, invited_by_user_id: int, target_user_id: int
|
||||
) -> None:
|
||||
from fabledassistant.models.group import Group
|
||||
from scribe.models.group import Group
|
||||
async with async_session() as session:
|
||||
group = await session.get(Group, group_id)
|
||||
inviter = await session.get(User, invited_by_user_id)
|
||||
@@ -409,7 +409,7 @@ async def notify_group_added(
|
||||
|
||||
|
||||
async def list_in_app_notifications(user_id: int, unread_only: bool = True) -> list[dict]:
|
||||
from fabledassistant.models.notification import Notification
|
||||
from scribe.models.notification import Notification
|
||||
async with async_session() as session:
|
||||
q = select(Notification).where(Notification.user_id == user_id)
|
||||
if unread_only:
|
||||
@@ -420,7 +420,7 @@ async def list_in_app_notifications(user_id: int, unread_only: bool = True) -> l
|
||||
|
||||
|
||||
async def unread_notification_count(user_id: int) -> int:
|
||||
from fabledassistant.models.notification import Notification
|
||||
from scribe.models.notification import Notification
|
||||
async with async_session() as session:
|
||||
result = await session.execute(
|
||||
select(func.count()).where(
|
||||
@@ -432,7 +432,7 @@ async def unread_notification_count(user_id: int) -> int:
|
||||
|
||||
|
||||
async def mark_notification_read(user_id: int, notification_id: int) -> bool:
|
||||
from fabledassistant.models.notification import Notification
|
||||
from scribe.models.notification import Notification
|
||||
from datetime import timezone as tz
|
||||
async with async_session() as session:
|
||||
n = (await session.execute(
|
||||
@@ -450,7 +450,7 @@ async def mark_notification_read(user_id: int, notification_id: int) -> bool:
|
||||
|
||||
|
||||
async def mark_all_notifications_read(user_id: int) -> int:
|
||||
from fabledassistant.models.notification import Notification
|
||||
from scribe.models.notification import Notification
|
||||
from datetime import datetime, timezone as tz
|
||||
from sqlalchemy import update as sa_update
|
||||
async with async_session() as session:
|
||||
@@ -3,9 +3,9 @@ import urllib.parse
|
||||
|
||||
import httpx
|
||||
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.config import Config
|
||||
from scribe.models import async_session
|
||||
from scribe.models.user import User
|
||||
from sqlalchemy import select
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -4,9 +4,9 @@ in Scribe and rules surface at the planning moment.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fabledassistant.services import notes as notes_svc
|
||||
from fabledassistant.services import projects as projects_svc
|
||||
from fabledassistant.services import rulebooks as rulebooks_svc
|
||||
from scribe.services import notes as notes_svc
|
||||
from scribe.services import projects as projects_svc
|
||||
from scribe.services import rulebooks as rulebooks_svc
|
||||
|
||||
PLAN_TEMPLATE = """## Goal
|
||||
|
||||
@@ -4,9 +4,9 @@ from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import func, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.project import Project, ProjectStatus
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.project import Project, ProjectStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -167,7 +167,7 @@ async def get_project_summary(user_id: int, project_id: int) -> dict:
|
||||
)
|
||||
)
|
||||
|
||||
from fabledassistant.services.milestones import get_project_milestone_summary
|
||||
from scribe.services.milestones import get_project_milestone_summary
|
||||
milestone_summary = await get_project_milestone_summary(user_id, project_id)
|
||||
|
||||
return {
|
||||
@@ -186,7 +186,7 @@ async def get_project_for_user(
|
||||
accessing_user_id: int, project_id: int
|
||||
) -> tuple[Project, str] | None:
|
||||
"""Returns (project, permission) if user has any access, else None."""
|
||||
from fabledassistant.services.access import get_project_permission
|
||||
from scribe.services.access import get_project_permission
|
||||
perm = await get_project_permission(accessing_user_id, project_id)
|
||||
if perm is None:
|
||||
return None
|
||||
@@ -197,9 +197,9 @@ async def get_project_for_user(
|
||||
|
||||
async def list_projects_for_user(user_id: int, status: str | None = None) -> list[dict]:
|
||||
"""Owned projects + shared projects, each dict has 'permission' field."""
|
||||
from fabledassistant.models.group import GroupMembership
|
||||
from fabledassistant.models.share import ProjectShare
|
||||
from fabledassistant.services.access import PERMISSION_RANK
|
||||
from scribe.models.group import GroupMembership
|
||||
from scribe.models.share import ProjectShare
|
||||
from scribe.services.access import PERMISSION_RANK
|
||||
|
||||
owned = await list_projects(user_id, status)
|
||||
owned_ids = {p.id for p in owned}
|
||||
@@ -6,8 +6,8 @@ from datetime import date, datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import and_, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note, TaskStatus
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note, TaskStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -103,8 +103,8 @@ async def spawn_recurring_tasks() -> int:
|
||||
|
||||
Returns the number of tasks spawned.
|
||||
"""
|
||||
from fabledassistant.services.embeddings import upsert_note_embedding
|
||||
from fabledassistant.services.notes import create_note
|
||||
from scribe.services.embeddings import upsert_note_embedding
|
||||
from scribe.services.notes import create_note
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
spawned = 0
|
||||
@@ -12,8 +12,8 @@ from typing import Optional
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.rulebook import Rulebook
|
||||
from scribe.models import async_session
|
||||
from scribe.models.rulebook import Rulebook
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -114,7 +114,7 @@ async def find_rulebook_by_title(
|
||||
|
||||
# ── Topic CRUD ──────────────────────────────────────────────────────────
|
||||
|
||||
from fabledassistant.models.rulebook import RulebookTopic
|
||||
from scribe.models.rulebook import RulebookTopic
|
||||
|
||||
|
||||
async def _assert_rulebook_owned(session, rulebook_id: int, user_id: int) -> None:
|
||||
@@ -223,7 +223,7 @@ async def delete_topic(topic_id: int, user_id: int) -> None:
|
||||
|
||||
# ── Rule CRUD ──────────────────────────────────────────────────────────
|
||||
|
||||
from fabledassistant.models.rulebook import Rule
|
||||
from scribe.models.rulebook import Rule
|
||||
|
||||
|
||||
async def _assert_topic_owned(session, topic_id: int, user_id: int) -> None:
|
||||
@@ -244,7 +244,7 @@ async def _assert_topic_owned(session, topic_id: int, user_id: int) -> None:
|
||||
|
||||
async def _assert_project_owned(session, project_id: int, user_id: int) -> None:
|
||||
"""Raise ValueError if project doesn't exist or isn't owned by user."""
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
result = await session.execute(
|
||||
select(Project).where(
|
||||
Project.id == project_id,
|
||||
@@ -263,7 +263,7 @@ async def _assert_rulebook_rule_owned(session, rule_id: int, user_id: int) -> No
|
||||
suppressible — they belong to the project; delete them instead. This
|
||||
helper deliberately excludes them.
|
||||
"""
|
||||
from fabledassistant.models.rulebook import Rule
|
||||
from scribe.models.rulebook import Rule
|
||||
result = await session.execute(
|
||||
select(Rule)
|
||||
.join(RulebookTopic, Rule.topic_id == RulebookTopic.id)
|
||||
@@ -341,7 +341,7 @@ async def list_rules(
|
||||
returned — adding all of a user's project-scoped rules unprompted would
|
||||
surprise existing callers.
|
||||
"""
|
||||
from fabledassistant.models.rulebook import project_rulebook_subscriptions
|
||||
from scribe.models.rulebook import project_rulebook_subscriptions
|
||||
|
||||
async with async_session() as session:
|
||||
stmt = (
|
||||
@@ -378,7 +378,7 @@ async def list_rules(
|
||||
|
||||
# Project-scoped rules (topic_id IS NULL, project_id matches).
|
||||
# Verifies ownership by joining Project on user_id.
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
proj_stmt = (
|
||||
select(Rule)
|
||||
.join(Project, Rule.project_id == Project.id)
|
||||
@@ -426,7 +426,7 @@ async def _fetch_owned_rule(session, rule_id: int, user_id: int) -> Optional[Rul
|
||||
(via topic) or its project (via project_id). Honors soft-delete.
|
||||
Returns None when not found or not owned.
|
||||
"""
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
|
||||
# Path A — rulebook rule.
|
||||
rulebook_rule = (await session.execute(
|
||||
@@ -495,7 +495,7 @@ async def subscribe_project(
|
||||
project_id: int, rulebook_id: int, user_id: int,
|
||||
) -> None:
|
||||
"""Add a subscription. Idempotent — duplicates raise; we swallow."""
|
||||
from fabledassistant.models.rulebook import project_rulebook_subscriptions
|
||||
from scribe.models.rulebook import project_rulebook_subscriptions
|
||||
|
||||
async with async_session() as session:
|
||||
await _assert_project_owned(session, project_id, user_id)
|
||||
@@ -515,7 +515,7 @@ async def subscribe_project(
|
||||
async def unsubscribe_project(
|
||||
project_id: int, rulebook_id: int, user_id: int,
|
||||
) -> None:
|
||||
from fabledassistant.models.rulebook import project_rulebook_subscriptions
|
||||
from scribe.models.rulebook import project_rulebook_subscriptions
|
||||
|
||||
async with async_session() as session:
|
||||
await _assert_project_owned(session, project_id, user_id)
|
||||
@@ -535,7 +535,7 @@ async def suppress_rule_for_project(
|
||||
project_id: int, rule_id: int, user_id: int,
|
||||
) -> None:
|
||||
"""Mute one rulebook rule for one project. Idempotent."""
|
||||
from fabledassistant.models.rulebook import project_rule_suppressions
|
||||
from scribe.models.rulebook import project_rule_suppressions
|
||||
|
||||
async with async_session() as session:
|
||||
await _assert_project_owned(session, project_id, user_id)
|
||||
@@ -555,7 +555,7 @@ async def unsuppress_rule_for_project(
|
||||
project_id: int, rule_id: int, user_id: int,
|
||||
) -> None:
|
||||
"""Unmute one rulebook rule for one project. Idempotent."""
|
||||
from fabledassistant.models.rulebook import project_rule_suppressions
|
||||
from scribe.models.rulebook import project_rule_suppressions
|
||||
|
||||
async with async_session() as session:
|
||||
await _assert_project_owned(session, project_id, user_id)
|
||||
@@ -572,7 +572,7 @@ async def suppress_topic_for_project(
|
||||
project_id: int, topic_id: int, user_id: int,
|
||||
) -> None:
|
||||
"""Mute every rule under one topic for one project. Idempotent."""
|
||||
from fabledassistant.models.rulebook import project_topic_suppressions
|
||||
from scribe.models.rulebook import project_topic_suppressions
|
||||
|
||||
async with async_session() as session:
|
||||
await _assert_project_owned(session, project_id, user_id)
|
||||
@@ -592,7 +592,7 @@ async def unsuppress_topic_for_project(
|
||||
project_id: int, topic_id: int, user_id: int,
|
||||
) -> None:
|
||||
"""Unmute a topic for one project. Idempotent."""
|
||||
from fabledassistant.models.rulebook import project_topic_suppressions
|
||||
from scribe.models.rulebook import project_topic_suppressions
|
||||
|
||||
async with async_session() as session:
|
||||
await _assert_project_owned(session, project_id, user_id)
|
||||
@@ -633,7 +633,7 @@ async def get_applicable_rules(
|
||||
titles + rulebook context callers need to display what was filtered
|
||||
without round-tripping for names.
|
||||
"""
|
||||
from fabledassistant.models.rulebook import (
|
||||
from scribe.models.rulebook import (
|
||||
project_rulebook_subscriptions,
|
||||
project_rule_suppressions,
|
||||
project_topic_suppressions,
|
||||
@@ -753,7 +753,7 @@ async def get_applicable_rules(
|
||||
]
|
||||
|
||||
# Project-scoped rules — verifies ownership via Project.user_id.
|
||||
from fabledassistant.models.project import Project
|
||||
from scribe.models.project import Project
|
||||
proj_rules_q = (
|
||||
select(Rule.id, Rule.title, Rule.statement)
|
||||
.join(Project, Rule.project_id == Project.id)
|
||||
@@ -2,8 +2,8 @@ import logging
|
||||
|
||||
from sqlalchemy import delete as sa_delete, select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.setting import Setting
|
||||
from scribe.models import async_session
|
||||
from scribe.models.setting import Setting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -4,12 +4,12 @@ import logging
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.group import Group, GroupMembership
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.project import Project
|
||||
from fabledassistant.models.share import NoteShare, ProjectShare
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.models import async_session
|
||||
from scribe.models.group import Group, GroupMembership
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.project import Project
|
||||
from scribe.models.share import NoteShare, ProjectShare
|
||||
from scribe.models.user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -33,7 +33,7 @@ async def _enrich_shares(session, shares) -> list[dict]:
|
||||
|
||||
def _deduplicate_by_permission(shares, id_attr: str) -> dict[int, str]:
|
||||
"""Return {resource_id: best_permission} keeping the highest-ranked permission per resource."""
|
||||
from fabledassistant.services.access import PERMISSION_RANK
|
||||
from scribe.services.access import PERMISSION_RANK
|
||||
seen: dict[int, str] = {}
|
||||
for share in shares:
|
||||
rid = getattr(share, id_attr)
|
||||
@@ -57,7 +57,7 @@ async def share_project(
|
||||
) -> ProjectShare | None:
|
||||
if permission not in VALID_PERMISSIONS:
|
||||
return None
|
||||
from fabledassistant.services.access import can_admin_project
|
||||
from scribe.services.access import can_admin_project
|
||||
if not await can_admin_project(acting_user_id, project_id):
|
||||
return None
|
||||
async with async_session() as session:
|
||||
@@ -83,7 +83,7 @@ async def update_project_share(
|
||||
share = await session.get(ProjectShare, share_id)
|
||||
if not share:
|
||||
return None
|
||||
from fabledassistant.services.access import can_admin_project
|
||||
from scribe.services.access import can_admin_project
|
||||
if not await can_admin_project(acting_user_id, share.project_id):
|
||||
return None
|
||||
share.permission = permission
|
||||
@@ -97,7 +97,7 @@ async def remove_project_share(acting_user_id: int, share_id: int) -> bool:
|
||||
share = await session.get(ProjectShare, share_id)
|
||||
if not share:
|
||||
return False
|
||||
from fabledassistant.services.access import can_admin_project
|
||||
from scribe.services.access import can_admin_project
|
||||
if not await can_admin_project(acting_user_id, share.project_id):
|
||||
return False
|
||||
await session.delete(share)
|
||||
@@ -127,7 +127,7 @@ async def share_note(
|
||||
) -> NoteShare | None:
|
||||
if permission not in VALID_PERMISSIONS:
|
||||
return None
|
||||
from fabledassistant.services.access import get_note_permission
|
||||
from scribe.services.access import get_note_permission
|
||||
perm = await get_note_permission(acting_user_id, note_id)
|
||||
if perm not in ("admin", "owner"):
|
||||
return None
|
||||
@@ -154,7 +154,7 @@ async def update_note_share(
|
||||
share = await session.get(NoteShare, share_id)
|
||||
if not share:
|
||||
return None
|
||||
from fabledassistant.services.access import get_note_permission
|
||||
from scribe.services.access import get_note_permission
|
||||
perm = await get_note_permission(acting_user_id, share.note_id)
|
||||
if perm not in ("admin", "owner"):
|
||||
return None
|
||||
@@ -169,7 +169,7 @@ async def remove_note_share(acting_user_id: int, share_id: int) -> bool:
|
||||
share = await session.get(NoteShare, share_id)
|
||||
if not share:
|
||||
return False
|
||||
from fabledassistant.services.access import get_note_permission
|
||||
from scribe.services.access import get_note_permission
|
||||
perm = await get_note_permission(acting_user_id, share.note_id)
|
||||
if perm not in ("admin", "owner"):
|
||||
return False
|
||||
@@ -4,9 +4,9 @@ from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.task_log import TaskLog
|
||||
from fabledassistant.models.note import Note
|
||||
from scribe.models import async_session
|
||||
from scribe.models.task_log import TaskLog
|
||||
from scribe.models.note import Note
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -12,12 +12,12 @@ from datetime import datetime, timezone
|
||||
|
||||
from sqlalchemy import or_, select, update
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.note import Note
|
||||
from fabledassistant.models.event import Event
|
||||
from fabledassistant.models.project import Project
|
||||
from fabledassistant.models.milestone import Milestone
|
||||
from fabledassistant.models.rulebook import Rulebook, RulebookTopic, Rule
|
||||
from scribe.models import async_session
|
||||
from scribe.models.note import Note
|
||||
from scribe.models.event import Event
|
||||
from scribe.models.project import Project
|
||||
from scribe.models.milestone import Milestone
|
||||
from scribe.models.rulebook import Rulebook, RulebookTopic, Rule
|
||||
|
||||
# entity_type -> Model. Used to resolve which table a trash op targets.
|
||||
_MODEL_FOR = {
|
||||
@@ -90,7 +90,7 @@ async def _cascade(session, user_id: int, etype: str, eid: int, batch: str, now)
|
||||
# soft-delete path keeps the project row alive; this guarantees the
|
||||
# rows are gone whether or not the project ever gets purged.
|
||||
from sqlalchemy import delete as _sql_delete
|
||||
from fabledassistant.models.rulebook import (
|
||||
from scribe.models.rulebook import (
|
||||
project_rule_suppressions, project_topic_suppressions,
|
||||
)
|
||||
await session.execute(
|
||||
@@ -169,7 +169,7 @@ async def delete(user_id: int, entity_type: str, entity_id: int) -> str | None:
|
||||
# copy lingers forever (and re-appears on any client syncing that server).
|
||||
if caldav_event:
|
||||
import asyncio
|
||||
from fabledassistant.services.events import _push_delete
|
||||
from scribe.services.events import _push_delete
|
||||
asyncio.create_task(_push_delete(caldav_event[0], caldav_event[1], user_id))
|
||||
return batch
|
||||
|
||||
+4
-4
@@ -13,8 +13,8 @@ import logging
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
from fabledassistant.services import trash as trash_svc
|
||||
from fabledassistant.services.settings import get_setting
|
||||
from scribe.services import trash as trash_svc
|
||||
from scribe.services.settings import get_setting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -32,8 +32,8 @@ def _run_purge_threadsafe() -> None:
|
||||
try:
|
||||
from sqlalchemy import select
|
||||
|
||||
from fabledassistant.models import async_session
|
||||
from fabledassistant.models.user import User
|
||||
from scribe.models import async_session
|
||||
from scribe.models.user import User
|
||||
|
||||
async with async_session() as session:
|
||||
user_ids = (await session.execute(select(User.id))).scalars().all()
|
||||
@@ -11,7 +11,7 @@ from __future__ import annotations
|
||||
from datetime import date, datetime, timedelta
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
||||
from fabledassistant.services.settings import get_setting
|
||||
from scribe.services.settings import get_setting
|
||||
|
||||
# Day-rollover boundary for journal/day-anchored views. The "day" flips at
|
||||
# this local hour (not midnight) so the 00:00–04:00 local window still
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user