From 88178e5ad2cdb60b030beeb51f44c5d2217b9630 Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Fri, 10 Apr 2026 14:52:34 -0400 Subject: [PATCH] [tasks] Add auto tagging from titles --- vrobbler/apps/scrobbles/signals.py | 18 +++++++++ vrobbler/apps/scrobbles/utils.py | 61 ++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/vrobbler/apps/scrobbles/signals.py b/vrobbler/apps/scrobbles/signals.py index 054ff35..7b7e560 100644 --- a/vrobbler/apps/scrobbles/signals.py +++ b/vrobbler/apps/scrobbles/signals.py @@ -6,6 +6,7 @@ from django.utils import timezone from scrobbles.models import Scrobble from scrobbles.tasks import MEDIA_TYPES, update_charts_for_timestamp +from scrobbles.utils import tokenize_title_to_tags logger = logging.getLogger(__name__) @@ -48,3 +49,20 @@ def _update_charts_for_timestamp(user, ts): day = ts.day update_charts_for_timestamp.delay(user.id, year, month, day, week) + + +@receiver(post_save, sender=Scrobble) +def add_tags_from_task_title(sender, instance, **kwargs): + if instance.media_type != Scrobble.MediaType.TASK: + return + + title = instance.logdata.title + if not title: + return + + new_tags = tokenize_title_to_tags(title) + existing_tags = {t.name for t in instance.tags.all()} + + for tag in new_tags: + if tag not in existing_tags: + instance.tags.add(tag) diff --git a/vrobbler/apps/scrobbles/utils.py b/vrobbler/apps/scrobbles/utils.py index c3857eb..71973b2 100644 --- a/vrobbler/apps/scrobbles/utils.py +++ b/vrobbler/apps/scrobbles/utils.py @@ -453,3 +453,64 @@ def next_url_if_exists(url: str) -> str: # If it doesn’t exist return "" + + +STOPWORDS = { + "a", + "an", + "the", + "and", + "or", + "but", + "in", + "on", + "at", + "to", + "for", + "of", + "with", + "by", + "from", + "is", + "as", + "it", + "be", + "are", + "was", + "were", + "been", + "have", + "has", + "had", + "do", + "does", + "did", + "will", + "would", + "could", + "should", + "might", + "must", + "this", + "that", + "these", + "those", + "your", + "their", +} + + +def tokenize_title_to_tags(title: str) -> list[str]: + """Tokenize a title into tags, filtering common words and short tokens.""" + if not title: + return [] + + cleaned = re.sub(r"[\(\)\[\]\{\}]", "", title) + cleaned = re.sub(r"[^\w\s]", "", cleaned) + + words = [ + w.lower() + for w in cleaned.split() + if w.lower() not in STOPWORDS and len(w) > 2 + ] + return words