Ver código fonte

[notifications] Send mood check-in

Colin Powell 3 semanas atrás
pai
commit
c452ac24e0

+ 5 - 0
PROJECT.org

@@ -443,6 +443,11 @@ it's annoying.
 ** TODO [#C] Allow users to see tasks on calendar view :vrobbler:personal:project:templates:feature:
 https://codepen.io/oliviale/pen/QYqybo
 ** TODO [#C] Come up with a possible flow using WebDAV and super-productivity for tasks :personal:feature:project:vrobbler:tasks:
+* Version 19.0
+** DONE Add periodic check for mood :vrobbler:feature:moods:personal:project:
+:PROPERTIES:
+:ID:       55404488-c69f-0dd5-838e-1d1e15c873eb
+:END:
 * Version 18.7
 ** DONE Use the timezone history log to fix old Scrobbles that fall into those timezone blocks :vrobbler:chore:scrobbles:project:personal:
 :PROPERTIES:

+ 2 - 2
vrobbler/apps/boardgames/sources/lichess.py

@@ -3,7 +3,7 @@ from boardgames.models import BoardGame
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from scrobbles.models import Scrobble
-from scrobbles.notifications import NtfyNotification
+from scrobbles.notifications import ScrobbleNtfyNotification
 
 User = get_user_model()
 
@@ -124,5 +124,5 @@ def import_chess_games_for_all_users():
     if scrobbles_to_create:
         created = Scrobble.objects.bulk_create(scrobbles_to_create)
         for scrobble in created:
-            NtfyNotification(scrobble).send()
+            ScrobbleNtfyNotification(scrobble).send()
     return scrobbles_to_create

+ 2 - 2
vrobbler/apps/books/koreader.py

@@ -9,7 +9,7 @@ import requests
 from books.constants import BOOKS_TITLES_TO_IGNORE
 from django.apps import apps
 from django.contrib.auth import get_user_model
-from scrobbles.notifications import NtfyNotification
+from scrobbles.notifications import ScrobbleNtfyNotification
 from stream_sqlite import stream_sqlite
 from webdav.client import get_webdav_client
 
@@ -409,7 +409,7 @@ def process_koreader_sqlite_file(file_path, user_id) -> list:
     if new_scrobbles:
         created = Scrobble.objects.bulk_create(new_scrobbles)
         if created:
-            NtfyNotification(created[-1]).send()
+            ScrobbleNtfyNotification(created[-1]).send()
         fix_long_play_stats_for_scrobbles(created)
         logger.info(
             f"Created {len(created)} scrobbles",

+ 23 - 0
vrobbler/apps/profiles/migrations/0027_userprofile_mood_checkin_enabled_and_more.py

@@ -0,0 +1,23 @@
+# Generated by Django 4.2.19 on 2025-07-30 22:27
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('profiles', '0026_userprofile_timezone_change_log'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='userprofile',
+            name='mood_checkin_enabled',
+            field=models.BooleanField(default=False),
+        ),
+        migrations.AddField(
+            model_name='userprofile',
+            name='mood_checkin_frequency',
+            field=models.CharField(default='hourly', max_length=20),
+        ),
+    ]

+ 3 - 0
vrobbler/apps/profiles/models.py

@@ -56,6 +56,9 @@ class UserProfile(TimeStampedModel):
     imap_pass = EncryptedField(**BNULL)
     imap_auto_import = models.BooleanField(default=False)
 
+    mood_checkin_enabled = models.BooleanField(default=False)
+    mood_checkin_frequency = models.CharField(max_length=20, default="hourly")
+
     ntfy_url = models.CharField(max_length=255, **BNULL)
     ntfy_enabled = models.BooleanField(default=False)
 

+ 10 - 0
vrobbler/apps/scrobbles/management/commands/send_mood_checkin.py

@@ -0,0 +1,10 @@
+from django.core.management.base import BaseCommand
+from vrobbler.apps.scrobbles.utils import (
+    send_mood_checkin_reminders
+)
+
+
+class Command(BaseCommand):
+    def handle(self, *args, **options):
+        sent_count = send_mood_checkin_reminders()
+        print(f"Sent {sent_count} mood check-in notifications")

+ 2 - 2
vrobbler/apps/scrobbles/models.py

@@ -42,7 +42,7 @@ from puzzles.models import Puzzle
 from scrobbles import dataclasses as logdata
 from scrobbles.constants import LONG_PLAY_MEDIA, MEDIA_END_PADDING_SECONDS
 from scrobbles.importers.lastfm import LastFM
-from scrobbles.notifications import NtfyNotification
+from scrobbles.notifications import ScrobbleNtfyNotification
 from scrobbles.stats import build_charts
 from scrobbles.utils import get_file_md5_hash, media_class_to_foreign_key
 from sports.models import SportEvent
@@ -1316,7 +1316,7 @@ class Scrobble(TimeStampedModel):
         scrobble_data: dict,
     ) -> "Scrobble":
         scrobble = cls.objects.create(**scrobble_data)
-        NtfyNotification(scrobble).send()
+        ScrobbleNtfyNotification(scrobble).send()
         return scrobble
 
     def stop(self, timestamp=None, force_finish=False) -> None:

+ 40 - 4
vrobbler/apps/scrobbles/notifications.py

@@ -4,12 +4,24 @@ import requests
 from django.conf import settings
 from django.contrib.sites.models import Site
 
-class Notification(ABC):
-    scrobble: "Scrobble"
+class BasicNtfyNotification(ABC):
     ntfy_headers: dict = {}
     ntfy_url: str = ""
     title: str = ""
 
+    def __init__(self, profile: "UserProfile"):
+        self.user = profile.user
+        protocol = "http" if settings.DEBUG else "https"
+        domain = Site.objects.get_current().domain
+        self.url_tmpl = f'{protocol}://{domain}' + '{path}'
+
+    @abstractmethod
+    def send(self) -> None:
+        pass
+
+class ScrobbleNotification(BasicNtfyNotification):
+    scrobble: "Scrobble"
+
     def __init__(self, scrobble: "Scrobble"):
         self.scrobble = scrobble
         self.user = scrobble.user
@@ -19,13 +31,12 @@ class Notification(ABC):
         self.url_tmpl = f'{protocol}://{domain}' + '{path}'
 
 
-
     @abstractmethod
     def send(self) -> None:
         pass
 
 
-class NtfyNotification(Notification):
+class ScrobbleNtfyNotification(ScrobbleNotification):
     def __init__(self, scrobble, **kwargs):
         super().__init__(scrobble)
         self.ntfy_str: str = f"{self.scrobble.media_obj}"
@@ -55,3 +66,28 @@ class NtfyNotification(Notification):
                     "Click": self.click_url,
                 },
             )
