feat(tags): Add database schema and tags module (v1.3.0 Phase 1)
Implements tag/category system backend following microformats2 p-category specification. Database changes: - Migration 008: Add tags and note_tags tables - Normalized tag storage (case-insensitive lookup, display name preserved) - Indexes for performance New module: - starpunk/tags.py: Tag management functions - normalize_tag: Normalize tag strings - get_or_create_tag: Get or create tag records - add_tags_to_note: Associate tags with notes (replaces existing) - get_note_tags: Retrieve note tags (alphabetically ordered) - get_tag_by_name: Lookup tag by normalized name - get_notes_by_tag: Get all notes with specific tag - parse_tag_input: Parse comma-separated tag input Model updates: - Note.tags property (lazy-loaded, prefer pre-loading in routes) - Note.to_dict() add include_tags parameter CRUD updates: - create_note() accepts tags parameter - update_note() accepts tags parameter (None = no change, [] = remove all) Micropub integration: - Pass tags to create_note() (tags already extracted by extract_tags()) - Return tags in q=source response Per design doc: docs/design/v1.3.0/microformats-tags-design.md Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -134,7 +134,11 @@ def _get_existing_slugs(db) -> set[str]:
|
||||
|
||||
|
||||
def create_note(
|
||||
content: str, published: bool = False, created_at: Optional[datetime] = None, custom_slug: Optional[str] = None
|
||||
content: str,
|
||||
published: bool = False,
|
||||
created_at: Optional[datetime] = None,
|
||||
custom_slug: Optional[str] = None,
|
||||
tags: Optional[list[str]] = None
|
||||
) -> Note:
|
||||
"""
|
||||
Create a new note
|
||||
@@ -148,6 +152,7 @@ def create_note(
|
||||
published: Whether the note should be published (default: False)
|
||||
created_at: Creation timestamp (default: current UTC time)
|
||||
custom_slug: Optional custom slug (from Micropub mp-slug property)
|
||||
tags: Optional list of tag display names (v1.3.0)
|
||||
|
||||
Returns:
|
||||
Note object with all metadata and content loaded
|
||||
@@ -294,7 +299,16 @@ def create_note(
|
||||
# Create Note object
|
||||
note = Note.from_row(row, data_dir)
|
||||
|
||||
# 9. UPDATE FTS INDEX (if available)
|
||||
# 9. ADD TAGS (v1.3.0)
|
||||
if tags:
|
||||
try:
|
||||
from starpunk.tags import add_tags_to_note
|
||||
add_tags_to_note(note_id, tags)
|
||||
except Exception as e:
|
||||
# Tag addition failure should not prevent note creation
|
||||
current_app.logger.warning(f"Failed to add tags to note {slug}: {e}")
|
||||
|
||||
# 10. UPDATE FTS INDEX (if available)
|
||||
try:
|
||||
from starpunk.search import update_fts_index, has_fts_table
|
||||
db_path = Path(current_app.config["DATABASE_PATH"])
|
||||
@@ -540,6 +554,7 @@ def update_note(
|
||||
id: Optional[int] = None,
|
||||
content: Optional[str] = None,
|
||||
published: Optional[bool] = None,
|
||||
tags: Optional[list[str]] = None
|
||||
) -> Note:
|
||||
"""
|
||||
Update a note's content and/or published status
|
||||
@@ -553,6 +568,7 @@ def update_note(
|
||||
id: Note ID to update (mutually exclusive with slug)
|
||||
content: New markdown content (None = no change)
|
||||
published: New published status (None = no change)
|
||||
tags: New tags list (None = no change, [] = remove all tags) (v1.3.0)
|
||||
|
||||
Returns:
|
||||
Updated Note object with new content and metadata
|
||||
@@ -608,8 +624,8 @@ def update_note(
|
||||
if slug is not None and id is not None:
|
||||
raise ValueError("Cannot provide both slug and id")
|
||||
|
||||
if content is None and published is None:
|
||||
raise ValueError("Must provide at least one of content or published to update")
|
||||
if content is None and published is None and tags is None:
|
||||
raise ValueError("Must provide at least one of content, published, or tags to update")
|
||||
|
||||
# Validate content if provided
|
||||
if content is not None:
|
||||
@@ -695,7 +711,16 @@ def update_note(
|
||||
f"Failed to update note: {existing_note.slug}",
|
||||
)
|
||||
|
||||
# 6. UPDATE FTS INDEX (if available and content changed)
|
||||
# 6. UPDATE TAGS (v1.3.0)
|
||||
if tags is not None:
|
||||
try:
|
||||
from starpunk.tags import add_tags_to_note
|
||||
add_tags_to_note(existing_note.id, tags)
|
||||
except Exception as e:
|
||||
# Tag update failure should not prevent note update
|
||||
current_app.logger.warning(f"Failed to update tags for note {existing_note.slug}: {e}")
|
||||
|
||||
# 7. UPDATE FTS INDEX (if available and content changed)
|
||||
if content is not None:
|
||||
try:
|
||||
from starpunk.search import update_fts_index, has_fts_table
|
||||
@@ -707,7 +732,7 @@ def update_note(
|
||||
# FTS update failure should not prevent note update
|
||||
current_app.logger.warning(f"Failed to update FTS index for note {existing_note.slug}: {e}")
|
||||
|
||||
# 7. RETURN UPDATED NOTE
|
||||
# 8. RETURN UPDATED NOTE
|
||||
updated_note = get_note(slug=existing_note.slug, load_content=True)
|
||||
|
||||
return updated_note
|
||||
|
||||
Reference in New Issue
Block a user