S1: iso() datetime helper + one shared colour normalizer (colors.py)
M9 section S1, commit 3 — two more shared-toolkit pieces: - common.iso(dt): the "x.isoformat() if x else None" idiom (repeated 20+ times across every serializer) as one helper. Adopted in Note.serialize() and the revision serializer; other serializers adopt it in their sections. - colors.py: NOTE_COLORS (canonical, on the model) + a single normalize_color(). notes.py now imports the palette + normalizer from here and drops its local copy. labels.py's identical LABEL_COLORS/_normalize_label_color fold into this in the Organize section (S3); sync in S4. normalize_color and NOTE_COLORS remain importable from thoughtsync.notes (used by tests + sync), so nothing downstream breaks. common has no in-app imports, so the model→common→colors chain has no cycle. Behavior-preserving. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .models.note import NOTE_COLORS
|
||||||
|
|
||||||
|
# Notes and labels share one colour palette (their sets were identical). NOTE_COLORS
|
||||||
|
# is the canonical vocabulary (defined on the model); this module is the single home
|
||||||
|
# for the "clamp to the palette" normalizer so notes.py, labels.py and sync.py stop
|
||||||
|
# each carrying their own copy.
|
||||||
|
|
||||||
|
__all__ = ["NOTE_COLORS", "normalize_color"]
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_color(color: object) -> str:
|
||||||
|
"""Return `color` if it's a known palette key, else the default. One definition
|
||||||
|
for both notes and labels."""
|
||||||
|
return color if color in NOTE_COLORS else "default"
|
||||||
@@ -7,6 +7,12 @@ from datetime import datetime
|
|||||||
# definition instead of a near-identical copy per module.
|
# definition instead of a near-identical copy per module.
|
||||||
|
|
||||||
|
|
||||||
|
def iso(dt: datetime | None) -> str | None:
|
||||||
|
"""A tz-aware datetime as an ISO-8601 string, or None. Collapses the
|
||||||
|
`x.isoformat() if x else None` idiom repeated across every serializer."""
|
||||||
|
return dt.isoformat() if dt else None
|
||||||
|
|
||||||
|
|
||||||
def parse_dt(raw: object) -> datetime | None:
|
def parse_dt(raw: object) -> datetime | None:
|
||||||
"""Parse an ISO-8601 timestamp (accepting a trailing 'Z' for UTC).
|
"""Parse an ISO-8601 timestamp (accepting a trailing 'Z' for UTC).
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from sqlalchemy.dialects.postgresql import UUID
|
|||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
from . import Base
|
from . import Base
|
||||||
|
from ..common import iso
|
||||||
|
|
||||||
# The Keep-style palette. Stored as a key string, so the actual tints live in the
|
# The Keep-style palette. Stored as a key string, so the actual tints live in the
|
||||||
# frontend and can change without a schema migration.
|
# frontend and can change without a schema migration.
|
||||||
@@ -82,8 +83,8 @@ class Note(Base):
|
|||||||
"pinned": self.pinned,
|
"pinned": self.pinned,
|
||||||
"archived": self.archived,
|
"archived": self.archived,
|
||||||
"trashed": self.deleted_at is not None,
|
"trashed": self.deleted_at is not None,
|
||||||
"remind_at": self.remind_at.isoformat() if self.remind_at else None,
|
"remind_at": iso(self.remind_at),
|
||||||
"recurrence": self.recurrence,
|
"recurrence": self.recurrence,
|
||||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
"created_at": iso(self.created_at),
|
||||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
"updated_at": iso(self.updated_at),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,15 @@ from sqlalchemy import case, delete, func, literal_column, select
|
|||||||
|
|
||||||
from .acl import visible_to_user
|
from .acl import visible_to_user
|
||||||
from .auth import login_required
|
from .auth import login_required
|
||||||
from .common import coerce_bool, parse_dt
|
from .colors import NOTE_COLORS, normalize_color
|
||||||
|
from .common import coerce_bool, iso, parse_dt
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .db import session_scope
|
from .db import session_scope
|
||||||
from .responses import json_error, not_found, parse_uuid
|
from .responses import json_error, not_found, parse_uuid
|
||||||
from .settings import get_setting
|
from .settings import get_setting
|
||||||
from .unfurl import UnfurlError, unfurl
|
from .unfurl import UnfurlError, unfurl
|
||||||
from .models.label import Label, NoteLabel
|
from .models.label import Label, NoteLabel
|
||||||
from .models.note import NOTE_COLORS, Note
|
from .models.note import Note
|
||||||
from .models.note_attachment import NoteAttachment
|
from .models.note_attachment import NoteAttachment
|
||||||
from .models.note_item import NoteItem
|
from .models.note_item import NoteItem
|
||||||
from .models.note_link import NoteLink
|
from .models.note_link import NoteLink
|
||||||
@@ -101,10 +102,6 @@ def parse_list_items(raw: object) -> list[str]:
|
|||||||
return [s.strip() for s in raw if isinstance(s, str) and s.strip()]
|
return [s.strip() for s in raw if isinstance(s, str) and s.strip()]
|
||||||
|
|
||||||
|
|
||||||
def normalize_color(color: object) -> str:
|
|
||||||
return color if color in NOTE_COLORS else "default"
|
|
||||||
|
|
||||||
|
|
||||||
def apply_filter(stmt, filter_name: str):
|
def apply_filter(stmt, filter_name: str):
|
||||||
"""Narrow a notes query to one board view."""
|
"""Narrow a notes query to one board view."""
|
||||||
if filter_name == "archived":
|
if filter_name == "archived":
|
||||||
@@ -1143,7 +1140,7 @@ def _serialize_revision(rev: NoteRevision) -> dict:
|
|||||||
"id": str(rev.id),
|
"id": str(rev.id),
|
||||||
"title": rev.title,
|
"title": rev.title,
|
||||||
"body": rev.body,
|
"body": rev.body,
|
||||||
"created_at": rev.created_at.isoformat() if rev.created_at else None,
|
"created_at": iso(rev.created_at),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user