+
+class MoodNtfyNotification(BasicNtfyNotification):
+    def __init__(self, profile, **kwargs):
+        super().__init__(scrobble)
+        self.profile = profile
+        self.ntfy_str: str = "Would you like to check in about your mood?"
+        self.click_url = self.url_tmpl.format(path=reverse("moods:mood-list"))
+        self.title = "Mood Check-in!"
+
+    def send(self):
+        if (
+            self.profile
+            and self.profile.ntfy_enabled
+            and self.profile.ntfy_url
+        ):
+            requests.post(
+                self.user.profile.ntfy_url,
+                data=self.ntfy_str.encode(encoding="utf-8"),
+                headers={
+                    "Title": self.title,
+                    "Priority": "high",
+                    "Tags": "smiley, check",
+                    "Click": self.click_url,
+                },
+            )

+ 2 - 2
vrobbler/apps/scrobbles/scrobblers.py

@@ -26,7 +26,7 @@ from scrobbles.constants import (
     SCROBBLE_CONTENT_URLS,
 )
 from scrobbles.models import Scrobble
-from scrobbles.notifications import NtfyNotification
+from scrobbles.notifications import ScrobbleNtfyNotification
 from scrobbles.utils import convert_to_seconds, extract_domain
 from sports.models import SportEvent
 from sports.thesportsdb import lookup_event_from_thesportsdb
@@ -505,7 +505,7 @@ def email_scrobble_board_game(
         scrobble.played_to_completion = True
         scrobble.save()
         scrobbles_created.append(scrobble)
-        NtfyNotification(scrobble).send()
+        ScrobbleNtfyNotification(scrobble).send()
 
     return scrobbles_created
 

+ 15 - 2
vrobbler/apps/scrobbles/utils.py

@@ -13,7 +13,7 @@ from django.utils import timezone
 from profiles.models import UserProfile
 from profiles.utils import now_user_timezone
 from scrobbles.constants import LONG_PLAY_MEDIA
-from scrobbles.notifications import NtfyNotification
+from scrobbles.notifications import MoodNtfyNotification, ScrobbleNtfyNotification
 from scrobbles.tasks import (
     process_koreader_import,
     process_lastfm_import,
@@ -318,7 +318,20 @@ def send_stop_notifications_for_in_progress_scrobbles() -> int:
         ).seconds
 
         if elapsed_scrobble_seconds > scrobble.media_obj.run_time_seconds:
-            NtfyNotification(scrobble, end=True).send()
+            ScrobbleNtfyNotification(scrobble, end=True).send()
+            notifications_sent += 1
+
+    return notifications_sent
+
+def send_mood_checkin_reminders() -> int:
+    """Get all profiles with mood check-ins enabled and checkin!"""
+    from profiles.models import UserProfile
+
+    now = timezone.now()
+    notifications_sent = 0
+    for profile in UserProfile.objects.filter(mood_checkin_enabled=True):
+        if profile.mood_checkin_period == "hourly" and now.minute == 0:
+            MoodNtfyNotification(profile).send()
             notifications_sent += 1
 
     return notifications_sent