Skip to main content

Tagging and Categorization

In etchblok-test-api, tags provide a flexible way to organize bookmarks beyond fixed collections. The tagging system is built around the Tag model and the TagColor enumeration, allowing you to label, color-code, and track the popularity of different categories.

The Tag Model

A Tag is a lightweight label identified by a unique 8-character hex ID. Beyond its name, it stores visual metadata and a usage counter.

from app.models.tag import Tag, TagColor

# Create a basic tag
tag = Tag(name="Research", color=TagColor.BLUE, description="Academic papers and articles")

print(tag.id) # e.g., 'a1b2c3d4'
print(tag.usage_count) # 0

The Tag class (found in app/models/tag.py) uses a field factory to automatically generate a unique ID if one isn't provided during initialization.

Creating and Validating Tags

When you create or rename a tag, etchblok-test-api enforces several constraints to ensure data integrity and prevent conflicts with system-reserved keywords.

Validation Rules

The _validate_tag_name function in app/models/_validators.py enforces the following:

  • Length: Names must be between 1 and 50 characters.
  • Reserved Names: You cannot use names that conflict with system views: all, untagged, archived, or trash.
  • Uniqueness: While the model doesn't enforce uniqueness itself, the BookmarkService and BookmarkRepository ensure tag names are unique per user (case-insensitive).

Renaming Tags

To change a tag's name, use the rename() method. This method performs basic validation (checking for empty strings or excessive length) before updating the attribute.

tag = Tag(name="Old Name")
try:
tag.rename("New Project Name")
except ValueError as e:
print(f"Validation failed: {e}")

Visual Customization with TagColor

To help distinguish tags in a UI, etchblok-test-api provides a set of preset colors via the TagColor enum.

ColorValue
TagColor.RED"red"
TagColor.BLUE"blue"
TagColor.GREEN"green"
TagColor.YELLOW"yellow"
TagColor.PURPLE"purple"
TagColor.GRAY"gray" (Default)

You can update a tag's color by assigning a new enum value:

tag.color = TagColor.PURPLE

Tracking Tag Usage

The Tag model tracks how many bookmarks are currently associated with it via the usage_count attribute. This is managed through two explicit methods:

  • increment_usage(): Increases the count by 1.
  • decrement_usage(): Decreases the count by 1, ensuring it never drops below 0.
tag.increment_usage() # returns 1
tag.increment_usage() # returns 2
tag.decrement_usage() # returns 1

In the BookmarkService, when a tag is deleted via delete_tag(tag_id), the service automatically iterates through all bookmarks containing that tag and removes the reference before deleting the tag itself from the repository.

Serialization and Sorting

JSON Serialization

The Tag class provides to_dict() and from_dict() methods for easy conversion to and from JSON-safe formats, which is used by the API routes in app/routes/tags.py.

# Convert to dict for API response
data = tag.to_dict()
# {
# "id": "...",
# "name": "Research",
# "color": "blue",
# "description": "...",
# "usage_count": 1
# }

# Reconstruct from dict
new_tag = Tag.from_dict(data)

Alphabetical Sorting

The Tag class implements the __lt__ (less than) dunder method, which allows lists of tags to be sorted alphabetically by name (case-insensitive).

tags = [Tag(name="Zebra"), Tag(name="Apple")]
sorted_tags = sorted(tags) # [Tag(name='Apple', ...), Tag(name='Zebra', ...)]

This sorting logic is utilized in the GET /api/tags/ endpoint to provide a consistent, ordered list of labels to the user.