pdf: ignored tags
[librarian.git] / librarian / pypdf.py
index 4cc4d1d..2bcd8d0 100644 (file)
@@ -10,20 +10,23 @@ with TeXML, then runs it by XeLaTeX.
 
 """
 from __future__ import with_statement
 
 """
 from __future__ import with_statement
+from copy import deepcopy
 import os
 import os.path
 import shutil
 from StringIO import StringIO
 from tempfile import mkdtemp, NamedTemporaryFile
 import re
 import os
 import os.path
 import shutil
 from StringIO import StringIO
 from tempfile import mkdtemp, NamedTemporaryFile
 import re
+import random
 from copy import deepcopy
 from subprocess import call, PIPE
 from copy import deepcopy
 from subprocess import call, PIPE
+from urllib2 import urlopen
 
 from Texml.processor import process
 from lxml import etree
 from lxml.etree import XMLSyntaxError, XSLTApplyError
 
 
 from Texml.processor import process
 from lxml import etree
 from lxml.etree import XMLSyntaxError, XSLTApplyError
 
-from xmlutils import Xmill, tag, tagged, ifoption
+from xmlutils import Xmill, tag, tagged, ifoption, tag_open_close
 from librarian.dcparser import Person
 from librarian.parser import WLDocument
 from librarian import ParseError, DCNS, get_resource, IOFile, Format
 from librarian.dcparser import Person
 from librarian.parser import WLDocument
 from librarian import ParseError, DCNS, get_resource, IOFile, Format
@@ -49,15 +52,21 @@ def escape(really):
     return deco
 
 
     return deco
 
 
-def cmd(name, pass_text=False):
-    def wrap(self, element):
-        pre = u'<cmd name="%s">' % name
+def cmd(name, parms=None):
+    def wrap(self, element=None):
+        pre, post = tag_open_close('cmd', name=name)
 
 
-        if pass_text:
-            pre += "<parm>%s</parm>" % element.text
-            return pre + '</cmd>'
+        if parms:
+            for parm in parms:
+                e = etree.Element("parm")
+                e.text = parm
+                pre += etree.tostring(e)
+        if element is not None:
+            pre += "<parm>"
+            post = "</parm>" + post
+            return pre, post
         else:
         else:
-            return pre, '</cmd>'
+            return pre + post
     return wrap
 
 
     return wrap
 
 
@@ -74,7 +83,7 @@ class EduModule(Xmill):
 
         def swap_endlines(txt):
             if self.options['strofa']:
 
         def swap_endlines(txt):
             if self.options['strofa']:
-                txt = txt.replace("/\n", '<ctrl ch="\"/>')
+                txt = txt.replace("/\n", '<ctrl ch="\\"/>')
             return txt
         self.register_text_filter(functions.substitute_entities)
         self.register_text_filter(mark_alien_characters)
             return txt
         self.register_text_filter(functions.substitute_entities)
         self.register_text_filter(mark_alien_characters)
@@ -99,11 +108,15 @@ class EduModule(Xmill):
           u'</cmd>'
 
     @escape(True)
           u'</cmd>'
 
     @escape(True)
-    def get_authors(self, element):
-        authors = self.get_dc(element, 'creator.expert') + \
-          self.get_dc(element, 'creator.scenario') + \
-          self.get_dc(element, 'creator.textbook')
-        return u', '.join(authors)
+    def get_authors(self, element, which=None):
+        dc = self.options['wldoc'].book_info
+        if which is None:
+            authors = dc.authors_textbook + \
+                dc.authors_scenario + \
+                dc.authors_expert
+        else:
+            authors = getattr(dc, "authors_%s" % which)
+        return u', '.join(author.readable() for author in authors)
 
     @escape(1)
     def get_title(self, element):
 
     @escape(1)
     def get_title(self, element):
@@ -124,9 +137,13 @@ class EduModule(Xmill):
             \\usepackage{morefloats}
         }{}'''),
     u'''\\def\\authors{%s}''' % self.get_authors(element),
             \\usepackage{morefloats}
         }{}'''),
     u'''\\def\\authors{%s}''' % self.get_authors(element),
