Fix popup map panning
[wolnelektury.git] / src / catalogue / api / tojson.py
1 from collections import defaultdict
2 import json
3 import re
4 from sys import argv
5 from lxml import etree
6 from catalogue.utils import absolute_url, gallery_url
7
8 tags = {
9     'utwor': ('_pass', False, None, None, None),
10     '{http://www.w3.org/1999/02/22-rdf-syntax-ns#}RDF': ('_ignore', False, None, None, None),
11     'abstrakt': ('_ignore', False, None, None, None),
12     'uwaga': ('_ignore', False, None, None, None),
13     'extra': ('_ignore', False, None, None, None),
14     'nota_red': ('_ignore', False, None, None, None),
15     'numeracja': ('_ignore', False, None, None, None),
16
17     'powiesc': ('master', False, None, None, None),
18     'opowiadanie': ('master', False, None, None, None),
19     'liryka_lp': ('master', False, None, None, None),
20     'liryka_l': ('master', False, None, None, None),
21     'dramat_wspolczesny': ('master', False, None, None, None),
22     'dramat_wierszowany_lp': ('master', False, None, None, None),
23     'dramat_wierszowany_l': ('master', False, None, None, None),
24
25     'dlugi_cytat': ('blockquote', False, None, None, None),
26     'poezja_cyt': ('blockquote', False, None, None, None),
27     'dlugi_cyt': ('blockquote', False, None, None, None),
28     'ramka': ('blockquote', False, {'class': 'ramka'}, None, None),
29     
30     'blok': ('div', False, None, None, None),
31
32     'strofa': ('div', True, {'class': 'stanza'}, None, None),
33     'wers': ('div', True, {'class': 'verse'}, None, None),
34     'wers_wciety': ('div', True, {'class': 'wers_wciety'}, None, None),
35     'wers_cd': ('div', True, {'class': 'wers_cd'}, None, None),
36     'wers_akap': ('div', True, {'class': 'wers_akap'}, None, None),
37     'zastepnik_wersu': ('div', True, {'class': 'zastepnik_wersu'}, None, None),
38     'wers_do_prawej': ('div', True, {'class': 'wers_do_prawej'}, None, None),
39     'wers_srodek': ('div', True, {'class': 'wers_srodek'}, None, None),
40     
41     'autor_utworu': ('div', True, {'class': 'author'}, None, None),
42     'dzielo_nadrzedne': ('div', True, {'class': 'dzielo_nadrzedne'}, None, None),
43     'nazwa_utworu': ('div', True, {'class': 'title'}, None, None),
44     'podtytul': ('div', True, {'class': 'podtytul'}, None, None),
45
46     'motto': ('div', False, {'class': 'motto'}, None, None),
47     'motto_podpis': ('div', True, {'class': 'motto_podpis'}, None, None),
48     'dedykacja': ('div', True, {'class': 'dedykacja'}, None, None),
49     'miejsce_czas': ('div', True, {'class': 'miejsce_czas'}, None, None),
50     
51     'lista_osob': ('div', False, {'class': 'lista_osob'}, None, None),
52     'naglowek_listy': ('div', True, {'class': 'naglowek_listy'}, None, None),
53     'lista_osoba': ('div', True, {'class': 'lista_osoba'}, None, None),
54     'naglowek_osoba': ('div', True, {'class': 'naglowek_osoba'}, None, None),
55     'osoba': ('em', True, {'class': 'osoba'}, None, None),
56     'didaskalia': ('div', True, {'class': 'didaskalia'}, None, None),
57     'kwestia': ('div', False, {'class': 'kwestia'}, None, None),
58     'didask_tekst': ('em', True, {'class': 'didask_tekst'}, None, None),
59     
60     'naglowek_czesc': ('h2', True, None, None, None),
61     'naglowek_akt': ('h2', True, None, None, None),
62     'naglowek_scena': ('h3', True, None, None, None),
63     'naglowek_rozdzial': ('h3', True, None, None, None),
64     'naglowek_podrozdzial': ('h4', True, None, None, None),
65     'srodtytul': ('h5', True, None, None, None),
66
67     'nota': ('div', True, {'class': 'note'}, None, False),
68
69     'akap': ('p', True, {'class': 'paragraph'}, None, True),
70     'akap_dialog': ('p', True, {'class': 'paragraph'}, None, True),
71     'akap_cd': ('p', True, {'class': 'paragraph'}, None, True),
72
73     'sekcja_asterysk': ('p', True, {'class': 'spacer-asterisk'}, None, True),
74     'sekcja_swiatlo': ('p', True, {'class': 'sekcja_swiatlo'}, None, True),
75     'separator_linia': ('p', True, {'class': 'separator_linia'}, None, True),
76
77     'tytul_dziela': ('em', True, {'class': 'book-title'}, None, False),
78     'slowo_obce': ('em', True, {'class': 'foreign-word'}, None, False),
79     'wyroznienie': ('em', True, {'class': 'author-emphasis'}, None, False),
80     'wieksze_odstepy': ('em', True, {'class': 'wieksze_odstepy'}, None, False),
81
82     'ref': ('a', True, {'class': 'reference'}, {'data-uri': 'href'}, False),
83
84     'begin': ('_ignore', True, {'class': 'reference'}, {'data-uri': 'href'}, False),
85     'end': ('_ignore', True, {'class': 'reference'}, {'data-uri': 'href'}, False),
86     'motyw': ('_ignore', True, {'class': 'theme'}, None, False),
87
88     'pa': ('a', True, {'class': 'footnote footnote-pa'}, None, False),
89     'pe': ('a', True, {'class': 'footnote footnote-pe'}, None, False),
90     'pr': ('a', True, {'class': 'footnote footnote-pr'}, None, False),
91     'pt': ('a', True, {'class': 'footnote footnote-pt'}, None, False),
92     'ptrad': ('a', True, {'class': 'footnote footnote-ptrad'}, None, False),
93
94     'werset': ('p', True, {'class': 'werset'}, None, True),
95     'br': ('br', False, None, None, None),
96     'indeks_dolny': ('em', True, {'class': 'indeks_dolny'}, None, False),
97     'mat': ('span', True, {'class': 'mat'}, None, False),
98
99     'mfenced': ('math_mfenced', True, None, None, False),
100     'mfrac': ('math_mfrac', True, None, None, False),
101     'mrow': ('math_mrow', True, None, None, False),
102     'mi': ('math_mi', True, None, None, False),
103     'mn': ('math_mn', True, None, None, False),
104     'mo': ('math_mo', True, None, None, False),
105     'msup': ('math_msup', True, None, None, False),
106
107     'list': ('blockquote', False, {'class': 'list'}, None, None),
108     'wywiad_pyt': ('blockquote', False, {'class': 'wywiad_pyt'}, None, None),
109     'wywiad_odp': ('blockquote', False, {'class': 'wywiad_odp'}, None, None),
110     'rownolegle': ('blockquote', False, {'class': 'rownolegle'}, None, None),
111     'animacja': ('div', False, {'class': 'animacja'}, None, None),
112     'data': ('div', True, {'class': 'data'}, None, None),
113     'podpis': ('div', True, {'class': 'podpis'}, None, None),
114     'naglowek_listu': ('div', True, {'class': 'naglowek_listu'}, None, None),
115     'pozdrowienie': ('div', True, {'class': 'pozdrowienie'}, None, None),
116     'adresat': ('div', True, {'class': 'adresat'}, None, None),
117     'tytul_oryg': ('div', True, {'class': 'tytul_oryg'}, None, None),
118     'miejsce_data': ('div', True, {'class': 'miejsce_data'}, None, None),
119     'audio': ('_ignore', False, None, None, None),
120     'www': ('a', True, {'class': 'www'}, {'href': '.text'}, False),
121
122     'tabela': ('table', False, None, None, None),
123     'tabelka': ('table', False, None, None, None),
124     'wiersz': ('tr', False, None, None, None),
125     'kol': ('td', True, None, None, None),
126
127     'ilustr': ('img', False, None, {
128         'src': lambda e, S: absolute_url(gallery_url(S['slug'])) + e.attrib['src'].rsplit('/', 1)[-1],
129     }, False),
130     'tab': ('span', False, {'class': 'tab'}, {'szer': 'szer'}, False),
131     
132 }
133
134 id_prefixes = {
135     'pa': 'fn',
136     'pe': 'fn',
137     'pr': 'fn',
138     'pt': 'fn',
139     'ptrad': 'fn',
140     'wers': 'f',
141     'wers_wciety': 'f',
142     'wers_cd': 'f',
143     'wers_akap': 'f',
144     'zastepnik_wersu': 'f',
145     'wers_do_prawej': 'f',
146     'wers_srodek': 'f',
147     'akap': 'f',
148     'akap_cd': 'f',
149     'akap_dialog': 'f',
150 }
151
152
153 #tree = etree.parse(argv[1])
154
155 front1 = set([
156     'dzielo_nadrzedne',
157     'nazwa_utworu',
158     'podtytul',
159     ])
160 front2 = set(['autor_utworu'])
161
162
163 def norm(text):
164     text = text.replace('---', '—').replace('--', '–').replace('...', '…').replace(',,', '„').replace('"', '”').replace('\n', ' ')
165     return text
166
167
168 def toj(elem, S):
169     if elem.tag is etree.Comment: return []
170     tag, hastext, attrs, attr_map, num = tags[elem.tag]
171     contents = []
172     if tag == '_pass':
173         output = contents
174     elif tag == '_ignore':
175         return []
176     else:
177         output = {
178             'tag': tag,
179         }
180         if num:
181             S['index'] += 1
182             output['paragraphIndex'] = S['index']
183             if 'dlugi_cytat' not in S['stack'] and 'poezja_cyt' not in S['stack']:
184                 S['vindex'] += 1
185                 output['visibleNumber'] = S['vindex']
186         id_prefix = id_prefixes.get(elem.tag, 'i')
187         S['id'][id_prefix] += 1
188         output['id'] = id_prefix + str(S['id'][id_prefix])
189         if elem.attrib.get('id'):
190             output['id'] = 'wl-' + elem.attrib.get('id')
191         if attrs:
192             output['attr'] = attrs.copy()
193         if attr_map:
194             output.setdefault('attr', {})
195             for k, v in attr_map.items():
196                 if v == '.text':
197                     val = elem.text
198                 elif callable(v):
199                     val = v(elem, S)
200                 else:
201                     val = elem.attrib[v]
202                 output['attr'][k] = val
203         output['contents'] = contents
204         output = [output]
205     if elem.tag == 'strofa':
206         verses = [etree.Element('wers')]
207         if elem.text:
208             vparts = re.split(r'/\s+', elem.text)
209             for i, v in enumerate(vparts):
210                 if i:
211                     verses.append(etree.Element('wers'))
212                 verses[-1].text = (verses[-1].text or '') + v
213         for child in elem:
214             vparts = re.split(r'/\s+', child.tail or '')
215             child.tail = vparts[0]
216             verses[-1].append(child)
217             for v in vparts[1:]:
218                 verses.append(etree.Element('wers'))
219                 verses[-1].text = v
220
221         if not(len(verses[-1]) or (verses[-1].text or '').strip()):
222             verses.pop()
223
224         elem.clear(keep_tail=True)
225         for verse in verses:
226             if len(verse) == 1 and (verse[0].tag.startswith('wers') or verse[0].tag == 'zastepnik_wersu') and not (verse[0].tail or '').strip():
227                 elem.append(verse[0])
228             else:
229                 elem.append(verse)
230
231         #if not len(elem):
232         #    for v in re.split(r'/\s+', elem.text):
233         #        etree.SubElement(elem, 'wers').text = v
234         #    elem.text = None
235         
236     if hastext and elem.text:
237         contents.append(norm(elem.text))
238     for c in elem:
239         S['stack'].append(elem.tag)
240         contents += toj(c, S)
241         if hastext and c.tail:
242             contents.append(norm(c.tail))
243         S['stack'].pop()
244
245     if elem.tag in front1:
246         S['front1'] += output
247         return []
248     if elem.tag in front2:
249         S['front2'] += output
250         return []
251     return output
252
253 def conv(tree, slug):
254     S = {
255         'index': 0,
256         'vindex': 0,
257         'id': defaultdict(lambda: 0),
258         'stack': [],
259         'front1': [],
260         'front2': [],
261         'slug': slug,
262     }
263     output = toj(tree.getroot(), S)
264     if not len(output): return {}
265     jt = output[0]
266     jt['front1'] = S['front1']
267     jt['front2'] = S['front2']
268     return jt
269
270 #print(json.dumps(jt, indent=2, ensure_ascii=False))