1
0
Fork 0
mirror of https://github.com/Findus23/RPGnotes.git synced 2024-09-19 15:43:45 +02:00

store linked pages in DB and show in graph

This commit is contained in:
Lukas Winkler 2022-11-25 19:43:58 +01:00
parent 19aa3e0722
commit 1d7d713243
Signed by: lukas
GPG key ID: 54DE4D798D244853
12 changed files with 177 additions and 15 deletions

View file

@ -0,0 +1,23 @@
# Generated by Django 4.1.3 on 2022-11-25 18:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("characters", "0016_remove_character_nickname_and_more"),
]
operations = [
migrations.AddField(
model_name="character",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
migrations.AddField(
model_name="historicalcharacter",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
]

View file

@ -36,9 +36,10 @@ class Command(BaseCommand):
objects.extend(list(Note.objects.all())) objects.extend(list(Note.objects.all()))
objects.extend(list(IngameDay.objects.all())) objects.extend(list(IngameDay.objects.all()))
for object in objects: for object in objects:
fresh_html = md_to_html(object.description_md, replacements=replacements) fresh_html, linked_objects = md_to_html(object.description_md, replacements=replacements)
if object.description_html != fresh_html: if object.description_html != fresh_html:
print_diff_call(object.description_html, fresh_html, str(object)) print_diff_call(object.description_html, fresh_html, str(object))
if store: if store:
object.description_html = fresh_html object.description_html = fresh_html
object.linked_objects = ",".join(linked_objects)
object.save() object.save()

View file

@ -7,11 +7,13 @@ from utils.markdown import md_to_html
class DescriptionModel(models.Model): class DescriptionModel(models.Model):
description_md = models.TextField(_("Description"), blank=True) description_md = models.TextField(_("Description"), blank=True)
description_html = models.TextField(_("Description (HTML)"), blank=True, editable=False) description_html = models.TextField(_("Description (HTML)"), blank=True, editable=False)
linked_objects = models.TextField(max_length=1000, blank=True, default="")
class Meta: class Meta:
abstract = True abstract = True
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.description_html = md_to_html(self.description_md) self.description_html, linked_objects = md_to_html(self.description_md)
self.linked_objects = ",".join(linked_objects)
super(DescriptionModel, self).save(*args, **kwargs) super(DescriptionModel, self).save(*args, **kwargs)

View file

@ -28,20 +28,20 @@ class ColorsTests(SimpleTestCase):
class MarkdownTests(SimpleTestCase): class MarkdownTests(SimpleTestCase):
def test_basic_markdown(self): def test_basic_markdown(self):
self.assertHTMLEqual( self.assertHTMLEqual(
md_to_html("**test** *it*", replacements={}), md_to_html("**test** *it*", replacements={})[0],
"<p><strong>test</strong> <em>it</em></p>" "<p><strong>test</strong> <em>it</em></p>"
) )
def test_nb_md(self): def test_nb_md(self):
self.assertHTMLEqual( self.assertHTMLEqual(
md_to_html("This\nis\nTest", replacements={}), md_to_html("This\nis\nTest", replacements={})[0],
"<p>This<br>is<br>Test</p>" "<p>This<br>is<br>Test</p>"
) )
def test_bleach(self): def test_bleach(self):
self.assertEqual( self.assertEqual(
md_to_html( md_to_html(
"<script>console.log()</script> <a onclick='console.log()'>Hi</button>", replacements={}), "<script>console.log()</script> <a onclick='console.log()'>Hi</button>", replacements={})[0],
"&lt;script&gt;console.log()&lt;/script&gt;\n<p><a>Hi&lt;/button&gt;</a></p>" "&lt;script&gt;console.log()&lt;/script&gt;\n<p><a>Hi&lt;/button&gt;</a></p>"
) )

View file

@ -0,0 +1,23 @@
# Generated by Django 4.1.3 on 2022-11-25 18:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("days", "0006_alter_historicalsession_date_alter_session_date"),
]
operations = [
migrations.AddField(
model_name="historicalingameday",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
migrations.AddField(
model_name="ingameday",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 4.1.3 on 2022-11-25 18:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("factions", "0004_faction_aliases_historicalfaction_aliases"),
]
operations = [
migrations.AddField(
model_name="faction",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
migrations.AddField(
model_name="historicalfaction",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
]

View file

@ -37,6 +37,12 @@ class Graph:
"target": target.graphkey "target": target.graphkey
}) })
def add_edge_str(self, source: str, target: str):
self.edges.append({
"source": source,
"target": target
})
def prune(self) -> None: def prune(self) -> None:
connected_nodes = set() connected_nodes = set()
for e in self.edges: for e in self.edges:
@ -62,14 +68,22 @@ class GraphView(TemplateView):
template_name = "graph/graph.jinja" template_name = "graph/graph.jinja"
def get_description_links(el: GraphModelEl, g: Graph):
if el.linked_objects:
for lo in el.linked_objects.split(","):
g.add_edge_str(el.graphkey, lo)
def get_graph(request: HttpRequest) -> HttpResponse: def get_graph(request: HttpRequest) -> HttpResponse:
g = Graph() g = Graph()
for loc in list(Location.objects.all()) + list(Note.objects.all()): for loc in list(Location.objects.all()) + list(Note.objects.all()):
g.add_node(loc) g.add_node(loc)
if loc.parent: if loc.parent:
g.add_edge(loc, loc.parent) g.add_edge(loc, loc.parent)
get_description_links(loc, g)
for faction in Faction.objects.all(): for faction in Faction.objects.all():
g.add_node(faction, faction.name) g.add_node(faction, faction.name)
get_description_links(faction, g)
for user in TenantUser.objects \ for user in TenantUser.objects \
.filter(tenants=connection.get_tenant()) \ .filter(tenants=connection.get_tenant()) \
.exclude(pk__in=[1, 2]): .exclude(pk__in=[1, 2]):
@ -82,6 +96,7 @@ def get_graph(request: HttpRequest) -> HttpResponse:
g.add_edge(char, char.faction) g.add_edge(char, char.faction)
if char.player: if char.player:
g.add_edge(char, char.player) g.add_edge(char, char.player)
get_description_links(char, g)
for loottype in LootType.objects.all(): for loottype in LootType.objects.all():
g.add_node(loottype) g.add_node(loottype)
@ -94,6 +109,8 @@ def get_graph(request: HttpRequest) -> HttpResponse:
g.add_edge(loot, loot.owner) g.add_edge(loot, loot.owner)
if loot.type: if loot.type:
g.add_edge(loot, loot.type) g.add_edge(loot, loot.type)
get_description_links(loot, g)
g.prune() g.prune()
return JsonResponse(g.export()) return JsonResponse(g.export())

View file

@ -0,0 +1,23 @@
# Generated by Django 4.1.3 on 2022-11-25 18:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("locations", "0010_historicallocation_aliases_location_aliases"),
]
operations = [
migrations.AddField(
model_name="historicallocation",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
migrations.AddField(
model_name="location",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 4.1.3 on 2022-11-25 18:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("loot", "0015_loottype_slug"),
]
operations = [
migrations.AddField(
model_name="historicalloot",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
migrations.AddField(
model_name="loot",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
]

View file

@ -0,0 +1,23 @@
# Generated by Django 4.1.3 on 2022-11-25 18:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("notes", "0005_historicalnote_aliases_note_aliases"),
]
operations = [
migrations.AddField(
model_name="historicalnote",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
migrations.AddField(
model_name="note",
name="linked_objects",
field=models.TextField(blank=True, default="", max_length=1000),
),
]

View file

@ -1,5 +1,6 @@
import re import re
from html.parser import HTMLParser from html.parser import HTMLParser
from typing import Tuple, Set
import bleach import bleach
import markdown import markdown
@ -8,8 +9,8 @@ from bleach_allowlist import markdown_tags, markdown_attrs
custom_allowed_tags = ["del", "ins"] custom_allowed_tags = ["del", "ins"]
def md_to_html(md: str, replacements=None) -> str: def md_to_html(md: str, replacements=None) -> Tuple[str, Set[str]]:
md = autolink(md, replacements=replacements) md, linked_objects = autolink(md, replacements=replacements)
html = markdown.markdown( html = markdown.markdown(
md, md,
output_format="html", output_format="html",
@ -22,25 +23,28 @@ def md_to_html(md: str, replacements=None) -> str:
tags=markdown_tags + custom_allowed_tags, tags=markdown_tags + custom_allowed_tags,
attributes=markdown_attrs attributes=markdown_attrs
) )
return html return html, linked_objects
def autolink(md: str, replacements=None) -> str: def autolink(md: str, replacements=None) -> Tuple[str, Set[str]]:
if replacements is None: if replacements is None:
from utils.urls import name2url from utils.urls import name2url
replacements = name2url() replacements = name2url()
links = {} links = {}
linked_objects = set()
i = 0 i = 0
for name, url in replacements.items(): for name, (url, obj) in replacements.items():
regex = r"\bWORD\b".replace("WORD", name) regex = r"\bWORD\b".replace("WORD", name)
placeholder = f"SOME{i}LINK" placeholder = f"SOME{i}LINK"
md = re.sub(regex, placeholder, md) md, n_replacements = re.subn(regex, placeholder, md)
if n_replacements > 0:
linked_objects.add(obj.graphkey)
links[placeholder] = f"[{name}]({url})" links[placeholder] = f"[{name}]({url})"
i += 1 i += 1
for placeholder, value in links.items(): for placeholder, value in links.items():
md = md.replace(placeholder, value) md = md.replace(placeholder, value)
return md return md, linked_objects
class HTMLFilter(HTMLParser): class HTMLFilter(HTMLParser):

View file

@ -9,17 +9,17 @@ from notes.models import Note
def name2url() -> Dict[str, str]: def name2url() -> Dict[str, str]:
data = {} data = {}
objects=[] objects = []
objects.extend(Character.objects.all()) objects.extend(Character.objects.all())
objects.extend(Location.objects.all()) objects.extend(Location.objects.all())
objects.extend(Loot.objects.all()) objects.extend(Loot.objects.all())
objects.extend(Faction.objects.all()) objects.extend(Faction.objects.all())
objects.extend(Note.objects.all()) objects.extend(Note.objects.all())
for object in objects: for object in objects:
data[object.name] = object.get_absolute_url() data[object.name] = (object.get_absolute_url(), object)
if object.aliases: if object.aliases:
for alias in object.aliases: for alias in object.aliases:
data[alias] = object.get_absolute_url() data[alias] = (object.get_absolute_url(), object)
# longer replacements first # longer replacements first
data = {k: v for k, v in sorted(data.items(), key=lambda item: -len(item[0]))} data = {k: v for k, v in sorted(data.items(), key=lambda item: -len(item[0]))}