From 7c087d7c6168513bab1043e5de6db01e764c6844 Mon Sep 17 00:00:00 2001 From: "Jon Michael Aanes (aider)" Date: Sun, 16 Mar 2025 14:52:12 +0100 Subject: [PATCH] feat: Add WaniKani lessons fetcher to retrieve unlocked lesson datetimes --- personal_data/fetchers/wanikani_lessons.py | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 personal_data/fetchers/wanikani_lessons.py diff --git a/personal_data/fetchers/wanikani_lessons.py b/personal_data/fetchers/wanikani_lessons.py new file mode 100644 index 0000000..a104486 --- /dev/null +++ b/personal_data/fetchers/wanikani_lessons.py @@ -0,0 +1,50 @@ +import datetime +import logging +import requests +import dataclasses +from typing import Iterator, Mapping +# Import the base Scraper class; adjust the import if your code structure differs. +from personal_data.data import Scraper + +logger = logging.getLogger(__name__) + +@dataclasses.dataclass(frozen=True) +class WaniKaniLessonsFetcher(Scraper): + api_token: str + + @staticmethod + def dataset_name() -> str: + return "wanikani_lessons" + + def scrape(self) -> Iterator[Mapping[str, object]]: + """ + Fetch assignments from the WaniKani API and yield a dict for each assignment + with a non-null unlocked_at timestamp. + """ + url = "https://api.wanikani.com/v2/assignments" + headers = { + "Authorization": f"Bearer {self.api_token}", + "Wanikani-Revision": "20170710" + } + response = requests.get(url, headers=headers) + if response.status_code != 200: + logger.error("Error retrieving assignments: %s", response.text) + return + data = response.json() + # Check that 'data' key exists in the JSON response. + assignments = data.get("data", []) + for assignment in assignments: + assignment_data = assignment.get("data", {}) + # Only yield if unlocked_at is available. + unlocked_at = assignment_data.get("unlocked_at") + if unlocked_at: + # Convert unlocked_at ISO8601 string (assume 'Z' for UTC) to a datetime object. + try: + dt = datetime.datetime.fromisoformat(unlocked_at.replace("Z", "+00:00")) + except Exception as e: + logger.error("Error parsing unlocked_at '%s': %s", unlocked_at, e) + continue + yield { + "subject_id": assignment_data.get("subject_id"), + "unlocked_at": dt + }