2 from django.apps import apps
3 from django.core.files import File
4 from django.db import models
5 from django.urls import reverse
6 from django.utils.translation import gettext_lazy as _
7 from librarian import DocProvider
8 from librarian.parser import WLDocument as LegacyWLDocument
9 from librarian.builders import StandaloneHtmlBuilder, TxtBuilder
10 from librarian.document import WLDocument
13 class Tag(models.Model):
14 name = models.CharField(max_length=255, unique=True, db_index=True)
15 type = models.CharField(max_length=255, choices=[
16 ('section', _('Section, contains blocks')),
17 ('div', _('Block element, like a paragraph')),
18 ('span', _('Inline element, like an emphasis')),
19 ('sep', _('Separator, has no content')),
20 ('aside', _('Aside content, like a footnote')),
21 ('verse', _('Verse element')),
23 similar_to = models.ForeignKey('self', models.PROTECT, null=True, blank=True)
24 description = models.TextField(blank=True)
25 example = models.TextField(blank=True)
27 example_html = models.FileField(upload_to='wlxml/tag/example/html/', blank=True)
28 example_pdf = models.FileField(upload_to='wlxml/tag/example/pdf/', blank=True)
29 example_txt = models.FileField(upload_to='wlxml/tag/example/txt/', blank=True)
32 editor_css = models.TextField(blank=True)
33 editor_css_after = models.TextField(blank=True)
36 verbose_name = _('tag')
37 verbose_name_plural = _('tags')
42 def get_absolute_url(self):
43 return reverse('wlxml_tag', args=[self.name])
46 def save(self, **kwargs):
47 docbytes = b'''<utwor>
49 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
50 <rdf:Description rdf:about="http://redakcja.wolnelektury.pl/documents/book/brudnopis/">
52 <dc:language xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">pol</dc:language>
53 <dc:creator xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:creator>
54 <dc:title xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:title>
55 <dc:date xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:date>
56 <dc:publisher xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:publisher>
57 <dc:identifier.url xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:identifier.url>
58 <dc:rights xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:rights>
63 <opowiadanie>''' + self.example.encode('utf-8') + b'</opowiadanie></utwor>'
66 doc = WLDocument(filename=BytesIO(docbytes))
68 self.example_html.save(
71 StandaloneHtmlBuilder().build(doc).get_file()),
73 self.example_txt.save(
76 TxtBuilder().build(doc).get_file()),
79 provider=DocProvider()
80 legacy_doc = LegacyWLDocument.from_bytes(docbytes, provider=provider)
82 self.example_pdf.save(
84 File(legacy_doc.as_pdf().get_file()),
88 super().save(**kwargs)
92 class Attribute(models.Model):
93 tag = models.ForeignKey(Tag, models.CASCADE)
94 name = models.CharField(max_length=255)
97 verbose_name = _('attribute')
98 verbose_name_plural = _('attribute')
108 class TagUsage(models.Model):
109 tag = models.ForeignKey(Tag, models.CASCADE)
110 chunk = models.ForeignKey('documents.Chunk', models.CASCADE)
113 verbose_name = _('tag usage')
114 verbose_name_plural = _('tags usage')
117 return f'{self.tag.name} @ {self.chunk.slug}'
121 def update_chunk(cls, chunk):
124 doc = WLDocument.from_bytes(chunk.materialize().encode('utf-8'))
125 for element in doc.edoc.iter():
126 tag_names.add(element.tag)
127 for k, v in element.attrib.iteritems():
128 attribute_items.setdefault(element.tag, set()).add((k, v))
130 cls.objects.filter(chunk=chunk).exclude(tag__name__in=tag_names).delete()
131 for tag_name in tag_names:
132 tag, create = Tag.objects.get_or_create(name=tag_name)
133 tu, created = cls.objects.get_or_create(tag=tag, chunk=chunk)
135 new_attributes = attribute_items.get(tag_name, [])
137 for attr in tu.attributeusage_set.all():
138 key = (attr.attribute.name, value)
139 if key not in new_attributes:
142 new_attributes.delete(key)
144 for k, v in new_attributes:
145 attribute, created = tag.attribute_set.get_or_create(name=k)
146 tu.attributeusage_set.create(attribute=attribute, value=v)
150 def update_all_chunks(cls):
151 Chunk = apps.get_model('documents', 'Chunk')
152 for chunk in Chunk.objects.all():
153 cls.update_chunk(chunk)
156 class AttributeUsage(models.Model):
157 tag_usage = models.ForeignKey(TagUsage, models.CASCADE)
158 attribute = models.ForeignKey(Attribute, models.CASCADE)
159 value = models.CharField(max_length=2048, blank=True)
162 verbose_name = _('attribute usage')
163 verbose_name_plural = _('attributes usage')