reset button
[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 <input type="button" class="reset" value="reset"/>
159 </div>
160 </form>
161 </div>
162 """
163         # Add a single <pytanie> tag if it's not there
164         if not element.xpath(".//pytanie"):
165             qpre, qpost = self.handle_pytanie(element)
166             pre = pre + qpre
167             post = qpost + post
168         return pre, post
169
170     def handle_pytanie(self, element):
171         """This will handle <cwiczenie> element, when there is no <pytanie>
172         """
173         add_class = ""
174         self.question_counter += 1
175         self.piece_counter = 0
176         solution = element.attrib.get('rozw', None)
177         if solution: solution_s = ' data-solution="%s"' % solution
178         else: solution_s = ''
179
180         handles = element.attrib.get('uchwyty', None)
181         if handles:
182             add_class += ' handles handles-%s' % handles
183             self.options = {'handles': handles}
184
185
186         return '<div class="question%s" data-no="%d" %s>' %\
187             (add_class, self.question_counter, solution_s), \
188     "</div>"
189
190
191 class Wybor(Excercise):
192     def handle_punkt(self, element):
193         if self.options['excercise'] and element.attrib.get('nazwa', None):
194             qc = self.question_counter
195             self.piece_counter += 1
196             no = self.piece_counter
197             eid = "q%(qc)d_%(no)d" % locals()
198             aname = element.attrib.get('nazwa', None)
199             return u"""
200 <li class="question-piece" data-qc="%(qc)d" data-no="%(no)d" data-name="%(aname)s">
201 <input type="checkbox" name="" id="%(eid)s" />
202 <label for="%(eid)s">
203 """ % locals(), u"</label></li>"
204
205         else:
206             return super(Wybor, self).handle_punkt(element)
207
208
209 class Uporzadkuj(Excercise):
210     def handle_pytanie(self, element):
211         """
212 Overrides the returned content default handle_pytanie
213         """
214         # we ignore the result, returning our own
215         super(Uporzadkuj, self).handle_pytanie(element)
216         order_items = element.xpath(".//punkt/@rozw")
217
218         return u"""<div class="question" data-original="%s" data-no="%s">""" % \
219             (','.join(order_items), self.question_counter), \
220             u"""</div>"""
221
222     def handle_punkt(self, element):
223         return """<li class="question-piece" data-pos="%(rozw)s"/>""" \
224             % element.attrib,\
225             "</li>"
226
227
228 class Luki(Excercise):
229     def find_pieces(self, question):
230         return question.xpath("//luka")
231
232     def solution_html(self, piece):
233         return piece.text + ''.join(
234             [etree.tostring(n, encoding=unicode)
235              for n in piece])
236
237     def handle_pytanie(self, element):
238         qpre, qpost = super(Luki, self).handle_pytanie(element)
239
240         luki = list(enumerate(self.find_pieces(element)))
241         luki_html = ""
242         i = 0
243         random.shuffle(luki)
244         for (i, luka) in luki:
245             i += 1
246             luka_html = self.solution_html(luka)
247             luki_html += u'<span class="draggable question-piece" data-no="%d">%s</span>' % (i, luka_html)
248         self.words_html = '<div class="words">%s</div>' % luki_html
249
250         return qpre, qpost
251
252     def handle_opis(self, element):
253         pre, post = super(Luki, self).handle_opis(element)
254         return pre, self.words_html + post
255
256     def handle_luka(self, element):
257         self.piece_counter += 1
258         return '<span class="placeholder" data-solution="%d"></span>' % self.piece_counter
259
260
261 class Zastap(Luki):
262     def find_pieces(self, question):
263         return question.xpath("//zastap")
264
265     def solution_html(self, piece):
266         return piece.attrib['rozw']
267
268     def handle_zastap(self, element):
269         self.piece_counter += 1
270         return '<span class="placeholder zastap question-piece" data-solution="%d">' \
271             % self.piece_counter, '</span>'
272
273
274 class Przyporzadkuj(Excercise):
275     def handle_lista(self, lista):
276         if 'nazwa' in lista.attrib:
277             attrs = {
278                 'data-name': lista.attrib['nazwa'],
279                 'class': 'predicate'
280                 }
281             self.options = {'predicate': True}
282         elif 'cel' in lista.attrib:
283             attrs = {
284                 'data-target': lista.attrib['cel'],
285                 'class': 'subject'
286                 }
287             self.options = {'subject': True}
288         else:
289             attrs = {}
290         pre, post = super(Przyporzadkuj, self).handle_lista(lista, attrs)
291         return pre, post + '<br class="clr"/>'
292
293     def handle_punkt(self, element):
294         if self.options['subject']:
295             self.piece_counter += 1
296             if self.options['handles']:
297                 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>'
298             else:
299                 return '<li data-solution="%s" data-no="%s" class="question-piece draggable">' % (element.attrib['rozw'], self.piece_counter), '</li>'
300
301         elif self.options['predicate']:
302             placeholders = u'<li class="placeholder multiple"/>'
303             return '<li data-predicate="%(nazwa)s">' % element.attrib, '<ul class="subjects">' + placeholders + '</ul></li>'
304
305         else:
306             return super(Przyporzadkuj, self).handle_punkt(element)
307
308
309 class PrawdaFalsz(Excercise):
310     def handle_punkt(self, element):
311         if 'rozw' in element.attrib:
312             return u'''<li data-solution="%s" class="question-piece">
313             <span class="buttons">
314             <a href="#" data-value="true" class="true">Prawda</a>
315             <a href="#" data-value="false" class="false">Fałsz</a>
316         </span>''' % {'prawda': 'true', 'falsz': 'false'}[element.attrib['rozw']], '</li>'
317         else:
318             return super(PrawdaFalsz, self).handle_punkt(element)
319
320
321 def transform(wldoc, stylesheet='edumed', options=None, flags=None):
322     """Transforms the WL document to XHTML.
323
324     If output_filename is None, returns an XML,
325     otherwise returns True if file has been written,False if it hasn't.
326     File won't be written if it has no content.
327     """
328     edumod = EduModule(options)
329 #    from pdb import set_trace; set_trace()
330     html = edumod.generate(wldoc.edoc.getroot())
331
332     return OutputFile.from_string(html.encode('utf-8'))