2023-12-06 23:30:01 +00:00
|
|
|
from django.db import models
|
2024-03-24 21:59:25 +00:00
|
|
|
import markdown
|
2024-03-31 15:02:48 +01:00
|
|
|
from django.utils.functional import cached_property
|
2024-10-11 17:32:46 +01:00
|
|
|
from django.core.files.storage import storages
|
|
|
|
from django.core.files import File
|
|
|
|
from pathlib import Path
|
|
|
|
import os.path
|
2024-10-17 14:13:17 +01:00
|
|
|
from django.template import engines
|
|
|
|
from django_jinja.backend import Origin
|
2024-10-17 22:05:46 +01:00
|
|
|
from django.conf import settings
|
2023-12-06 23:30:01 +00:00
|
|
|
|
2024-01-10 21:59:54 +00:00
|
|
|
class Tag(models.Model):
|
|
|
|
__yamdl__ = True
|
|
|
|
|
|
|
|
slug = models.SlugField(max_length=128, primary_key=True)
|
2024-10-11 15:35:54 +01:00
|
|
|
file_path = models.CharField(max_length=255)
|
2024-01-10 21:59:54 +00:00
|
|
|
|
|
|
|
content = models.TextField()
|
|
|
|
|
2024-10-11 17:32:46 +01:00
|
|
|
def get_page_upload_to(instance, filename):
|
|
|
|
return f"{instance._meta.verbose_name}/{instance.slug}/{os.path.basename(filename)}"
|
2024-01-10 21:59:54 +00:00
|
|
|
|
2023-12-06 23:30:01 +00:00
|
|
|
class Page(models.Model):
|
|
|
|
__yamdl__ = True
|
|
|
|
|
2024-03-31 15:02:48 +01:00
|
|
|
_template_cache = {}
|
|
|
|
|
2023-12-06 23:30:01 +00:00
|
|
|
title = models.CharField(max_length=255)
|
|
|
|
|
2024-03-24 21:59:25 +00:00
|
|
|
raw_content = models.TextField()
|
2024-08-03 14:02:33 +01:00
|
|
|
runtime_render = models.BooleanField(default=False)
|
2023-12-06 23:30:01 +00:00
|
|
|
content = models.TextField()
|
2024-04-30 21:23:21 +01:00
|
|
|
toc = models.JSONField()
|
2024-01-10 21:59:54 +00:00
|
|
|
slug = models.CharField(max_length=128, unique=True, db_index=True, default=None, null=True)
|
2024-10-11 15:35:54 +01:00
|
|
|
file_path = models.CharField(max_length=255)
|
2024-01-10 21:59:54 +00:00
|
|
|
|
2024-10-11 17:32:46 +01:00
|
|
|
media = models.FileField(null=True, default=None, storage=storages["yamdl"], upload_to=get_page_upload_to)
|
|
|
|
|
2024-01-10 21:59:54 +00:00
|
|
|
tags = models.ManyToManyField(Tag)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_yaml(cls, **data):
|
|
|
|
tags = data.pop("tags", None)
|
2024-03-24 21:59:25 +00:00
|
|
|
|
2024-04-30 21:23:21 +01:00
|
|
|
md = markdown.Markdown(extensions=["toc"])
|
|
|
|
|
2024-03-24 21:59:25 +00:00
|
|
|
content = data.pop("content")
|
|
|
|
data["raw_content"] = content
|
2024-04-30 21:23:21 +01:00
|
|
|
|
|
|
|
data["content"] = md.convert(content)
|
2024-08-03 14:02:33 +01:00
|
|
|
|
2024-04-30 21:23:21 +01:00
|
|
|
data["toc"] = {
|
|
|
|
"html": md.toc,
|
|
|
|
"tokens": md.toc_tokens
|
|
|
|
}
|
2024-03-24 21:59:25 +00:00
|
|
|
|
2024-10-11 17:32:46 +01:00
|
|
|
if data.get("media"):
|
|
|
|
source_dir = Path(data["file_path"]).parent
|
|
|
|
|
|
|
|
data["media"] = File((source_dir / data["media"]).open("rb"))
|
|
|
|
|
2024-08-03 14:02:33 +01:00
|
|
|
instance = cls(**data)
|
|
|
|
|
|
|
|
if not instance.runtime_render:
|
|
|
|
instance.content = instance.render_content()
|
|
|
|
|
|
|
|
instance.save()
|
2024-01-10 21:59:54 +00:00
|
|
|
|
|
|
|
if tags:
|
|
|
|
instance.tags.set(tags)
|
|
|
|
|
|
|
|
return instance
|
2024-03-31 15:02:48 +01:00
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def content_template(self):
|
2024-10-17 14:13:17 +01:00
|
|
|
if cached_template := self._template_cache.get(self.slug):
|
|
|
|
return cached_template
|
|
|
|
|
2024-10-17 22:05:46 +01:00
|
|
|
template_name = Path(self.file_path).relative_to(settings.YAMDL_DIRECTORIES[0])
|
|
|
|
|
2024-10-17 14:13:17 +01:00
|
|
|
template = engines["jinja2"].from_string(self.content)
|
2024-10-17 22:05:46 +01:00
|
|
|
template.origin = Origin(name=self.file_path, template_name=template_name)
|
|
|
|
template.name = template.template.name = template_name
|
2024-10-17 14:13:17 +01:00
|
|
|
|
|
|
|
if not self.runtime_render:
|
|
|
|
self._template_cache[self.slug] = template
|
2024-08-03 14:02:33 +01:00
|
|
|
|
2024-10-17 14:13:17 +01:00
|
|
|
return template
|
2024-08-03 14:02:33 +01:00
|
|
|
|
|
|
|
def get_context(self):
|
|
|
|
return {
|
|
|
|
"page": self
|
|
|
|
}
|
|
|
|
|
|
|
|
def render_content(self, extra_context=None):
|
|
|
|
if extra_context is None:
|
|
|
|
extra_context = {}
|
|
|
|
|
2024-10-17 14:13:17 +01:00
|
|
|
return self.content_template.render({**self.get_context(), **extra_context})
|