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