1 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
4 from collections import Counter
10 from .parser import parser
11 from . import dcparser, DCNS, RDFNS, DirDocProvider
12 from .functions import lang_code_3to2
16 def __init__(self, filename=None, url=None, provider=None):
17 source = filename or urllib.request.urlopen(url)
18 tree = etree.parse(source, parser=parser)
20 tree.getroot().document = self
21 self.base_meta = dcparser.BookInfo({}, {
22 DCNS('language'): ["pol"],
23 }, validate_required=False)
25 self.provider = provider if provider is not None else DirDocProvider('.')
27 self.tree.getroot().validate()
31 # Allow metadata of the master element as document meta.
32 #master = self.tree.getroot()[-1]
33 return self.tree.getroot().meta
38 for part_uri in self.meta.parts or []:
39 with self.provider.by_slug(part_uri.slug) as f:
41 yield type(self)(filename=f, provider=self.provider)
42 except Exception as e:
46 def build(self, builder, base_url=None, **kwargs):
47 return builder(base_url=base_url).build(self, **kwargs)
49 def assign_ids(self, existing=None):
50 # Find all existing IDs.
51 existing = existing or set()
52 que = [self.tree.getroot()]
56 item.normalize_insides()
57 except AttributeError:
59 existing.add(item.attrib.get('id'))
63 que = [self.tree.getroot()]
67 if item.attrib.get('id'):
69 if not getattr(item, 'SHOULD_HAVE_ID', False):
71 while f'e{i}' in existing:
73 item.attrib['id'] = f'e{i}'
76 def _compat_assign_ordered_ids(self):
78 Compatibility: ids in document order, to be roughly compatible with legacy
79 footnote ids. Just for testing consistency, change to some sane identifiers
82 EXPR = re.compile(r'/\s', re.MULTILINE | re.UNICODE)
83 def _compat_assign_ordered_ids_in_elem(elem, i):
84 if isinstance(elem, etree._Comment): return i
85 elem.attrib['_compat_ordered_id'] = str(i)
87 if getattr(elem, 'HTML_CLASS', None) == 'stanza':
89 i += len(EXPR.split(elem.text)) - 1
91 i = _compat_assign_ordered_ids_in_elem(sub, i)
93 i += len(EXPR.split(sub.tail)) - 1
95 if elem.tag in ('uwaga', 'extra'):
98 i = _compat_assign_ordered_ids_in_elem(sub, i)
101 _compat_assign_ordered_ids_in_elem(self.tree.getroot(), 4)
103 def _compat_assign_section_ids(self):
105 Ids in master-section order. These need to be compatible with the
106 #secN anchors used by WL search results page to link to fragments.
108 def _compat_assigns_section_ids_in_elem(elem, prefix='sec'):
109 for i, child in enumerate(elem):
110 idfier = '{}{}'.format(prefix, i + 1)
112 child.attrib['_compat_section_id'] = idfier
115 _compat_assigns_section_ids_in_elem(child, idfier + '-')
116 _compat_assigns_section_ids_in_elem(self.tree.getroot().master)
120 persons = set(self.meta.editors
121 + self.meta.technical_editors)
122 for child in self.children:
123 persons.update(child.editors())
128 def references(self):
129 return self.tree.findall('.//ref')
131 def get_statistics(self):
132 def count_text(text, counter, in_fn=False, stanza=False):
134 text = re.sub(r'\s+', ' ', text)
136 chars = len(text) if text.strip() else 0
137 words = len(text.split()) if text.strip() else 0
139 counter['chars_with_fn'] += chars
140 counter['words_with_fn'] += words
142 counter['chars'] += chars
143 counter['words'] += words
145 counter['chars_out_verse_with_fn'] += chars
147 counter['chars_out_verse'] += chars
149 def count(elem, counter, in_fn=False, stanza=False):
150 if elem.tag in (RDFNS('RDF'), 'nota_red', 'abstrakt', 'uwaga', 'ekstra'):
152 if not in_fn and elem.tag in ('pa', 'pe', 'pr', 'pt', 'motyw'):
154 if elem.tag == 'strofa':
156 #verses = len(elem.findall('.//br')) + 1
157 verses = list(elem.get_verses())
158 counter['verses_with_fn'] += len(verses)
160 counter['verses'] += len(verses)
164 count(child, counter, in_fn=in_fn, stanza=True)
166 count_text(elem.text, counter, in_fn=in_fn, stanza=stanza)
168 count(child, counter, in_fn=in_fn, stanza=stanza)
169 count_text(child.tail, counter, in_fn=in_fn, stanza=stanza)
178 count(self.tree.getroot(), data['self'])
179 for k, v in data['self'].items():
182 for part in self.children:
183 if isinstance(part, Exception):
184 data['parts'].append((None, {'error': part}))
186 data['parts'].append((part, part.get_statistics()))
187 for k, v in data['parts'][-1][1]['total'].items():
188 data['total'][k] = data['total'].get(k, 0) + v