[tasks] Implement todoist webhooks #8479861260
This commit is contained in:
@ -27,6 +27,10 @@ from scrobbles.constants import (
|
||||
SCROBBLE_CONTENT_URLS,
|
||||
)
|
||||
from tasks.models import Task
|
||||
from vrobbler.apps.tasks.constants import (
|
||||
TODOIST_TITLE_PREFIX_LABELS,
|
||||
TODOIST_TITLE_SUFFIX_LABELS,
|
||||
)
|
||||
from webpages.models import WebPage
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -306,6 +310,54 @@ def manual_scrobble_from_url(url: str, user_id: int) -> Scrobble:
|
||||
return eval(scrobble_fn)(item_id, user_id)
|
||||
|
||||
|
||||
def todoist_scrobble_task_finish(todoist_task: dict, user_id: int) -> Scrobble:
|
||||
scrobble = Scrobble.obejcts.filter(
|
||||
user_id=user_id, logdata__todoist_id=todoist_task.get("todoist_id")
|
||||
)
|
||||
|
||||
if not scrobble.in_progress or scrobble.played_to_completion:
|
||||
logger.warning(
|
||||
"[todoist_scrobble_task_finish] todoist webhook finish called on finished task"
|
||||
)
|
||||
|
||||
scrobble.stop(force_finish=True)
|
||||
|
||||
return scrobble
|
||||
|
||||
|
||||
def todoist_scrobble_task(todoist_task: dict, user_id: int) -> Scrobble:
|
||||
|
||||
for label in todoist_task["todoist_label_list"]:
|
||||
if label in TODOIST_TITLE_PREFIX_LABELS:
|
||||
prefix = label
|
||||
if label in TODOIST_TITLE_SUFFIX_LABELS:
|
||||
suffix = label
|
||||
|
||||
title = " ".join([prefix.capitalize(), suffix.capitalize()])
|
||||
|
||||
task = Task.find_or_create(title)
|
||||
|
||||
scrobble_dict = {
|
||||
"user_id": user_id,
|
||||
"timestamp": todoist_task.get("timestamp_utc", timezone.now()),
|
||||
"playback_position_seconds": 0,
|
||||
"source": "Todoist Webhook",
|
||||
"log": todoist_task,
|
||||
}
|
||||
|
||||
logger.info(
|
||||
"[todoist_scrobble_task] task scrobble request received",
|
||||
extra={
|
||||
"task_id": task.id,
|
||||
"user_id": user_id,
|
||||
"scrobble_dict": scrobble_dict,
|
||||
"media_type": Scrobble.MediaType.TASK,
|
||||
},
|
||||
)
|
||||
scrobble = Scrobble.create_or_update(task, user_id, scrobble_dict)
|
||||
return scrobble
|
||||
|
||||
|
||||
def manual_scrobble_task(url: str, user_id: int):
|
||||
source_id = re.findall("\d+", url)[0]
|
||||
|
||||
|
||||
8
vrobbler/apps/tasks/constants.py
Normal file
8
vrobbler/apps/tasks/constants.py
Normal file
@ -0,0 +1,8 @@
|
||||
TODOIST_TITLE_PREFIX_LABELS = ["work", "home", "personal"]
|
||||
TODOIST_TITLE_SUFFIX_LABELS = [
|
||||
"project",
|
||||
"errand",
|
||||
"chores",
|
||||
"admin",
|
||||
"meeting",
|
||||
]
|
||||
@ -1,5 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from django.apps import apps
|
||||
@ -16,10 +16,14 @@ TODOIST_TASK_URL = "https://app.todoist.com/app/task/{id}"
|
||||
@dataclass
|
||||
class TaskLogData(LongPlayLogData):
|
||||
description: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
project: Optional[str] = None
|
||||
source_id: Optional[str] = None
|
||||
todoist_id: Optional[str] = None
|
||||
todoist_event: Optional[str] = None
|
||||
todoist_type: Optional[str] = None
|
||||
serial_scrobble_id: Optional[int] = None
|
||||
long_play_complete: Optional[bool] = None
|
||||
timestamp_utc: Optional[datetime] = None
|
||||
|
||||
|
||||
class Task(LongPlayScrobblableMixin):
|
||||
@ -53,7 +57,12 @@ class Task(LongPlayScrobblableMixin):
|
||||
|
||||
@classmethod
|
||||
def find_or_create(cls, title: str) -> "Task":
|
||||
return cls.objects.get_or_create(title=title)[0]
|
||||
task, created = cls.objects.get_or_create(title=title)
|
||||
if created:
|
||||
task.run_time_seconds = 1800
|
||||
task.save(update_fields=["run_time_seconds"])
|
||||
|
||||
return task
|
||||
|
||||
def scrobbles(self, user_id):
|
||||
Scrobble = apps.get_model("scrobbles", "Scrobble")
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
import logging
|
||||
|
||||
import pendulum
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from scrobbles.scrobblers import (
|
||||
todoist_scrobble_task,
|
||||
todoist_scrobble_task_finish,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -18,22 +23,47 @@ def todoist_webhook(request):
|
||||
"[todoist_webhook] called",
|
||||
extra={"post_data": post_data},
|
||||
)
|
||||
# ["inprogress","project","work"],
|
||||
# :item:updated,
|
||||
todoist_type, todoist_event = post_data.get("event_name").split(":")
|
||||
todoist_task = {
|
||||
"todoist_id": post_data.get("id"),
|
||||
"todoist_label_list": post_data.get("event_data", {}).get("labels"),
|
||||
"todoist_type": todoist_type,
|
||||
"todoist_event": todoist_event,
|
||||
"title": post_data.get("content"),
|
||||
"description": post_data.get("description"),
|
||||
"timestamp_utc": pendulum.parse(
|
||||
post_data.get("event_data", {}).get("updated_at")
|
||||
),
|
||||
}
|
||||
|
||||
# Disregard progress updates
|
||||
if in_progress and is_music:
|
||||
if todoist_task["todoist_type"] != "item" or todoist_task[
|
||||
"todoist_event"
|
||||
] not in [
|
||||
"updated",
|
||||
"completed",
|
||||
]:
|
||||
logger.info(
|
||||
"[jellyfin_webhook] ignoring update of music in progress",
|
||||
extra={"post_data": post_data},
|
||||
"[todoist_webhook] ignoring wrong todoist type or event",
|
||||
extra={
|
||||
"todoist_type": todoist_task["todoist_type"],
|
||||
"todoist_event": todoist_task["todoist_event"],
|
||||
},
|
||||
)
|
||||
return Response({}, status=status.HTTP_304_NOT_MODIFIED)
|
||||
|
||||
scrobble = todoist_scrobble_task(post_data, request.user.id)
|
||||
scrobble = None
|
||||
if "inprogress" in todoist_task["todoist_label_list"]:
|
||||
scrobble = todoist_scrobble_task(todoist_task, request.user.id)
|
||||
if "completed" in todoist_task["todoist_event"]:
|
||||
scrobble = todoist_scrobble_task_finish(todoist_task, request.user.id)
|
||||
|
||||
if not scrobble:
|
||||
return Response({}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
logger.info(
|
||||
"[jellyfin_webhook] finished",
|
||||
"[todoist_webhook] finished",
|
||||
extra={"scrobble_id": scrobble.id},
|
||||
)
|
||||
return Response({"scrobble_id": scrobble.id}, status=status.HTTP_200_OK)
|
||||
|
||||
Reference in New Issue
Block a user