Librarian in regular requirements.
[redakcja.git] / apps / catalogue / xml_tools.py
1 # -*- coding: utf-8 -*-
2 from copy import deepcopy
3 import re
4
5 from lxml import etree
6 from catalogue.constants import TRIM_BEGIN, TRIM_END, MASTERS
7
8 RE_TRIM_BEGIN = re.compile("^<!--%s-->$" % TRIM_BEGIN, re.M)
9 RE_TRIM_END = re.compile("^<!--%s-->$" % TRIM_END, re.M)
10
11
12 class ParseError(BaseException):
13     pass
14
15
16 def _trim(text, trim_begin=True, trim_end=True):
17     """ 
18         Cut off everything before RE_TRIM_BEGIN and after RE_TRIM_END, so
19         that eg. one big XML file can be compiled from many small XML files.
20     """
21     if trim_begin:
22         text = RE_TRIM_BEGIN.split(text, maxsplit=1)[-1]
23     if trim_end:
24         text = RE_TRIM_END.split(text, maxsplit=1)[0]
25     return text
26
27
28 def compile_text(parts):
29     """ 
30         Compiles full text from an iterable of parts,
31         trimming where applicable.
32     """
33     texts = []
34     trim_begin = False
35     text = ''
36     for next_text in parts:
37         if not next_text:
38             continue
39         if text:
40             # trim the end, because there's more non-empty text
41             # don't trim beginning, if `text' is the first non-empty part
42             texts.append(_trim(text, trim_begin=trim_begin))
43             trim_begin = True
44         text = next_text
45     # don't trim the end, because there's no more text coming after `text'
46     # only trim beginning if it's not still the first non-empty
47     texts.append(_trim(text, trim_begin=trim_begin, trim_end=False))
48     return "".join(texts)
49
50
51 def add_trim_begin(text):
52     trim_tag = etree.Comment(TRIM_BEGIN)
53     e = etree.fromstring(text)
54     for master in e[::-1]:
55         if master.tag in MASTERS:
56             break
57     if master.tag not in MASTERS:
58         raise ParseError('No master tag found!')
59
60     master.insert(0, trim_tag)
61     trim_tag.tail = '\n\n\n' + (master.text or '')
62     master.text = '\n'
63     return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
64
65
66 def add_trim_end(text):
67     trim_tag = etree.Comment(TRIM_END)
68     e = etree.fromstring(text)
69     for master in e[::-1]:
70         if master.tag in MASTERS:
71             break
72     if master.tag not in MASTERS:
73         raise ParseError('No master tag found!')
74
75     master.append(trim_tag)
76     trim_tag.tail = '\n'
77     prev = trim_tag.getprevious()
78     if prev is not None:
79         prev.tail = (prev.tail or '') + '\n\n\n'
80     else:
81         master.text = (master.text or '') + '\n\n\n'
82     return unicode(etree.tostring(e, encoding="utf-8"), 'utf-8')
83
84
85 def split_xml(text):
86     """Splits text into chapters.
87
88     All this stuff really must go somewhere else.
89
90     """
91     src = etree.fromstring(text)
92     chunks = []
93
94     splitter = u'naglowek_rozdzial'
95     parts = src.findall('.//naglowek_rozdzial')
96     while parts:
97         # copy the document
98         copied = deepcopy(src)
99
100         element = parts[-1]
101
102         # find the chapter's title
103         name_elem = deepcopy(element)
104         for tag in 'extra', 'motyw', 'pa', 'pe', 'pr', 'pt', 'uwaga':
105             for a in name_elem.findall('.//' + tag):
106                 a.text=''
107                 del a[:]
108         name = etree.tostring(name_elem, method='text', encoding='utf-8').strip()
109
110         # in the original, remove everything from the start of the last chapter
111         parent = element.getparent()
112         del parent[parent.index(element):]
113         element, parent = parent, parent.getparent()
114         while parent is not None:
115             del parent[parent.index(element) + 1:]
116             element, parent = parent, parent.getparent()
117
118         # in the copy, remove everything before the last chapter
119         element = copied.findall('.//naglowek_rozdzial')[-1]
120         parent = element.getparent()
121         while parent is not None:
122             parent.text = None
123             while parent[0] is not element:
124                 del parent[0]
125             element, parent = parent, parent.getparent()
126         chunks[:0] = [[name,
127             unicode(etree.tostring(copied, encoding='utf-8'), 'utf-8')
128             ]]
129
130         parts = src.findall('.//naglowek_rozdzial')
131
132     chunks[:0] = [[u'poczÄ…tek',
133         unicode(etree.tostring(src, encoding='utf-8'), 'utf-8')
134         ]]
135
136     for ch in chunks[1:]:
137         ch[1] = add_trim_begin(ch[1])
138     for ch in chunks[:-1]:
139         ch[1] = add_trim_end(ch[1])
140
141     return chunks