cleanups regarding dragging, zastap excercise
[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         print "in lista %s %s" % (lista.attrib, self.options)
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         print "in punkt %s %s" % (element.attrib, self.options)
295
296         if self.options['subject']:
297             self.piece_counter += 1
298             if self.options['handles']:
299                 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>'
300             else:
301                 return '<li data-solution="%s" data-no="%s" class="question-piece draggable">' % (element.attrib['rozw'], self.piece_counter), '</li>'
302
303         elif self.options['predicate']:
304             print etree.tostring(element, encoding=unicode)
305             placeholders = u'<li class="placeholder multiple"/>'
306             return '<li data-predicate="%(nazwa)s">' % element.attrib, '<ul class="subjects">' + placeholders + '</ul></li>'
307
308         else:
309             return super(Przyporzadkuj, self).handle_punkt(element)
310
311
312 class PrawdaFalsz(Excercise):
313     def handle_punkt(self, element):
314         if 'rozw' in element.attrib:
315             return u'''<li data-solution="%s" class="question-piece">
316             <span class="buttons">
317             <a href="#" data-value="true" class="true">Prawda</a>
318             <a href="#" data-value="false" class="false">Fałsz</a>
319         </span>''' % {'prawda': 'true', 'falsz': 'false'}[element.attrib['rozw']], '</li>'
320         else:
321             return super(PrawdaFalsz, self).handle_punkt(element)
322
323
324 def transform(wldoc, stylesheet='edumed', options=None, flags=None):
325     """Transforms the WL document to XHTML.
326
327     If output_filename is None, returns an XML,
328     otherwise returns True if file has been written,False if it hasn't.
329     File won't be written if it has no content.
330     """
331     edumod = EduModule(options)
332 #    from pdb import set_trace; set_trace()
333     html = edumod.generate(wldoc.edoc.getroot())
334
335     return OutputFile.from_string(html.encode('utf-8'))