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
10 from django.core.management.base import BaseCommand
12 from api.helpers import timestamp
13 from api.settings import MOBILE_INIT_DB
14 from catalogue.models import Book, Tag
17 class Command(BaseCommand):
18 help = 'Creates an initial SQLite file for the mobile app.'
20 def handle(self, **options):
21 # those should be versioned
22 last_checked = timestamp(datetime.now())
23 db = init_db(last_checked)
24 for b in Book.objects.all():
26 for t in Tag.objects.exclude(
27 category__in=('book', 'set', 'theme')).exclude(book_count=0):
28 # only add non-empty tags
35 def pretty_size(size):
36 """ Turns size in bytes into a prettier string.
38 >>> pretty_size(100000)
43 units = ['B', 'KiB', 'MiB', 'GiB']
46 while size > 1000 and units:
50 return "%.1f %s" % (size, unit)
51 return "%d %s" % (size, unit)
54 if not isinstance(value, unicode):
55 value = unicode(value, 'utf-8')
57 # try to replace chars
58 value = re.sub('[^a-zA-Z0-9\\s\\-]{1}', replace_char, value)
60 value = re.sub(r'[^a-z0-9{|}]+', '~', value)
62 return value.encode('ascii', 'ignore')
66 def init_db(last_checked):
67 if not os.path.isdir(MOBILE_INIT_DB):
68 os.makedirs(MOBILE_INIT_DB)
69 db = sqlite3.connect(os.path.join(MOBILE_INIT_DB, 'initial.db-%d' % last_checked))
73 id INTEGER PRIMARY KEY,
77 html_file_size INTEGER,
79 parent_number INTEGER,
86 CREATE INDEX IF NOT EXISTS book_title_index ON book (sort_key);
87 CREATE INDEX IF NOT EXISTS book_title_index ON book (title);
88 CREATE INDEX IF NOT EXISTS book_parent_index ON book (parent);
91 id INTEGER PRIMARY KEY,
96 CREATE INDEX IF NOT EXISTS tag_name_index ON tag (name);
97 CREATE INDEX IF NOT EXISTS tag_category_index ON tag (category);
98 CREATE INDEX IF NOT EXISTS tag_sort_key_index ON tag (sort_key);
100 CREATE TABLE state (last_checked INTEGER);
103 db.executescript(schema)
104 db.execute("INSERT INTO state VALUES (:last_checked)", locals())
108 def current(last_checked):
109 target = os.path.join(MOBILE_INIT_DB, 'initial.db')
110 if os.path.lexists(target):
113 'initial.db-%d' % last_checked,
121 (id, title, cover, html_file, html_file_size, parent, parent_number, sort_key, pretty_size, authors)
123 (:id, :title, :cover, :html_file, :html_file_size, :parent, :parent_number, :sort_key, :size_str, :authors);
125 book_tag_sql = "INSERT INTO book_tag (book, tag) VALUES (:book, :tag);"
128 (id, category, name, sort_key, books)
130 (:id, :category, :name, :sort_key, :book_ids);
132 categories = {'author': 'autor',
140 def add_book(db, book):
144 html_file = book.html_file.url
145 html_file_size = book.html_file.size
147 html_file = html_file_size = None
149 cover = book.cover.url
152 parent = book.parent_id
153 parent_number = book.parent_number
154 sort_key = book.sort_key
155 size_str = pretty_size(html_file_size)
156 authors = ", ".join(t.name for t in book.tags.filter(category='author'))
157 db.execute(book_sql, locals())
160 def add_tag(db, tag):
162 # category = categories[tag.category] # localized names here?
163 category = tag.category
165 sort_key = tag.sort_key
167 books = Book.tagged_top_level([tag])
168 book_ids = ','.join(str(b.id) for b in books)
169 db.execute(tag_sql, locals())