tonl parse pretty good

This commit is contained in:
Christoph J. Scherr 2023-10-02 03:06:45 +02:00
parent 3be2f095c0
commit 3bf0dd42c7
7 changed files with 159 additions and 192 deletions

View File

@ -8,3 +8,4 @@ colorlog>=6.7.0
favicon>=0.7.0 favicon>=0.7.0
markdown>=3.4.4 markdown>=3.4.4
Pygments>=2.16.1 Pygments>=2.16.1
toml>=0.10

View File

@ -1,16 +1,3 @@
---
Title: Bash Arrays
Subtitle: sub
Desc: Brief intro to Bash Arrays
Date: 2023-09-29
Keywords: bash
technology
test
Category: Test
Featured: True
Public: True
---
**NOTE** **NOTE**
This is a stolen article from [opensource.com](https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays) This is a stolen article from [opensource.com](https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays)

View File

@ -0,0 +1,8 @@
---
Title: empty
Subtitle: empty
Desc: empty
Date: 2023-09-29
Featured: True
Public: False
---

View File

@ -1,16 +1,3 @@
---
Title: Bash Arrays
Subtitle: sub
Desc: Brief intro to Bash Arrays
Date: 2023-09-29
Thumbnail: img/thumbnails/bash.png
Keywords: bash
technology
Category: Test
Featured: True
Public: True
---
**NOTE** **NOTE**
This is a stolen article from [opensource.com](https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays) This is a stolen article from [opensource.com](https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays)

View File

@ -0,0 +1,24 @@
date = "2023-09-29"
keywords = ["test", "bash"]
category = "Test"
featured = true
public = true
thumbnail = "img/thumbnails/bash.png"
[lang.en]
title = "title"
subtitle = "subtitle"
desc = """
long
multiline
desc
"""
[lang.de]
title = "Titel"
subtitle = "Subtitel"
desc = """
Lange,
mehrzeilige,
Beschreibung
"""

View File

