1 # -*- coding: utf-8 -*-
2 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 from datetime import datetime
8 from django.core.management.base import BaseCommand
10 from api.helpers import timestamp
11 from api.settings import MOBILE_INIT_DB
12 from catalogue.models import Book, Tag
13 from wolnelektury.utils import makedirs
16 class Command(BaseCommand):
17 help = 'Creates an initial SQLite file for the mobile app.'
19 def handle(self, **options):
20 # those should be versioned
21 last_checked = timestamp(datetime.now())
22 db = init_db(last_checked)
23 for b in Book.objects.all():
25 for t in Tag.objects.exclude(
26 category__in=('book', 'set', 'theme')).exclude(items=None):
27 # only add non-empty tags
34 def pretty_size(size):
35 """ Turns size in bytes into a prettier string.
37 >>> pretty_size(100000)
42 units = ['B', 'KiB', 'MiB', 'GiB']
45 while size > 1000 and units:
49 return "%.1f %s" % (size, unit)
50 return "%d %s" % (size, unit)
52 # if not isinstance(value, unicode):
53 # value = unicode(value, 'utf-8')
55 # # try to replace chars
56 # value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value)
57 # value = value.lower()
58 # value = re.sub(r'[^a-z0-9{|}]+', '~', value)
60 # return value.encode('ascii', 'ignore')
63 def init_db(last_checked):
64 makedirs(MOBILE_INIT_DB)
65 db = sqlite3.connect(os.path.join(MOBILE_INIT_DB, 'initial.db-%d' % last_checked))
69 id INTEGER PRIMARY KEY,
73 html_file_size INTEGER,
75 parent_number INTEGER,
82 CREATE INDEX IF NOT EXISTS book_title_index ON book (sort_key);
83 CREATE INDEX IF NOT EXISTS book_title_index ON book (title);
84 CREATE INDEX IF NOT EXISTS book_parent_index ON book (parent);
87 id INTEGER PRIMARY KEY,
92 CREATE INDEX IF NOT EXISTS tag_name_index ON tag (name);
93 CREATE INDEX IF NOT EXISTS tag_category_index ON tag (category);
94 CREATE INDEX IF NOT EXISTS tag_sort_key_index ON tag (sort_key);
96 CREATE TABLE state (last_checked INTEGER);
99 db.executescript(schema)
100 db.execute("INSERT INTO state VALUES (:last_checked)", {'last_checked': last_checked})
104 def current(last_checked):
105 target = os.path.join(MOBILE_INIT_DB, 'initial.db')
106 if os.path.lexists(target):
109 'initial.db-%d' % last_checked,
116 (id, title, cover, html_file, html_file_size, parent, parent_number, sort_key, pretty_size, authors)
118 (:id, :title, :cover, :html_file, :html_file_size, :parent, :parent_number, :sort_key, :size_str, :authors);
120 book_tag_sql = "INSERT INTO book_tag (book, tag) VALUES (:book, :tag);"
123 (id, category, name, sort_key, books)
125 (:id, :category, :name, :sort_key, :book_ids);
127 categories = {'author': 'autor',
135 def add_book(db, book):
137 html_file = book.html_file.url
138 html_file_size = book.html_file.size
140 html_file = html_file_size = None
141 db.execute(book_sql, {
143 'cover': book.cover.url if book.cover else None,
144 'html_file': html_file,
145 'html_file_size': html_file_size,
146 'parent': book.parent_id,
147 'parent_number': book.parent_number,
148 'sort_key': book.sort_key,
149 'size_str': pretty_size(html_file_size),
150 'authors': ", ".join(t.name for t in book.tags.filter(category='author')),
154 def add_tag(db, tag):
155 books = Book.tagged_top_level([tag])
156 book_ids = ','.join(str(book_id) for book_id in books.values_list('id', flat=True))
157 db.execute(tag_sql, {
158 'category': categories[tag.category],
160 'sort_key': tag.sort_key,
161 'book_ids': book_ids,