+    u'''\\def\\authorsexpert{%s}''' % self.get_authors(element, 'expert'),
+    u'''\\def\\authorsscenario{%s}''' % self.get_authors(element, 'scenario'),
+    u'''\\def\\authorstextbook{%s}''' % self.get_authors(element, 'textbook'),
+    
     u'''\\author{\\authors}''',
     u'''\\title{%s}''' % self.get_title(element),
     u'''\\author{\\authors}''',
     u'''\\title{%s}''' % self.get_title(element),
-    u'''\\def\\bookurl{%s}''' % self.get_dc(element, 'identifier.url', True),
+    u'''\\def\\bookurl{%s}''' % self.options['wldoc'].book_info.url.canonical(),
     u'''\\def\\rightsinfo{%s}''' % self.get_rightsinfo(element),
     u'</TeXML>']
 
     u'''\\def\\rightsinfo{%s}''' % self.get_rightsinfo(element),
     u'</TeXML>']
 
@@ -138,7 +155,7 @@ class EduModule(Xmill):
         return u"""
     <env name="document">
     <cmd name="maketitle"/>
         return u"""
     <env name="document">
     <cmd name="maketitle"/>
-    """, """</env>"""
+    """, """<cmd name="editorialsection" /></env>"""
 
     @escape(1)
     def handle_texcommand(self, element):
 
     @escape(1)
     def handle_texcommand(self, element):
@@ -189,7 +206,12 @@ class EduModule(Xmill):
     handle_wyroznienie = \
     handle_texcommand
 
     handle_wyroznienie = \
     handle_texcommand
 
-    _handle_strofa = cmd("strofa", True)
+    def handle_uwaga(self, _e):
+        return None
+    def handle_extra(self, _e):
+        return None
+
+    _handle_strofa = cmd("strofa")
 
     def handle_strofa(self, element):
         self.options = {'strofa': True}
 
     def handle_strofa(self, element):
         self.options = {'strofa': True}
@@ -222,7 +244,7 @@ class EduModule(Xmill):
         counter = self.activity_counter
 
         return u"""
         counter = self.activity_counter
 
         return u"""
-
+<cmd name="noindent" />
 <cmd name="activitycounter"><parm>%(counter)d.</parm></cmd>
 <cmd name="activityinfo"><parm>
  <cmd name="activitytime"><parm>%(czas)s</parm></cmd>
 <cmd name="activitycounter"><parm>%(counter)d.</parm></cmd>
 <cmd name="activityinfo"><parm>
  <cmd name="activitytime"><parm>%(czas)s</parm></cmd>
@@ -250,12 +272,17 @@ class EduModule(Xmill):
         return
 
     def handle_lista(self, element, attrs={}):
         return
 
     def handle_lista(self, element, attrs={}):
+        if not element.findall("punkt"):
+            return None
         ltype = element.attrib.get('typ', 'punkt')
         if ltype == 'slowniczek':
         ltype = element.attrib.get('typ', 'punkt')
         if ltype == 'slowniczek':
-            surl = element.attrib.get('href', None)
+            surl = element.attrib.get('src', None)
+            if surl is None:
+                # print '** missing src on <slowniczek>, setting default'
+                surl = 'http://edukacjamedialna.edu.pl/slowniczek'
             sxml = None
             if surl:
             sxml = None
             if surl:
