-
Notifications
You must be signed in to change notification settings - Fork 17
/
bookshelf.py
133 lines (106 loc) · 3.6 KB
/
bookshelf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import os
import fnmatch
import datetime
import collections
import json
from pathlib import Path
import functools
from flask import Flask, render_template, url_for, abort, send_from_directory, jsonify
from flask_frozen import Freezer
from elsa import cli
import yaml
import markdown
import markupsafe
app = Flask(__name__)
MISSING = object()
base = Path(app.root_path) / "covers"
@app.route('/data.json')
def data():
books = read_yaml('books.yml')
tags = set()
language = set()
today = datetime.date.today()
for key, value in books.items():
value['img_url'] = '/img/' + str(key) + ".jpg"
value['book_url'] = '/' + str(key)
for book in value['copies']:
if 'borrowed' in book:
book['days'] = (today - book['borrowed']).days
tags.update(value['tags'])
language.update(value['language'])
return jsonify({'books':books, 'tags':sorted(tags), 'language':sorted(language)})
@app.route('/')
def index():
books = read_yaml('books.yml')
if books is None:
abort(404)
today = datetime.date.today()
return render_template('index.html', books=books,today=today,)
@app.route('/<book_slug>/')
def book(book_slug):
books = read_yaml('books.yml')
book = books.get(book_slug)
if book is None:
abort(404)
today = datetime.date.today()
# Get the CC list (people who should be notified in the "borrow"
# issue on GitHub).
ccs = set()
for copy in book['copies']:
if 'keeper' in copy:
# For some books the owner wants a `keeper` to handle the lending;
# we don't bother the owner in that case.
ccs.add(copy['keeper'])
elif 'owner' in copy:
ccs.add(copy['owner'])
if 'current' in copy:
# Always put in the current reader
ccs.add(copy['current'])
borrow_issue_body = render_template(
'borrow_issue.md', book_slug=book_slug, book=book,
ccs=sorted(ccs),
)
return render_template(
'book.html',
book_slug=book_slug,
book=book,
today=today,
borrow_issue_body=borrow_issue_body,
)
@app.route('/info/')
def info():
readme_contents = Path(__file__).parent.joinpath('README.md').read_text()
instructions, sep, rest = readme_contents.partition('<!-- END')
html_instructions = markupsafe.Markup(markdown.markdown(instructions))
return render_template('info.html', instructions=html_instructions)
@app.route('/img/<book_slug>.jpg')
def image(book_slug):
name = find_photo(book_slug)
return send_from_directory(base, name)
def find_photo(book_slug):
name = book_slug + '.jpg'
path = base/name
if path.exists():
return name
return "python.png"
def read_yaml(filename, default=MISSING):
# Reading YAML is slow. To avoid loading all the time, we cache the result.
# To make the site react to changes in the files, we use the file size
# and last-modified time as part of the cache key. If either changes,
# the cache will be invalidated and the file will be read from disk again.
try:
info = os.stat(filename)
except FileNotFoundError:
if default is MISSING:
raise
return default
return _read_yaml_cached(filename, info.st_size, info.st_mtime)
@functools.lru_cache()
def _read_yaml_cached(filename, size, mtime):
with open(filename, encoding='utf-8') as file:
return yaml.safe_load(file)
@app.context_processor
def inject_context():
return {'today': datetime.date.today(),}
if __name__ == '__main__':
cli(app, base_url='https://books.pyvo.cz')