106e30b2d58e00e58e72fcef510048fbe30be8cb
[librarian.git] / librarian / pyhtml.py
1 # -*- coding: utf-8 -*-
2 #
3 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 #
6 from lxml import etree
7 from librarian import OutputFile, RDFNS, DCNS
8 from xmlutils import Xmill, tag, tagged, ifoption
9 import random
10
11 class EduModule(Xmill):
12     def __init__(self, *args):
13         super(EduModule, self).__init__(*args)
14         self.activity_counter = 0
15
16     def handle_powiesc(self, element):
17         return u"""
18 <div class="module" id="book-text">
19  <span class="teacher-toggle">
20   <input type="checkbox" name="teacher-toggle" id="teacher-toggle"/>
21   <label for="teacher-toggle">Pokaż treść dla nauczyciela</label>
22  </span>
23
24 """, u"</div>"
25
26
27     handle_autor_utworu = tag("span", "author")
28     handle_nazwa_utworu = tag("h1", "title")
29     handle_dzielo_nadrzedne = tag("span", "collection")
30     handle_podtytul = tag("span", "subtitle")
31     handle_naglowek_akt = handle_naglowek_czesc = handle_srodtytul = tag("h2")
32     handle_naglowek_scena = handle_naglowek_rozdzial = tag('h3')
33     handle_naglowek_osoba = handle_naglowek_podrozdzial = tag('h4')
34     handle_akap = handle_akap_dialog = handle_akap_cd = tag('p', 'paragraph')
35     handle_strofa = tag('div', 'stanza')
36
37     def handle_aktywnosc(self, element):
38         self.activity_counter += 1
39         self.options = {
40             'activity': True,
41             'activity_counter': self.activity_counter
42             }
43         submill = EduModule()
44
45         opis = submill.generate(element.xpath('opis')[0])
46
47         n = element.xpath('wskazowki')
48         if n: wskazowki = submill.generate(n[0])
49
50         else: wskazowki = ''
51         n = element.xpath('pomoce')
52
53         if n: pomoce = submill.generate(n[0])
54         else: pomoce = ''
55
56         forma = ''.join(element.xpath('forma/text()'))
57
58         czas = ''.join(element.xpath('czas/text()'))
59
60         counter = self.activity_counter
61
62         return u"""
63 <div class="activity">
64  <div class="text">%(counter)d.
65   %(opis)s
66   %(wskazowki)s
67  </div>
68  <div class="info">
69   <p>Czas: %(czas)s min</p>
70   <p>Forma: %(forma)s</p>
71   %(pomoce)s
72  </div>
73  <div class="clearboth"></div>
74 </div>
75 """ % locals()
76
77     handle_opis = ifoption(activity=False)(tag('div', 'description'))
78     handle_wskazowki = ifoption(activity=False)(tag('div', ('hints', 'teacher')))
79
80     @ifoption(activity=False)
81     @tagged('div', 'materials')
82     def handle_pomoce(self, _):
83         return "Pomoce: ", ""
84
85     def handle_czas(self, *_):
86         return
87
88     def handle_forma(self, *_):
89         return
90
91     def handle_cwiczenie(self, element):
92         excercise_handlers = {
93             'wybor': Wybor,
94             'uporzadkuj': Uporzadkuj,
95             'luki': Luki,
96             'zastap': Zastap,
97             'przyporzadkuj': Przyporzadkuj,
98             'prawdafalsz': PrawdaFalsz
99             }
100
101         typ = element.attrib['typ']
102         handler = excercise_handlers[typ](self.options)
103         return handler.generate(element)
104
105     # Lists
106     def handle_lista(self, element, attrs={}):
107         ltype = element.attrib.get('typ', 'punkt')
108         if ltype == 'slowniczek':
109             self.options = {'slowniczek': True}
110             return '<div class="slowniczek">', '</div>'
111 ### robie teraz punkty wyboru
112         listtag = {'num': 'ol',
113                'punkt': 'ul',
114                'alfa': 'ul',
115                'czytelnia': 'ul'}[ltype]
116
117         classes = attrs.get('class', '')
118         if classes: del attrs['class']
119
120         attrs_s = ' '.join(['%s="%s"' % kv for kv in attrs.items()])
121         if attrs_s: attrs_s = ' ' + attrs_s
122
123         return '<%s class="lista %s %s"%s>' % (listtag, ltype, classes, attrs_s), '</%s>' % listtag
124
125     def handle_punkt(self, element):
126         if self.options['slowniczek']:
127             return '<dl>', '</dl>'
128         else:
129             return '<li>', '</li>'
130
131     def handle_rdf__RDF(self, _):
132         # ustal w opcjach  rzeczy :D
133         return
134
135
136 class Excercise(EduModule):
137     def __init__(self, *args, **kw):
138         self.question_counter = 0
139         super(Excercise, self).__init__(*args, **kw)
140
141     def handle_rozw_kom(self, element):
142         return None
143
144     def handle_cwiczenie(self, element):
145         self.options = {'excercise': element.attrib['typ']}
146         self.question_counter = 0
147         self.piece_counter = 0
148
149         pre = u"""
150 <div class="excercise %(typ)s" data-type="%(typ)s">
151 <form action="#" method="POST">
152 """ % element.attrib
153         post = u"""
154 <div class="buttons">
155 <span class="message"></span>
156 <input type="button" class="check" value="sprawdź"/>
157 <input type="button" class="solutions" value="pokaż rozwiązanie"/>
158 </div>
159 </form>
160 </div>
161 """
162         # Add a single <pytanie> tag if it's not there
163         if not element.xpath(".//pytanie"):
164             qpre, qpost = self.handle_pytanie(element)
165             pre = pre + qpre
166             post = qpost + post
167         return pre, post
168
169     def handle_pytanie(self, element):
170         """This will handle <cwiczenie> element, when there is no <pytanie>
171         """
172         add_class = ""
173         self.question_counter += 1
174         self.piece_counter = 0
175         solution = element.attrib.get('rozw', None)
176         if solution: solution_s = ' data-solution="%s"' % solution
177         else: solution_s = ''
178
179         handles = element.attrib.get('uchwyty', None)
180         if handles:
181             add_class += ' handles handles-%s' % handles
182             self.options = {'handles': handles}
183
184
185         return '<div class="question%s" data-no="%d" %s>' %\
186             (add_class, self.question_counter, solution_s), \
187     "</div>"
188
189
190 class Wybor(Excercise):
191     def handle_punkt(self, element):
192         if self.options['excercise'] and element.attrib.get('nazwa', None):
193             qc = self.question_counter
194             self.piece_counter += 1
195             no = self.piece_counter
196             eid = "q%(qc)d_%(no)d" % locals()
197             aname = element.attrib.get('nazwa', None)
198             return u"""
199 <li class="question-piece" data-qc="%(qc)d" data-no="%(no)d" data-name="%(aname)s">
200 <input type="checkbox" name="" id="%(eid)s" />
201 <label for="%(eid)s">
202 """ % locals(), u"</label></li>"
203
204         else:
205             return super(Wybor, self).handle_punkt(element)
206
207
208 class Uporzadkuj(Excercise):
209     def handle_pytanie(self, element):
210         """
211 Overrides the returned content default handle_pytanie
212         """
213         # we ignore the result, returning our own
214         super(Uporzadkuj, self).handle_pytanie(element)
215         order_items = element.xpath(".//punkt/@rozw")
216
217         return u"""<div class="question" data-original="%s" data-no="%s">""" % \
218             (','.join(order_items), self.question_counter), \
219             u"""</div>"""
220
221     def handle_punkt(self, element):
222         return """<li class="question-piece" data-pos="%(rozw)s"/>""" \
223             % element.attrib,\
224             "</li>"
225
226
227 class Luki(Excercise):
228     def find_pieces(self, question):
229         return question.xpath("//luka")
230
231     def solution_html(self, piece):
232         return piece.text + ''.join(
233             [etree.tostring(n, encoding=unicode)
234              for n in piece])
235
236     def handle_pytanie(self, element):
237         qpre, qpost = super(Luki, self).handle_pytanie(element)
238
239         luki = list(enumerate(self.find_pieces(element)))
240         luki_html = ""
241         i = 0
242         random.shuffle(luki)
243         for (i, luka) in luki:
244             i += 1
245             luka_html = self.solution_html(luka)
246             luki_html += u'<span class="draggable question-piece" data-no="%d">%s</span>' % (i, luka_html)
247         self.words_html = '<div class="words">%s</div>' % luki_html
248
249         return qpre, qpost
250
251     def handle_opis(self, element):
252         pre, post = super(Luki, self).handle_opis(element)
253         return pre, self.words_html + post
254
255     def handle_luka(self, element):
256         self.piece_counter += 1
257         return '<span class="placeholder" data-solution="%d"></span>' % self.piece_counter
258
259
260 class Zastap(Luki):
261     def find_pieces(self, question):
262         return question.xpath("//zastap")
263
264     def solution_html(self, piece):
265         return piece.attrib['rozw']
266
267     def handle_zastap(self, element):
268         self.piece_counter += 1
269         return '<span class="placeholder zastap question-piece" data-solution="%d">' \
270             % self.piece_counter, '</span>'
271
272
273 class Przyporzadkuj(Excercise):
274     def handle_lista(self, lista):
275         if 'nazwa' in lista.attrib:
276             attrs = {
277                 'data-name': lista.attrib['nazwa'],
278                 'class': 'predicate'
279                 }
280             self.options = {'predicate': True}
281         elif 'cel' in lista.attrib:
282             attrs = {
283                 'data-target': lista.attrib['cel'],
284                 'class': 'subject'
285                 }
286             self.options = {'subject': True}
287         else:
288             attrs = {}
289         pre, post = super(Przyporzadkuj, self).handle_lista(lista, attrs)
290         return pre, post + '<br class="clr"/>'
291
292     def handle_punkt(self, element):
293         if self.options['subject']:
294             self.piece_counter += 1
295             if self.options['handles']:
296                 return '<li><span data-solution="%s" data-no="%s" class="question-piece draggable handle">%s</span>' % (element.attrib['rozw'], self.piece_counter, self.piece_counter), '</li>'
297             else:
298                 return '<li data-solution="%s" data-no="%s" class="question-piece draggable">' % (element.attrib['rozw'], self.piece_counter), '</li>'
299
300         elif self.options['predicate']:
301             placeholders = u'<li class="placeholder multiple"/>'
302             return '<li data-predicate="%(nazwa)s">' % element.attrib, '<ul class="subjects">' + placeholders + '</ul></li>'
303
304         else:
305             return super(Przyporzadkuj, self).handle_punkt(element)
306
307
308 class PrawdaFalsz(Excercise):
309     def handle_punkt(self, element):
310         if 'rozw' in element.attrib:
311             return u'''<li data-solution="%s" class="question-piece">
312             <span class="buttons">
313             <a href="#" data-value="true" class="true">Prawda</a>
314             <a href="#" data-value="false" class="false">Fałsz</a>
315         </span>''' % {'prawda': 'true', 'falsz': 'false'}[element.attrib['rozw']], '</li>'
316         else:
317             return super(PrawdaFalsz, self).handle_punkt(element)
318
319
320 def transform(wldoc, stylesheet='edumed', options=None, flags=None):
321     """Transforms the WL document to XHTML.
322
323     If output_filename is None, returns an XML,
324     otherwise returns True if file has been written,False if it hasn't.
325     File won't be written if it has no content.
326     """
327     edumod = EduModule(options)
328 #    from pdb import set_trace; set_trace()
329     html = edumod.generate(wldoc.edoc.getroot())
330
331     return OutputFile.from_string(html.encode('utf-8'))