Add language tags definition, caret and bubbles in editor.
[redakcja.git] / src / wlxml / models.py
diff --git a/src/wlxml/models.py b/src/wlxml/models.py
new file mode 100644 (file)
index 0000000..0b910a8
--- /dev/null
@@ -0,0 +1,164 @@
+from io import BytesIO
+from django.apps import apps
+from django.core.files import File
+from django.db import models
+from django.urls import reverse
+from django.utils.translation import gettext_lazy as _
+from librarian import DocProvider
+from librarian.parser import WLDocument as LegacyWLDocument
+from librarian.builders import StandaloneHtmlBuilder, TxtBuilder
+from librarian.document import WLDocument
+
+
+class Tag(models.Model):
+    name = models.CharField(max_length=255, unique=True, db_index=True)
+    type = models.CharField(max_length=255, choices=[
+        ('section', _('Section, contains blocks')),
+        ('div', _('Block element, like a paragraph')),
+        ('span', _('Inline element, like an emphasis')),
+        ('sep', _('Separator, has no content')),
+        ('aside', _('Aside content, like a footnote')),
+        ('verse', _('Verse element')),
+    ], blank=True)
+    similar_to = models.ForeignKey('self', models.PROTECT, null=True, blank=True)
+    description = models.TextField(blank=True)
+    example = models.TextField(blank=True)
+
+    example_html = models.FileField(upload_to='wlxml/tag/example/html/', blank=True)
+    example_pdf = models.FileField(upload_to='wlxml/tag/example/pdf/', blank=True)
+    example_txt = models.FileField(upload_to='wlxml/tag/example/txt/', blank=True)
+
+    # border_radius?
+    editor_css = models.TextField(blank=True)
+    editor_css_after = models.TextField(blank=True)
+
+    class Meta:
+        verbose_name = _('tag')
+        verbose_name_plural = _('tags')
+
+    def __str__(self):
+        return self.name
+
+    def get_absolute_url(self):
+        return reverse('wlxml_tag', args=[self.name])
+    ### allowed tags?
+
+    def save(self, **kwargs):
+        docbytes = b'''<utwor>
+
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+<rdf:Description rdf:about="http://redakcja.wolnelektury.pl/documents/book/brudnopis/">
+
+<dc:language xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">pol</dc:language>
+<dc:creator xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:creator>
+<dc:title xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:title>
+<dc:date xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:date>
+<dc:publisher xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:publisher>
+<dc:identifier.url xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:identifier.url>
+<dc:rights xml:lang="pl" xmlns:dc="http://purl.org/dc/elements/1.1/">test</dc:rights>
+
+</rdf:Description>
+</rdf:RDF>
+
+<opowiadanie>''' + self.example.encode('utf-8') + b'</opowiadanie></utwor>'
+
+
+        doc = WLDocument(filename=BytesIO(docbytes))
+
+        self.example_html.save(
+            self.name + '.html',
+            File(
+                StandaloneHtmlBuilder().build(doc).get_file()),
+            save=False)
+        self.example_txt.save(
+            self.name + '.txt',
+            File(
+                TxtBuilder().build(doc).get_file()),
+            save=False)
+
+        provider=DocProvider()
+        legacy_doc = LegacyWLDocument.from_bytes(docbytes, provider=provider)
+
+        self.example_pdf.save(
+            self.name + '.pdf',
+            File(legacy_doc.as_pdf().get_file()),
+            save=False)
+        
+
+        super().save(**kwargs)
+
+    
+
+class Attribute(models.Model):
+    tag = models.ForeignKey(Tag, models.CASCADE)
+    name = models.CharField(max_length=255)
+
+    class Meta:
+        verbose_name = _('attribute')
+        verbose_name_plural = _('attribute')
+    
+        unique_together = [
+            ('tag', 'name'),
+        ]
+
+    def __str__(self):
+        return self.name
+
+    
+class TagUsage(models.Model):
+    tag = models.ForeignKey(Tag, models.CASCADE)
+    chunk = models.ForeignKey('documents.Chunk', models.CASCADE)
+
+    class Meta:
+        verbose_name = _('tag usage')
+        verbose_name_plural = _('tags usage')
+    
+    def __str__(self):
+        return f'{self.tag.name} @ {self.chunk.slug}'
+        
+    
+    @classmethod
+    def update_chunk(cls, chunk):
+        tag_names = set()
+        attribute_items = {}
+        doc = WLDocument.from_bytes(chunk.materialize().encode('utf-8'))
+        for element in doc.edoc.iter():
+            tag_names.add(element.tag)
+            for k, v in element.attrib.iteritems():
+                attribute_items.setdefault(element.tag, set()).add((k, v))
+
+        cls.objects.filter(chunk=chunk).exclude(tag__name__in=tag_names).delete()
+        for tag_name in tag_names:
+            tag, create = Tag.objects.get_or_create(name=tag_name)
+            tu, created = cls.objects.get_or_create(tag=tag, chunk=chunk)
+
+            new_attributes = attribute_items.get(tag_name, [])
+            
+            for attr in tu.attributeusage_set.all():
+                key = (attr.attribute.name, value)
+                if key not in new_attributes:
+                    attr.delete()
+                else:
+                    new_attributes.delete(key)
+
+            for k, v in new_attributes:
+                attribute, created = tag.attribute_set.get_or_create(name=k)
+                tu.attributeusage_set.create(attribute=attribute, value=v)
+
+
+    @classmethod
+    def update_all_chunks(cls):
+        Chunk = apps.get_model('documents', 'Chunk')
+        for chunk in Chunk.objects.all():
+            cls.update_chunk(chunk)
+
+
+class AttributeUsage(models.Model):
+    tag_usage = models.ForeignKey(TagUsage, models.CASCADE)
+    attribute = models.ForeignKey(Attribute, models.CASCADE)
+    value = models.CharField(max_length=2048, blank=True)
+
+    class Meta:
+        verbose_name = _('attribute usage')
+        verbose_name_plural = _('attributes usage')
+