This commit is contained in:
Dima Gerasimov 2018-09-05 15:12:27 +04:00
commit f6a3255729
5 changed files with 298 additions and 0 deletions

178
.gitignore vendored Normal file
View file

@ -0,0 +1,178 @@
# Created by https://www.gitignore.io/api/python,emacs
### Emacs ###
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*
# eshell files
/eshell/history
/eshell/lastdir
# elpa packages
/elpa/
# reftex files
*.rel
# AUCTeX auto folder
/auto/
# cask packages
.cask/
dist/
# Flycheck
flycheck_*.el
# server auth directory
/server/
# projectiles files
.projectile
# directory configuration
.dir-locals.el
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
### Python Patch ###
.venv/
### Python.VirtualEnv Stack ###
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
pip-selfcheck.json
# End of https://www.gitignore.io/api/python,emacs

10
ci.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/bash
cd "$(this_dir)" || exit
. ~/bash_ci
ci_run mypy goodreads
ci_run pylint -E goodreads
ci_report_errors

104
goodreads/__init__.py Normal file
View file

@ -0,0 +1,104 @@
import os
from xml.dom.minidom import parseString # type: ignore
BPATH = "/L/backups/goodreads"
# TODO might be useful to keep track of updates?...
# then I need some sort of system to store diffs in generic way...
# althogh... coud use same mechanism as for filtering
def get_last() -> str:
return max(sorted([os.path.join(BPATH, f) for f in os.listdir(BPATH) if f.endswith('.xmll')]))
_SP = '</review>'
def get_reviews():
fname = get_last()
xmls = []
with open(fname, 'r') as fo:
data = fo.read()
for xx in data.split(_SP):
if len(xx.strip()) == 0:
break
xmls.append(parseString(xx + _SP))
return xmls
def get_books():
books = []
for review in get_reviews():
book_element = review.getElementsByTagName('book')[0]
title_element = book_element.getElementsByTagName('title')[0]
id_element = book_element.getElementsByTagName('id')[0]
isbn_element = book_element.getElementsByTagName('isbn')[0]
isbn13_element = book_element.getElementsByTagName('isbn13')[0]
date_added = review.getElementsByTagName('date_added')[0]
started_at = review.getElementsByTagName('started_at')[0]
read_at = review.getElementsByTagName('read_at')[0]
shelves_element = review.getElementsByTagName('shelves')[0]
book_shelves = []
for shelf in shelves_element.getElementsByTagName('shelf'):
book_shelves.append(shelf.getAttribute('name'))
book = {
'title': title_element.firstChild.data,
'id': id_element.firstChild.data,
'shelves': book_shelves
}
if isbn_element.getAttribute('nil') != 'true':
book['isbn'] = isbn_element.firstChild.data
else:
book['isbn'] = ''
if isbn13_element.getAttribute('nil') != 'true':
book['isbn13'] = isbn13_element.firstChild.data
else:
book['isbn13'] = ''
if started_at.firstChild is not None:
book['started_at'] = started_at.firstChild.data
else:
book['started_at'] = ''
if read_at.firstChild is not None:
book['read_at'] = read_at.firstChild.data
else:
book['read_at'] = ''
book['date_added'] = None if date_added.firstChild is None else date_added.firstChild.data
books.append(book)
return books
from typing import List, Dict, NamedTuple
from datetime import datetime
class Event(NamedTuple):
dt: datetime
summary: str
def _parse_date(s: str) -> datetime:
return datetime.strptime(s, "%a %b %d %H:%M:%S %z %Y")
def get_events():
events = []
for b in get_books():
added = _parse_date(b['date_added'])
title = b['title']
events.append(Event(
dt=added,
summary=f'Added book "{title}"', # TODO shelf?
))
# TODO finished? other updates?
return sorted(events, key=lambda e: e.dt)
def main():
for e in get_events():
print(e)
if __name__ == '__main__':
main()

0
goodreads/__main__.py Normal file
View file

6
run Executable file
View file

@ -0,0 +1,6 @@
#!/bin/bash
set -eu
cd "$(dirname "$0")"
python3 -m goodreads