-                sxml = etree.fromstring(self.options['provider'].by_uri(surl).get_string())
+                sxml = etree.fromstring(self.options['wldoc'].provider.by_uri(surl).get_string())
             self.options = {'slowniczek': True, 'slowniczek_xml': sxml }
 
         listcmd = {'num': 'enumerate',
             self.options = {'slowniczek': True, 'slowniczek_xml': sxml }
 
         listcmd = {'num': 'enumerate',
@@ -271,16 +298,19 @@ class EduModule(Xmill):
 
     def handle_cwiczenie(self, element):
         exercise_handlers = {
 
     def handle_cwiczenie(self, element):
         exercise_handlers = {
-            'wybor': Wybor}
-            # 'uporzadkuj': Uporzadkuj,
-            # 'luki': Luki,
-            # 'zastap': Zastap,
-            # 'przyporzadkuj': Przyporzadkuj,
-            # 'prawdafalsz': PrawdaFalsz
+            'wybor': Wybor,
+            'uporzadkuj': Uporzadkuj,
+            'luki': Luki,
+            'zastap': Zastap,
+            'przyporzadkuj': Przyporzadkuj,
+            'prawdafalsz': PrawdaFalsz
+        }
 
         typ = element.attrib['typ']
 
         typ = element.attrib['typ']
+        self.exercise_counter += 1
         if not typ in exercise_handlers:
             return '(no handler)'
         if not typ in exercise_handlers:
             return '(no handler)'
+        self.options = {'exercise_counter': self.exercise_counter}
         handler = exercise_handlers[typ](self.options)
         return handler.generate(element)
 
         handler = exercise_handlers[typ](self.options)
         return handler.generate(element)
 
@@ -291,7 +321,7 @@ class EduModule(Xmill):
         definiens_s = ''
 
         # let's pull definiens from another document
         definiens_s = ''
 
         # let's pull definiens from another document
-        if self.options['slowniczek_xml'] and (not nxt or nxt.tag != 'definiens'):
+        if self.options['slowniczek_xml'] is not None and (nxt is None or nxt.tag != 'definiens'):
             sxml = self.options['slowniczek_xml']
             assert element.text != ''
             defloc = sxml.xpath("//definiendum[text()='%s']" % element.text)
             sxml = self.options['slowniczek_xml']
             assert element.text != ''
             defloc = sxml.xpath("//definiendum[text()='%s']" % element.text)
@@ -322,7 +352,7 @@ class EduModule(Xmill):
                 #        else: frames_c = ""
                 #        return u"""<table class="%s">""" % frames_c, u"</table>"
         return u'''
                 #        else: frames_c = ""
                 #        return u"""<table class="%s">""" % frames_c, u"</table>"
         return u'''
-<cmd name="begin"><parm>tabular</parm><opt>%s</opt></cmd>
+<cmd name="begin"><parm>tabular</parm><parm>%s</parm></cmd>
     ''' % ('l' * max_col), \
     u'''<cmd name="end"><parm>tabular</parm></cmd>'''
 
     ''' % ('l' * max_col), \
     u'''<cmd name="end"><parm>tabular</parm></cmd>'''
 
@@ -333,10 +363,43 @@ class EduModule(Xmill):
     @escape(1)
     def handle_kol(self, element):
         if element.getnext() is not None:
     @escape(1)
     def handle_kol(self, element):
         if element.getnext() is not None:
-            return u"", u'<spec cat="align">'
+            return u"", u'<spec cat="align" />'
         return u"", u""
 
         return u"", u""
 
-    handle_link = cmd('em', True)
+    def handle_link(self, element):
+        if element.attrib.get('url'):
+            url = element.attrib.get('url')
+            if url == element.text:
+                return cmd('url')(self, element)
+            else:
+                return cmd('href', parms=[element.attrib['url']])(self, element)
+        else:
+            return cmd('emph')(self, element)
+
+    def handle_obraz(self, element):
+        frmt = self.options['format']
+        name = element.attrib['nazwa'].strip()
+        image = frmt.get_image(name.strip())
+        img_path = "obraz/%s" % name.replace("_", "")
+        frmt.attachments[img_path] = image
+        return cmd("obraz", parms=[img_path])(self)
+
+    def handle_video(self, element):
+        url = element.attrib.get('url')
+        if not url:
+            print '!! <video> missing url'
+            return
+        m = re.match(r'(?:https?://)?(?:www.)?youtube.com/watch\?(?:.*&)?v=([^&]+)(?:$|&)', url)
+        if not m:
+            print '!! unknown <video> url scheme:', url
+            return
+        name = m.group(1)
+        thumb = IOFile.from_string(urlopen
+            ("http://img.youtube.com/vi/%s/0.jpg" % name).read())
+        img_path = "video/%s.jpg" % name.replace("_", "")
+        self.options['format'].attachments[img_path] = thumb
+        canon_url = "https://www.youtube.com/watch?v=%s" % name
+        return cmd("video", parms=[img_path, canon_url])(self)
 
 
 class Exercise(EduModule):
 
 
 class Exercise(EduModule):
@@ -344,14 +407,22 @@ class Exercise(EduModule):
         self.question_counter = 0
         super(Exercise, self).__init__(*args, **kw)
 
         self.question_counter = 0
         super(Exercise, self).__init__(*args, **kw)
 
-    handle_rozw_kom = ifoption(teacher=True)(cmd('akap', True))
+    handle_rozw_kom = ifoption(teacher=True)(cmd('akap'))
 
     def handle_cwiczenie(self, element):
 
     def handle_cwiczenie(self, element):
-        self.options = {'exercise': element.attrib['typ']}
+        self.options = {
+            'exercise': element.attrib['typ'],
+            'sub_gen': True,
+        }
         self.question_counter = 0
         self.piece_counter = 0
 
         self.question_counter = 0
         self.piece_counter = 0
 
-        pre = u""
+        header = etree.Element("parm")
+        header_cmd = etree.Element("cmd", name="naglowekpodrozdzial")
+        header_cmd.append(header)
+        header.text = u"Zadanie %d." % self.options['exercise_counter']
+
+        pre = etree.tostring(header_cmd, encoding=unicode)
         post = u""
         # Add a single <pytanie> tag if it's not there
         if not element.xpath(".//pytanie"):
         post = u""
         # Add a single <pytanie> tag if it's not there
         if not element.xpath(".//pytanie"):
@@ -363,29 +434,37 @@ class Exercise(EduModule):
     def handle_pytanie(self, element):
         """This will handle <cwiczenie> element, when there is no <pytanie>
         """
     def handle_pytanie(self, element):
         """This will handle <cwiczenie> element, when there is no <pytanie>
         """
-        opts = {}
         self.question_counter += 1
         self.piece_counter = 0
         self.question_counter += 1
         self.piece_counter = 0
-        solution = element.attrib.get('rozw', None)
-        if solution:
-            opts['solution'] = solution
+        pre = post = u""
+        if self.options['teacher'] and element.attrib.get('rozw'):
+            post += u" [rozwiązanie: %s]" % element.attrib.get('rozw')
+        return pre, post
 
 
-        handles = element.attrib.get('uchwyty', None)
-        if handles:
-            opts['handles'] = handles
+    def handle_punkt(self, element):
+        pre, post = super(Exercise, self).handle_punkt(element)
+        if self.options['teacher'] and element.attrib.get('rozw'):
+            post += u" [rozwiązanie: %s]" % element.attrib.get('rozw')
+        return pre, post
 
 
-        minimum = element.attrib.get('min', None)
-        if minimum:
-            opts['minimum'] = minimum
+    def solution_header(self):
+        par = etree.Element("cmd", name="par")
+        parm = etree.Element("parm")
+        parm.text = u"Rozwiązanie:"
+        par.append(parm)
+        return etree.tostring(par)
 
 
-        if opts:
-            self.options = opts
-        return u"", u""
+    def explicit_solution(self):
+        if self.options['solution']:
+            par = etree.Element("cmd", name="par")
+            parm = etree.Element("parm")
+            parm.text = self.options['solution']
+            par.append(parm)
+            return self.solution_header() + etree.tostring(par)
 
 
 
 
-class Wybor(Exercise):
-    INSTRUCTION = None
 
 
+class Wybor(Exercise):
     def handle_cwiczenie(self, element):
         pre, post = super(Wybor, self).handle_cwiczenie(element)
         is_single_choice = True
     def handle_cwiczenie(self, element):
         pre, post = super(Wybor, self).handle_cwiczenie(element)
         is_single_choice = True
@@ -415,6 +494,89 @@ class Wybor(Exercise):
             return super(Wybor, self).handle_punkt(element)
 
 
             return super(Wybor, self).handle_punkt(element)
 
 
+class Uporzadkuj(Exercise):
+    def handle_pytanie(self, element):
+        order_items = element.xpath(".//punkt/@rozw")
+        return super(Uporzadkuj, self).handle_pytanie(element)
+
+
+class Przyporzadkuj(Exercise):
+    def handle_lista(self, lista):
+        header = etree.Element("parm")
+        header_cmd = etree.Element("cmd", name="par")
+        header_cmd.append(header)
+        if 'nazwa' in lista.attrib:
+            header.text = u"Kategorie:"
+        elif 'cel' in lista.attrib:
+            header.text = u"Elementy do przyporządkowania:"
+        else:
+            header.text = u"Lista:"
+        pre, post = super(Przyporzadkuj, self).handle_lista(lista)
+        pre = etree.tostring(header_cmd, encoding=unicode) + pre
+        return pre, post
+
+
+class Luki(Exercise):
+    def find_pieces(self, question):
+        return question.xpath(".//luka")
+
+    def solution(self, piece):
+        piece = deepcopy(piece)
+        piece.tail = None
+        sub = EduModule()
+        return sub.generate(piece)
+
+    def handle_pytanie(self, element):
+        qpre, qpost = super(Luki, self).handle_pytanie(element)
+
+        luki = self.find_pieces(element)
+        random.shuffle(luki)
+        self.words = u"<env name='itemize'>%s</env>" % (
+            "".join("<cmd name='item'/>%s" % self.solution(luka) for luka in luki)
+        )
+        return qpre, qpost
+
+    def handle_opis(self, element):
+        return '', self.words
+
+    def handle_luka(self, element):
+        luka = "_" * 10
+        if self.options['teacher']:
+            piece = deepcopy(element)
+            piece.tail = None
+            sub = EduModule()
+            text = sub.generate(piece)
+            luka += u" [rozwiązanie: %s]" % text
+        return luka
+
+
+class Zastap(Luki):
+    def find_pieces(self, question):
+        return question.xpath(".//zastap")
+
+    def solution(self, piece):
+        return piece.attrib['rozw']
+
+    def list_header(self):
+        return u"Elementy do wstawienia"
+
+    def handle_zastap(self, element):
+        piece = deepcopy(element)
+        piece.tail = None
+        sub = EduModule()
+        text = sub.generate(piece)
+        if self.options['teacher'] and element.attrib.get('rozw'):
+            text += u" [rozwiązanie: %s]" % element.attrib.get('rozw')
+        return text
+
+
+class PrawdaFalsz(Exercise):
+    def handle_punkt(self, element):
+        pre, post = super(PrawdaFalsz, self).handle_punkt(element)
+        if 'rozw' in element.attrib:
+            post += u" [Prawda/Fałsz]"
+        return pre, post
+
 
 
 def fix_lists(tree):
 
 
 def fix_lists(tree):
@@ -434,9 +596,27 @@ def fix_lists(tree):
 
 
 class EduModulePDFFormat(PDFFormat):
 
 
 class EduModulePDFFormat(PDFFormat):
+    style = get_resource('res/styles/edumed/pdf/edumed.sty')
+
     def get_texml(self):
     def get_texml(self):
-        edumod = EduModule()
+        self.attachments = {}
+        edumod = EduModule({
+            "wldoc": self.wldoc,
+            "format": self,
+            "teacher": self.customization.get('teacher'),
+        })
         texml = edumod.generate(fix_lists(self.wldoc.edoc.getroot())).encode('utf-8')
 
         open("/tmp/texml.xml", "w").write(texml)
         return texml
         texml = edumod.generate(fix_lists(self.wldoc.edoc.getroot())).encode('utf-8')
 
         open("/tmp/texml.xml", "w").write(texml)
         return texml
+
+    def get_tex_dir(self):
+        temp = super(EduModulePDFFormat, self).get_tex_dir()
+        shutil.copy(get_resource('res/styles/edumed/logo.png'), temp)
+        for name, iofile in self.attachments.items():
+            iofile.save_as(os.path.join(temp, name))
+        return temp
+
+    def get_image(self, name):
+        return self.wldoc.source.attachments[name]
+