parser_service.py•1.97 kB
"""Parser service for extracting Fibery entity references from descriptions."""
import re
from typing import List
from ..models import ParsedEntry
class ParserService:
"""Parses Toggl descriptions to extract Fibery entity references."""
# Pattern to find entity IDs (#ID)
ENTITY_PATTERN = re.compile(r"#(\d+)")
# Pattern to find metadata in brackets [VALUE]
METADATA_PATTERN = re.compile(r"\[([^\]]+)\]")
def parse_description(self, description: str) -> ParsedEntry:
"""
Parse a Toggl description to extract Fibery entity reference.
Format: "Text #ID [DATABASE] [TYPE] [PROJECT]"
Rules:
- Use rightmost #ID if multiple exist
- Extract first 3 bracket values as: [DATABASE], [TYPE], [PROJECT]
- Everything before #ID is description_clean
Args:
description: The description string from Toggl
Returns:
ParsedEntry with extracted metadata
"""
# Find all entity ID matches
matches = list(self.ENTITY_PATTERN.finditer(description))
# If no match, return unmatched
if not matches:
return ParsedEntry(description_clean=description, is_matched=False)
# Use rightmost match
match = matches[-1]
entity_id = match.group(1)
description_clean = description[: match.start()].strip()
# Extract brackets after the entity ID
text_after_id = description[match.end() :]
bracket_matches = list(self.METADATA_PATTERN.finditer(text_after_id))
brackets = [m.group(1) for m in bracket_matches]
return ParsedEntry(
description_clean=description_clean,
entity_id=entity_id,
entity_database=brackets[0] if len(brackets) > 0 else None,
entity_type=brackets[1] if len(brackets) > 1 else None,
project=brackets[2] if len(brackets) > 2 else None,
is_matched=True,
)