@ -1,3 +1,4 @@
import toml
import ast import ast
import re import re
import os import os
@ -45,6 +46,14 @@ class Category(models.Model):
def __str__(self): def __str__(self):
return f"{{<{self.__class__.__name__}>\"{self.name}\"}}" return f"{{<{self.__class__.__name__}>\"{self.name}\"}}"
@staticmethod
def get_or_create_uncategorized():
try:
return Category.objects.get(slug="uncategorized")
except Category.DoesNotExist:
return Category.objects.create(
slug="uncategorized", name="uncategorized")
class BlogPost(Searchable): class BlogPost(Searchable):
""" """
@ -53,7 +62,8 @@ class BlogPost(Searchable):
body_en = models.TextField(blank=True, default="") body_en = models.TextField(blank=True, default="")
body_de = models.TextField(blank=True, default="") body_de = models.TextField(blank=True, default="")
category = models.ForeignKey( category = models.ForeignKey(
Category, on_delete=models.SET_NULL, null=True) Category, on_delete=models.SET_DEFAULT, null=False,
default=Category.get_or_create_uncategorized)
thumbnail = models.ImageField( thumbnail = models.ImageField(
blank=True, blank=True,
upload_to="img/thumbnails", upload_to="img/thumbnails",
@ -67,12 +77,14 @@ class BlogPost(Searchable):
DATA_DIR = "/app/blog/data/articles" DATA_DIR = "/app/blog/data/articles"
DEFAULT_LANGS = {'en': False, 'de': False} DEFAULT_LANGS = {'en': False, 'de': False}
META_TOP_KEYS = [
def has_keywords(self) -> bool: "date",
""" "keywords",
check if the post has keywords "category",
""" "featured",
return self.keywords.first() is not None "public",
"lang"]
META_LANG_KEYS = ["title", "subtitle", "desc"]
def regenerate(self): def regenerate(self):
""" """
@ -85,130 +97,80 @@ class BlogPost(Searchable):
self.suburl = f"/blog/{self.category.name}/{self.slug}" self.suburl = f"/blog/{self.category.name}/{self.slug}"
# load from markdown # load from markdown
self.sync_file() # self.sync_file()
# redundand vvvv
self.save() self.save()
def sync_file(self): def sync_file(self):
""" """
generate an article fromm it's original markdown file generate an article fromm it's original markdown file
""" """
logger.info(f"regenerating article from markdown for: {self}") logger = logging.getLogger(__name__)
logger.info(f"syncing article to markdown for: {self}")
# read metadata
try: try:
MD.reset() fmeta = open(f"{self.DATA_DIR}/{self.slug}.toml", "r")
with open(f"{self.DATA_DIR}/en-{self.slug}.md") as f_en:
body_en: str = f_en.read()
html_en: str = MD.convert(body_en)
try:
# NOTE: MD.Meta is generated after MD.convert() by the meta
# extension.
# Meta not being shown is a reported issue:
# https://github.com/Python-Markdown/markdown/issues/1383
if not hasattr(MD, 'Meta'):
logger.error("Metadata extension for markdown\
not loaded")
raise ValueError("Metadata extension for markdown\
not loaded")
meta_en = MD.Meta
self.title_en = meta_en["title"][0]
self.subtitle_en = meta_en["subtitle"][0]
self.desc_en = meta_en["desc"][0]
self.date = meta_en["date"][0]
self.featured = meta_en["featured"][0] == "True"
self.public = meta_en["public"][0] == "True"
if "thumbnail" in meta_en:
self.thumbnail = meta_en["thumbnail"][0]
try:
category: Category = Category.objects.get(
slug=meta_en['category'][0])
except Category.DoesNotExist:
category = Category.objects.create(
name=meta_en['category'], slug=meta_en['category'])
self.category = category
# NOTE: we need to save before we can use the manytomany
# logic.
self.save()
for item in meta_en["keywords"]:
try:
self.keywords.add(Keyword.objects.get(slug=item))
except Keyword.DoesNotExist:
self.keywords.create(
slug=item, text_en=item, text_de=item)
logger.debug(f"keywords of '{self}': {self.keywords}")
except Exception as e:
logger.warning(
f"could not generate metadata {self.slug} from markdown: {e}")
self.body_en = ""
self.body_en = html_en
except FileNotFoundError as e:
# TODO: mark as untranslated
pass
except Exception as e: except Exception as e:
logger.warning( logger.error(f"could not find meta file for '{self}'")
f"could not generate article {self.slug} from markdown: {e}") return
try: data = toml.load(fmeta)
MD.reset() for key in self.META_TOP_KEYS:
with open(f"{self.DATA_DIR}/de-{self.slug}.md") as f_de: if key not in data:
logger.error(f"Key '{key}' missing in meta file for '{self}'")
body_de: str = f_de.read() raise ValueError(
f"Key '{key}' missing in meta file for '{self}'")
html_de: str = MD.convert(body_de) for lang in data['lang']:
try: for key in self.META_LANG_KEYS:
# NOTE: MD.Meta is generated after MD.convert() by the meta if key not in data['lang'][lang]:
# extension. logger.error(
# Meta not being shown is a reported issue: f"Key '{key}' ('{lang}') missing in meta file for '{self}'")
# https://github.com/Python-Markdown/markdown/issues/1383 raise ValueError(
if not hasattr(MD, 'Meta'): f"Key '{key}' ('{lang}') missing in meta file for '{self}'")
logger.error("Metadata extension for markdown\ with open(f"{self.DATA_DIR}/{lang}-{self.slug}.md") as f_en:
not loaded") MD.reset()
raise ValueError("Metadata extension for markdown\ body: str = f_en.read()
not loaded") # FIXME:
meta_de = MD.Meta # these fields are loaded but wrong?
self.title_de = meta_de["title"][0] # de is in en, de is empty
self.subtitle_de = meta_de["subtitle"][0] match lang:
self.desc_de = meta_de["desc"][0] case "en":
# TODO: parse date from markdown self.title_en = data['lang'][lang]["title"]
self.featured = meta_de["featured"][0] == "True" self.subtitle_en = data['lang'][lang]["subtitle"]
self.public = meta_de["public"][0] == "True" self.desc_en = data['lang'][lang]["desc"]
if "thumbnail" in meta_de: self.body_en = MD.convert(body)
self.thumbnail = meta_de["thumbnail"][0] case "de":
self.title_en = data['lang'][lang]["title"]
try: self.subtitle_en = data['lang'][lang]["subtitle"]
category: Category = Category.objects.get( self.desc_en = data['lang'][lang]["desc"]
slug=meta_de['category'][0]) self.body_de = MD.convert(body)
except Category.DoesNotExist: case _:
category = Category.objects.create( logger.error(
name=meta_de['category'], slug=meta_de['category']) f"unknown language '{lang}' in meta file for '{self}'")
self.category = category self.date = data["date"]
self.featured = data["featured"]
# NOTE: we need to save before we can use the manytomany self.public = data["public"]
# logic. # NOTE: thumbnail is optional
self.save() if "thumbnail" in data:
for item in meta_de["keywords"]: self.thumbnail = data["thumbnail"]
try: # NOTE: thumbnail is optional
self.keywords.add(Keyword.objects.get(slug=item)) if "category" in data:
except Keyword.DoesNotExist: try:
self.keywords.create( category: Category = Category.objects.get(
slug=item, text_en=item, text_de=item) slug=data['category'])
logger.debug(f"keywords of '{self}': {self.keywords}") except Category.DoesNotExist:
except Exception as e: category = Category.objects.create(
logger.warning( name=data['category'], slug=data['category'])
f"could not generate metadata {self.slug} from markdown: {e}") self.category = category
self.save()
self.body_de = "" logger.debug("keywords next")
self.body_de = html_de for keyword in data["keywords"]:
except FileNotFoundError as e: try:
# TODO: mark as untranslated self.keywords.add(Keyword.objects.get(slug=keyword))
pass except Keyword.DoesNotExist:
except Exception as e: self.keywords.create(
logger.warning( slug=keyword, text_en=keyword, text_de=keyword)
f"could not generate article {self.slug} from markdown: {e}") logger.debug(f"keywords of '{self}': {self.keywords}")
self.save() self.save()
def get_langs(self) -> dict[str, bool]: def get_langs(self) -> dict[str, bool]:

View File

@ -7,48 +7,46 @@
<div class="card col m-2 p-0"> <div class="card col m-2 p-0">
<a class="text-reset link-offset-2 link-underline link-underline-opacity-0" <a class="text-reset link-offset-2 link-underline link-underline-opacity-0"
href=" {% url 'blog:post' post.category.slug post.slug %}"> href=" {% url 'blog:post' post.category.slug post.slug %}">
<img src="{{ post.thumbnail.url }}" <img src="{{ post.thumbnail.url }}"
class="card-img-top img-fluid" class="card-img-top img-fluid"
style="max-height: 150px" style="max-height: 150px"
alt="thumbnail" /> alt="thumbnail" />
<div class="card-body" style="height: 100px"> <div class="card-body" style="height: 100px">
{% if LANGUAGE_CODE == "de" %} {% if LANGUAGE_CODE == "de" %}
<h5 class="card-title"> <h5 class="card-title">
{{ post.title_de }}<small class="text-body-secondary">{{ post.subtitle }}</small> {{ post.title_de }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5> </h5>
<p class="card-text">{{ post.desc_de }}</p> <p class="card-text">{{ post.desc_de }}</p>
{% elif LANGUAGE_CODE == "en" %} {% elif LANGUAGE_CODE == "en" %}
<h5 class="card-title"> <h5 class="card-title">
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small> {{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5> </h5>
<p class="card-text">{{ post.desc_en }}</p> <p class="card-text">{{ post.desc_en }}</p>
{% else %} {% else %}
<h5 class="card-title"> <h5 class="card-title">
{{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small> {{ post.title_en }}<small class="text-body-secondary">{{ post.subtitle }}</small>
</h5> </h5>
<p class="card-text">{{ post.desc_en }}</p> <p class="card-text">{{ post.desc_en }}</p>
{% endif %} {% endif %}
</div> </div>
<div class="container pt-5"> <div class="container pt-5">
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
<li class="list-group-item"> <li class="list-group-item">
{% translate "category" %}: <b>{{ post.category.name }}</b> {% translate "category" %}: <b>{{ post.category.name }}</b>
</li> </li>
{% if post.has_keywords %} {% for keyword in post.keywords.all %}
{% for keyword in post.keywords.all %} {% if LANGUAGE_CODE == "de" %}
{% if LANGUAGE_CODE == "de" %} <li class="list-group-item">{{ keyword.text_de }}</li>
<li class="list-group-item">{{ keyword.text_de }}</li> {% elif LANGUAGE_CODE == "en" %}
{% elif LANGUAGE_CODE == "en" %} <li class="list-group-item">{{ keyword.text_en }}</li>
<li class="list-group-item">{{ keyword.text_en }}</li> {% else %}
{% else %} <li class="list-group-item">{{ keyword.text_en }}</li>
<li class="list-group-item">{{ keyword.text_en }}</li> {% endif %}
{% endif %} {% endfor %}
{% endfor %} </ul>
{% endif %} </div>
</ul> </a>
</div> </div>
</a> {% endfor %}
</div> </div>
{% endfor %}
</div>
</div> </div>