link characters and npcs in texts

This commit is contained in:
Niko Abeler 2023-08-25 22:43:07 +02:00
parent a7294f309a
commit dbd7193c90
9 changed files with 154 additions and 3 deletions

0
conftest.py Normal file
View File

View File

@ -0,0 +1,22 @@
# Generated by Django 4.2.1 on 2023-08-25 20:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("guild", "0014_alter_playsession_npcs"),
]
operations = [
migrations.AlterField(
model_name="character",
name="name",
field=models.CharField(max_length=255, unique=True),
),
migrations.AlterField(
model_name="npc",
name="name",
field=models.CharField(max_length=255, unique=True),
),
]

View File

@ -77,7 +77,7 @@ class Character(models.Model):
RETIRED = "RETIRED", _("Retired") RETIRED = "RETIRED", _("Retired")
UNKNOWN = "UNKNOWN", _("Unknown") UNKNOWN = "UNKNOWN", _("Unknown")
name = models.CharField(max_length=255) name = models.CharField(max_length=255, unique=True)
description = models.TextField() description = models.TextField()
status = models.CharField( status = models.CharField(
max_length=16, choices=Status.choices, default=Status.ALIVE max_length=16, choices=Status.choices, default=Status.ALIVE
@ -106,6 +106,10 @@ class Character(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
@classmethod
def get_by_name(cls, name):
return cls.objects.filter(name=name).get()
def get_absolute_url(self): def get_absolute_url(self):
return reverse("guild:character_detail", kwargs={"pk": self.pk}) return reverse("guild:character_detail", kwargs={"pk": self.pk})
@ -203,7 +207,7 @@ class Reward(models.Model):
class NPC(models.Model): class NPC(models.Model):
name = models.CharField(max_length=255) name = models.CharField(max_length=255, unique=True)
description = models.TextField() description = models.TextField()
picture = models.ImageField( picture = models.ImageField(
@ -222,3 +226,7 @@ class NPC(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse("guild:npc_detail", kwargs={"pk": self.pk}) return reverse("guild:npc_detail", kwargs={"pk": self.pk})
@classmethod
def get_by_name(cls, name):
return cls.objects.filter(name=name).get()

View File

@ -1,6 +1,7 @@
import markdown import markdown
from django import template from django import template
from django.template.defaultfilters import stringfilter from django.template.defaultfilters import stringfilter
from guild_md.entity_links import GuildLinkExtension
register = template.Library() register = template.Library()
@ -8,4 +9,4 @@ register = template.Library()
@register.filter(name="md") @register.filter(name="md")
@stringfilter @stringfilter
def md(value): def md(value):
return markdown.markdown(value) return markdown.markdown(value, extensions=[GuildLinkExtension()])

0
guild_md/__init__.py Normal file
View File

68
guild_md/entity_links.py Normal file
View File

@ -0,0 +1,68 @@
"""
Guild Journal Extension for Python-Markdown
======================================
Converts [[NPC/Frankie]] to links.
Original code Copyright [Waylan Limberg](http://achinghead.com/).
All changes Copyright The Python Markdown Project
License: [BSD](https://opensource.org/licenses/bsd-license.php)
"""
from markdown.extensions import Extension
from markdown.inlinepatterns import InlineProcessor
from guild.models import NPC, Character
import xml.etree.ElementTree as etree
def build_url(category: str, name):
"""Build a URL from the label, a base, and an end."""
if category.lower() == "char":
try:
return Character.get_by_name(name).get_absolute_url()
except Character.DoesNotExist:
return ""
elif category.lower() == "npc":
try:
return NPC.get_by_name(name).get_absolute_url()
except NPC.DoesNotExist:
return ""
return ""
class GuildLinkExtension(Extension):
def extendMarkdown(self, md):
self.md = md
# append to end of inline patterns
GUILD_RE = r"\[\[([\w0-9_ -]+)\/([\w0-9_ -]+)\]\]"
wikilinkPattern = GuildLinksInlineProcessor(GUILD_RE, self.getConfigs())
wikilinkPattern.md = md
md.inlinePatterns.register(wikilinkPattern, "guildlinks", 75)
class GuildLinksInlineProcessor(InlineProcessor):
def __init__(self, pattern, config):
super().__init__(pattern)
self.config = config
def handleMatch(self, m, data):
if m.group(1).strip() and m.group(2).strip():
category = m.group(1).strip()
name = m.group(2).strip()
url = build_url(category, name)
a = etree.Element("a")
a.text = name
a.set("href", url)
if url == "":
a.set("class", "invalid-url")
else:
a = ""
return a, m.start(0), m.end(0)
def makeExtension(**kwargs): # pragma: no cover
return GuildLinkExtension(**kwargs)

4
pytest.ini Normal file
View File

@ -0,0 +1,4 @@
[pytest]
DJANGO_SETTINGS_MODULE = guild_journal.settings
python_files = tests.py test_*.py *_tests.py

0
tests/__init__.py Normal file
View File

View File

@ -0,0 +1,48 @@
import pytest
import markdown
from guild_md.entity_links import GuildLinkExtension
from guild.models import NPC, Character
@pytest.mark.parametrize(
["objs", "md", "html"],
[
[
[],
"",
"",
],
[
[Character(name="Blimm")],
"[[Char/Blimm]]",
'<p><a href="/characters/1/">Blimm</a></p>',
],
[
[Character(name="Blimm")],
"[[Char/Lun]]",
'<p><a class="invalid-url" href="">Lun</a></p>',
],
[
[Character(name="Blimm"), Character(name="Lun")],
"[[Char/Lun]] and [[Char/Blimm]]",
'<p><a href="/characters/2/">Lun</a> and <a href="/characters/1/">Blimm</a></p>',
],
[
[NPC(name="Frank")],
"[[NPC/Frank]]",
'<p><a href="/npcs/1/">Frank</a></p>',
],
[
[NPC(name="Frank"), Character(name="Blimm")],
"[[Char/Blimm]] and [[NPC/Frank]]",
'<p><a href="/characters/1/">Blimm</a> and <a href="/npcs/1/">Frank</a></p>',
],
],
)
@pytest.mark.django_db
def test_link_conversion(objs, md, html):
for obj in objs:
obj.save()
output = markdown.markdown(md, extensions=[GuildLinkExtension()])
assert output == html