diff --git a/my/config.py b/my/config.py index bdeb55a..306ce5d 100644 --- a/my/config.py +++ b/my/config.py @@ -45,6 +45,9 @@ class bluemaestro: class stackexchange: export_path: Paths = '' +class goodreads: + export_path: Paths = '' + class pinboard: export_dir: Paths = '' diff --git a/my/goodreads.py b/my/goodreads.py new file mode 100644 index 0000000..acf2bb9 --- /dev/null +++ b/my/goodreads.py @@ -0,0 +1,102 @@ +""" +[[https://www.goodreads.com][Goodreads]] statistics +""" +REQUIRES = [ + 'git+https://github.com/karlicoss/goodrexport', +] + + +from dataclasses import dataclass +from my.core import Paths +from my.config import goodreads as user_config + +@dataclass +class goodreads(user_config): + # paths[s]/glob to the exported JSON data + export_path: Paths + +from my.core.cfg import make_config, Attrs + +def _migration(attrs: Attrs) -> Attrs: + export_dir = 'export_dir' + if export_dir in attrs: # legacy name + attrs['export_path'] = attrs[export_dir] + from my.core.warnings import high + high(f'"{export_dir}" is deprecated! Please use "export_path" instead."') + return attrs +config = make_config(goodreads, migration=_migration) + +#############################3 + + +from my.core import get_files +from typing import Sequence, Iterator +from pathlib import Path + +def inputs() -> Sequence[Path]: + return get_files(config.export_path) + + +from datetime import datetime +import pytz + + +from goodrexport import dal + + +def _dal() -> dal.DAL: + return dal.DAL(inputs()) + + +def reviews() -> Iterator[dal.Review]: + return _dal().reviews() + + +# todo should be in DAL? +def books() -> Iterator[dal.Book]: + for r in reviews(): + yield r.book + + +####### +# todo ok, not sure these really belong here... + +from my.core.common import datetime_aware +@dataclass +class Event: + dt: datetime_aware + summary: str + eid: str + + +def events() -> Iterator[Event]: + for b in books(): + yield Event( + dt=b.date_added, + summary=f'Added book "{b.title}"', # todo shelf? + eid=b.id + ) + # todo finished? other updates? + + +def print_read_history() -> None: + def ddate(x): + if x is None: + return datetime.fromtimestamp(0, pytz.utc) + else: + return x + + def key(b): + return ddate(b.date_started) + + def fmtdt(dt): + if dt is None: + return dt + tz = pytz.timezone('Europe/London') + return dt.astimezone(tz) + for b in sorted(books(), key=key): + print(f""" +{b.title} by {', '.join(b.authors)} + started : {fmtdt(b.date_started)} + finished: {fmtdt(b.date_read)} + """) diff --git a/my/reading/goodreads.py b/my/reading/goodreads.py old mode 100755 new mode 100644 index 1b06c26..3c62602 --- a/my/reading/goodreads.py +++ b/my/reading/goodreads.py @@ -1,85 +1,5 @@ -#!/usr/bin/env python3 -from functools import lru_cache -from typing import NamedTuple -from datetime import datetime -import pytz +from my.core import warnings -from my.config.repos.goodrexport import dal as goodrexport -from my.config import goodreads as config +warnings.medium('my.reading.goodreads is deprecated! Use my.goodreads instead!') - -def get_model(): - sources = list(sorted(config.export_dir.glob('*.xml'))) - model = goodrexport.DAL(sources) - return model - - -def get_books(): - model = get_model() - return [r.book for r in model.reviews()] - - -def test_books(): - books = get_books() - assert len(books) > 10 - - -class Event(NamedTuple): - dt: datetime - summary: str - eid: str - - -def get_events(): - events = [] - for b in get_books(): - events.append(Event( - dt=b.date_added, - summary=f'Added book "{b.title}"', # TODO shelf? - eid=b.id - )) - # TODO finished? other updates? - return sorted(events, key=lambda e: e.dt) - - -def print_read_history(): - def ddate(x): - if x is None: - return datetime.fromtimestamp(0, pytz.utc) - else: - return x - - def key(b): - return ddate(b.date_started) - - def fmtdt(dt): - if dt is None: - return dt - tz = pytz.timezone('Europe/London') - return dt.astimezone(tz) - for b in sorted(get_books(), key=key): - print(f""" -{b.title} by {', '.join(b.authors)} - started : {fmtdt(b.date_started)} - finished: {fmtdt(b.date_read)} - """) - -def test(): - assert len(get_events()) > 20 - - -# def main(): -# import argparse -# p = argparse.ArgumentParser() -# sp = p.add_argument('mode', nargs='?') -# args = p.parse_args() - -# if args.mode == 'history': -# print_read_history() -# else: -# assert args.mode is None -# for b in iter_books(): -# print(b) - -# if __name__ == '__main__': -# main() +from ..goodreads import * diff --git a/tests/goodreads.py b/tests/goodreads.py new file mode 100644 index 0000000..9acab5c --- /dev/null +++ b/tests/goodreads.py @@ -0,0 +1,13 @@ +from .common import skip_if_not_karlicoss as pytestmark + +from more_itertools import ilen + + +def test_events() -> None: + from my.goodreads import events + assert ilen(events()) > 20 + + +def test_books() -> None: + from my.goodreads import books + assert ilen(books()) > 10 diff --git a/tox.ini b/tox.ini index 7294726..5b6f789 100644 --- a/tox.ini +++ b/tox.ini @@ -81,6 +81,7 @@ commands = hpi module install my.pinboard hpi module install my.arbtt hpi module install my.coding.commits + hpi module install my.goodreads # todo fuck. -p my.github isn't checking the subpackages?? wtf... # guess it wants .pyi file?? @@ -101,6 +102,7 @@ commands = -p my.calendar.holidays \ -p my.arbtt \ -p my.coding.commits \ + -p my.goodreads \ --txt-report .coverage.mypy-misc \ --html-report .coverage.mypy-misc \ {posargs}