2 # -*- coding: utf-8 -*-
4 from slughifi import slughifi
8 def __init__(self, state, lines):
13 return cls(self.state, self.lines)
15 def line(self, position):
16 return self.lines[position]
18 ignore = [re.compile(r"^[\[][PA][\]] - [^ ]+$")]
19 empty_line = re.compile(r"^\s+$")
21 def skip_empty(self, position):
22 while self.line(position) == "" or \
23 self.empty_line.match(self.line(position)) or \
24 filter(lambda r: r.match(self.line(position)),
29 def tag(self, position):
31 Return None -- means that we can't tag it in any way
35 def wrap(self, tagname, content):
36 return u"<%s>%s</%s>" % (tagname, content, tagname)
39 def anymatches(regex):
40 return lambda x: regex.match(x)
43 class Section(Tagger):
44 looks_like = re.compile(r"^[IVX]+[.]\s+(.*)$")
46 def __init__(self, *a):
47 super(Section, self).__init__(*a)
48 self.is_podrozdzial = False
51 pos2 = self.skip_empty(pos)
53 m = self.looks_like.match(self.line(pos))
55 self.title = m.groups()[0]
58 def __unicode__(self):
59 return self.wrap(self.is_podrozdzial and "naglowek_podrozdzial" or "naglowek_rozdzial",
64 looks_like = re.compile(r"([^:]+): (.*)", re.UNICODE)
67 pos = self.skip_empty(pos)
68 m = self.looks_like.match(self.line(pos))
72 m = self.state.get('meta', {})
74 self.state['meta'] = m
78 class Informacje(Tagger):
80 self.title = self.spawn(Section)
82 pos = self.title.tag(pos)
83 if pos is None: return
87 pos = self.skip_empty(pos)
88 meta = self.spawn(Meta)
90 if pos2 is None: break
91 self.meta.append(meta)
98 point = re.compile(r"^[\s]*[-*·]{1,2}(.*)")
99 num = re.compile(r"^[\s]*[a-z][.]\s+(.*)")
101 def __init__(self, *args):
103 super(List, self).__init__(*args)
110 m = self.point.match(l)
112 m = self.num.match(l)
113 if m: self.type = 'num'
115 self.items.append(m.groups()[0].lstrip())
122 def append(self, tagger):
123 self.items.append(tagger)
125 def __unicode__(self):
126 s = '<lista typ="%s">' % self.type
128 if isinstance(i, list):
129 x = "\n".join(map(lambda elem: unicode(elem), i))
132 s += "\n<punkt>%s</punkt>" % x
137 class Paragraph(Tagger):
139 re.compile(r"[\s]*opis zawarto.ci[\s]*", re.I),
140 re.compile(r"^[\s]*$"),
141 re.compile(r"http://pad.nowoczesnapolska.org.pl/p/slowniczek")
144 re.compile(r"[\s]*(przebieg zaj..|opcje dodatkowe)[\s]*", re.I),
148 self.line = self.lines[pos]
150 self.is_podrozdzial = False
152 for x in self.remove_this:
153 if x.match(self.line):
156 for x in self.podrozdzial:
157 if x.match(self.line):
158 self.is_podrozdzial = True
162 def __unicode__(self):
164 if self.is_podrozdzial:
165 tag = 'naglowek_podrozdzial'
168 return u"<%s>%s</%s>" % (tag, self.line, tag)
174 def __init__(self, tag_name, *elems):
175 self.tag_name = tag_name
178 def __unicode__(self):
179 s = u"<%s>" % self.tag_name
182 if isinstance(e, (str, unicode)):
185 s += "\n " + unicode(e)
189 s += u"</%s>" % self.tag_name
193 def eatany(pos, *taggers):
195 for t in list(taggers):
204 def eatseq(pos, *taggers):
206 taggers = list(taggers[:])
209 p = taggers[0].tag(pos)
211 return (tuple(good), pos)
212 good.append(taggers.pop(0))
213 # print "%d -> %d" % (pos, p)
217 print "Got index error for pos=%d" % pos
218 return (tuple(good), pos)
221 def tagger(text, pretty_print=False):
223 tagger(text) function name and signature is a contract.
224 returns auto-tagged text
226 if not isinstance(text, unicode):
227 text = unicode(text.decode('utf-8'))
228 lines = text.split("\n")
232 info = Informacje(state, lines)
234 ((info,), pos) = eatseq(pos, info)
236 # print "[i] %d. %s" % (pos, lines[pos])
241 x, pos = eatany(pos, info.spawn(Section),
242 info.spawn(List), info.spawn(Paragraph))
247 content.append(lines[pos])
249 if pos >= len(lines):
252 return toxml(content, pretty_print=pretty_print)
255 'description': u'Publikacja zrealizowana w ramach projektu Cyfrowa Przyszłość (http://edukacjamedialna.edu.pl).',
256 'relation': u'moduły powiązane linki',
257 'description.material': u'linki do załączników',
258 'rights': u'Creative Commons Uznanie autorstwa - Na tych samych warunkach 3.0',
262 class NotFound(Exception):
266 def find_block(content, title_re, begin=-1, end=-1):
267 title_re = re.compile(title_re, re.I | re.UNICODE)
270 if begin < 0: begin = 0
271 if end < 0: end = len(content)
273 for i in range(begin, end):
275 if isinstance(elem, Paragraph):
276 if title_re.match(elem.line):
279 if isinstance(elem, Section):
280 if title_re.match(elem.title):
284 if isinstance(elem, List):
286 if isinstance(elem, Paragraph) and elem.line:
294 def remove_block(content, title_re, removed=None):
295 rb, re = find_block(content, title_re)
296 if removed is not None and isinstance(removed, list):
297 removed += content[rb:re][:]
302 def mark_activities(content):
305 is_przebieg = re.compile(r"[\s]*przebieg zaj..[\s]*", re.I)
307 is_next_section = re.compile(r"^[IVX]+[.]? ")
308 is_activity = re.compile(r"^[0-9]+[.] (.+)")
310 is_activity_tools = re.compile(r"^pomoce:[\s]*(.+)")
311 is_activity_work = re.compile(r"^forma pracy:[\s]*(.+)")
312 is_activity_time = re.compile(r"^czas:[\s]*([\d]+).*")
314 'pomoce': is_activity_tools,
315 'forma': is_activity_work,
316 'czas': is_activity_time
320 in_activities = False
325 if isinstance(e, Section):
326 if in_activities and \
327 is_next_section.match(e.title):
328 in_activities = False
330 if isinstance(e, Paragraph):
331 if not in_activities and \
332 is_przebieg.match(e.line):
336 m = is_activity.match(e.line)
338 e.line = m.groups()[0]
340 if is_activity_time.match(e.line):
342 activities.append((ab, ae))
347 for ab, ae in activities:
352 act_els.append(Container("opis", content[ab]))
353 for i in range(ab, ae):
355 if isinstance(e, Paragraph):
356 for prop, pattern in activity_props.items():
357 m = pattern.match(e.line)
359 act_els.append(Container(prop, m.groups()[0]))
360 if info_start > i: info_start = i
361 act_els.insert(1, Container('wskazowki',
362 *content[ab + 1:info_start]))
363 content[ab:ae] = [Container('aktywnosc', *act_els)]
367 def mark_dictionary(content):
371 is_dictionary = re.compile(r"[\s]*s.owniczek[\s]*", re.I)
372 is_dictentry = re.compile(r"([^-]+) - (.+)")
373 slowniczek = content[0].spawn(List)
374 slowniczek.type = 'slowniczek'
375 while i < len(content):
377 if isinstance(e, Section):
378 if is_dictionary.match(e.title):
382 content[db:de] = [slowniczek]
385 if isinstance(e, Paragraph):
386 m = is_dictentry.match(e.line)
388 slowniczek.append([Container('definiendum', m.groups()[0]),
389 Container('definiens', m.groups()[1])])
398 def mark_czytelnia(content):
402 czy_czytelnia = re.compile(r"[\s]*czytelnia[\s]*", re.I)
403 czytelnia = content[0].spawn(List)
404 czytelnia.type = 'czytelnia'
405 while i < len(content):
407 if isinstance(e, Section):
408 if czy_czytelnia.match(e.title):
412 content[db:de] = [czytelnia]
415 if isinstance(e, Paragraph):
417 czytelnia.append(e.line)
424 def move_evaluation(content):
427 content = remove_block(content, r"ewaluacja[+ PA\[\].]*", evaluation)
429 # print "found evaluation %s" % (evaluation,)
430 evaluation[0].is_podrozdzial = True
432 opcje_dodatkowe = find_block(content, r"opcje dodatkowe\s*")
434 # print "putting evaluation just before opcje dodatkowe @ %s" % (opcje_dodatkowe, )
435 content[opcje_dodatkowe[0]:opcje_dodatkowe[0]] = evaluation
437 materialy = find_block(content, r"materia.y[+ AP\[\].]*")
439 # print "putting evaluation just before materialy @ %s" % (materialy, )
440 content[materialy[0]:materialy[0]] = evaluation
442 print "er.. no idea where to place evaluation"
446 def toxml(content, pretty_print=False):
447 # some transformations
448 content = mark_activities(content)
449 content = mark_dictionary(content)
450 content = mark_czytelnia(content)
453 content = remove_block(content, r"wykorzyst(yw)?ane metody[+ PA\[\].]*")
457 content = remove_block(content, r"(pomoce|potrzebne materia.y)[+ PA\[\]]*")
460 content = move_evaluation(content)
462 info = content.pop(0)
466 slug = slughifi(meta.get(u'Tytuł modułu', ''))
471 holder['xml'] += u"%s\n" % t
474 p(u'<dc:%s xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">%s</dc:%s>' % (k, v, k))
477 p(u'<%s>%s</%s>' % (tag, ct, tag))
484 p(u'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">')
485 p(u'<rdf:Description rdf:about="http://redakcja.edukacjamedialna.edu.pl/documents/">')
486 authors = map(unicode.strip, meta[u'Autorzy'].split(u','))
487 for author in authors:
488 names = author.split(u' ')
489 lastname = names.pop()
490 names.insert(0, lastname + ",")
491 author = u' '.join(names)
492 dc(u'creator', author)
493 dc(u'title', meta.get(u'Tytuł modułu', u''))
494 dc(u'relation.isPartOf', meta.get(u'Dział', u''))
495 dc(u'publisher', u'Fundacja Nowoczesna Polska')
496 dc(u'subject.competence', meta.get(u'Wybrana kompetencja z Katalogu', u''))
497 dc(u'subject.curriculum', meta.get(u'Odniesienie do podstawy programowej', u''))
498 for keyword in meta.get(u'Słowa kluczowe', u'').split(u','):
499 keyword = keyword.strip()
500 dc(u'subject', keyword)
501 dc(u'description', dc_fixed['description'])
502 dc(u'description.material', dc_fixed['description.material'])
503 dc(u'relation', dc_fixed['relation'])
504 dc(u'identifier.url', u'http://edukacjamedialna.edu.pl/%s' % slug)
505 dc(u'rights', dc_fixed['rights'])
506 dc(u'rights.license', u'http://creativecommons.org/licenses/by-sa/3.0/')
507 dc(u'format', u'xml')
509 dc(u'date', u'2012-11-09') # TODO
510 dc(u'audience', meta.get(u'Poziom edukacyjny', u''))
511 dc(u'language', u'pol')
512 p(u'</rdf:Description>')
516 t(u'nazwa_utworu', meta.get(u'Tytuł modułu', u''))
518 a(u'<!-- Numer porządkowy: %s -->' % meta.get(u'Numer porządkowy', u''))
521 p(unicode(info.title))
523 if isinstance(elm, unicode) or isinstance(elm, str):
532 from lxml import etree
533 from StringIO import StringIO
534 xml = etree.parse(StringIO(holder['xml']))
535 holder['xml'] = etree.tostring(xml, pretty_print=pretty_print, encoding=unicode)
541 # ogarnąć podrozdziały
544 # usunąć 'opis zawartości'