diff --git a/characters/migrations/0017_character_linked_objects_and_more.py b/characters/migrations/0017_character_linked_objects_and_more.py
new file mode 100644
index 0000000..c6d372e
--- /dev/null
+++ b/characters/migrations/0017_character_linked_objects_and_more.py
@@ -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),
+ ),
+ ]
diff --git a/common/management/commands/refresh_html.py b/common/management/commands/refresh_html.py
index c9c9a54..b27a6b1 100644
--- a/common/management/commands/refresh_html.py
+++ b/common/management/commands/refresh_html.py
@@ -36,9 +36,10 @@ class Command(BaseCommand):
objects.extend(list(Note.objects.all()))
objects.extend(list(IngameDay.objects.all()))
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:
print_diff_call(object.description_html, fresh_html, str(object))
if store:
object.description_html = fresh_html
+ object.linked_objects = ",".join(linked_objects)
object.save()
diff --git a/common/models/descriptionmodel.py b/common/models/descriptionmodel.py
index 2dd5abe..56933a6 100644
--- a/common/models/descriptionmodel.py
+++ b/common/models/descriptionmodel.py
@@ -7,11 +7,13 @@ from utils.markdown import md_to_html
class DescriptionModel(models.Model):
description_md = models.TextField(_("Description"), blank=True)
description_html = models.TextField(_("Description (HTML)"), blank=True, editable=False)
+ linked_objects = models.TextField(max_length=1000, blank=True, default="")
class Meta:
abstract = True
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)
diff --git a/common/tests.py b/common/tests.py
index 3313997..aac50a0 100644
--- a/common/tests.py
+++ b/common/tests.py
@@ -28,20 +28,20 @@ class ColorsTests(SimpleTestCase):
class MarkdownTests(SimpleTestCase):
def test_basic_markdown(self):
self.assertHTMLEqual(
- md_to_html("**test** *it*", replacements={}),
+ md_to_html("**test** *it*", replacements={})[0],
"
test it
"
)
def test_nb_md(self):
self.assertHTMLEqual(
- md_to_html("This\nis\nTest", replacements={}),
+ md_to_html("This\nis\nTest", replacements={})[0],
"This
is
Test
"
)
def test_bleach(self):
self.assertEqual(
md_to_html(
- " Hi", replacements={}),
+ " Hi", replacements={})[0],
"<script>console.log()</script>\nHi</button>
"
)
diff --git a/days/migrations/0007_historicalingameday_linked_objects_and_more.py b/days/migrations/0007_historicalingameday_linked_objects_and_more.py
new file mode 100644
index 0000000..1e8cd1e
--- /dev/null
+++ b/days/migrations/0007_historicalingameday_linked_objects_and_more.py
@@ -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),
+ ),
+ ]
diff --git a/factions/migrations/0005_faction_linked_objects_and_more.py b/factions/migrations/0005_faction_linked_objects_and_more.py
new file mode 100644
index 0000000..f370251
--- /dev/null
+++ b/factions/migrations/0005_faction_linked_objects_and_more.py
@@ -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),
+ ),
+ ]
diff --git a/graph/views.py b/graph/views.py
index 174e6ee..fc4797a 100644
--- a/graph/views.py
+++ b/graph/views.py
@@ -37,6 +37,12 @@ class Graph:
"target": target.graphkey
})
+ def add_edge_str(self, source: str, target: str):
+ self.edges.append({
+ "source": source,
+ "target": target
+ })
+
def prune(self) -> None:
connected_nodes = set()
for e in self.edges:
@@ -62,14 +68,22 @@ class GraphView(TemplateView):
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:
g = Graph()
for loc in list(Location.objects.all()) + list(Note.objects.all()):
g.add_node(loc)
if loc.parent:
g.add_edge(loc, loc.parent)
+ get_description_links(loc, g)
for faction in Faction.objects.all():
g.add_node(faction, faction.name)
+ get_description_links(faction, g)
for user in TenantUser.objects \
.filter(tenants=connection.get_tenant()) \
.exclude(pk__in=[1, 2]):
@@ -82,6 +96,7 @@ def get_graph(request: HttpRequest) -> HttpResponse:
g.add_edge(char, char.faction)
if char.player:
g.add_edge(char, char.player)
+ get_description_links(char, g)
for loottype in LootType.objects.all():
g.add_node(loottype)
@@ -94,6 +109,8 @@ def get_graph(request: HttpRequest) -> HttpResponse:
g.add_edge(loot, loot.owner)
if loot.type:
g.add_edge(loot, loot.type)
+ get_description_links(loot, g)
+
g.prune()
return JsonResponse(g.export())
diff --git a/locations/migrations/0011_historicallocation_linked_objects_and_more.py b/locations/migrations/0011_historicallocation_linked_objects_and_more.py
new file mode 100644
index 0000000..dedef7a
--- /dev/null
+++ b/locations/migrations/0011_historicallocation_linked_objects_and_more.py
@@ -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),
+ ),
+ ]
diff --git a/loot/migrations/0016_historicalloot_linked_objects_loot_linked_objects.py b/loot/migrations/0016_historicalloot_linked_objects_loot_linked_objects.py
new file mode 100644
index 0000000..20d88ae
--- /dev/null
+++ b/loot/migrations/0016_historicalloot_linked_objects_loot_linked_objects.py
@@ -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),
+ ),
+ ]
diff --git a/notes/migrations/0006_historicalnote_linked_objects_note_linked_objects.py b/notes/migrations/0006_historicalnote_linked_objects_note_linked_objects.py
new file mode 100644
index 0000000..ff5e718
--- /dev/null
+++ b/notes/migrations/0006_historicalnote_linked_objects_note_linked_objects.py
@@ -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),
+ ),
+ ]
diff --git a/utils/markdown.py b/utils/markdown.py
index 46b2f85..5046960 100644
--- a/utils/markdown.py
+++ b/utils/markdown.py
@@ -1,5 +1,6 @@
import re
from html.parser import HTMLParser
+from typing import Tuple, Set
import bleach
import markdown
@@ -8,8 +9,8 @@ from bleach_allowlist import markdown_tags, markdown_attrs
custom_allowed_tags = ["del", "ins"]
-def md_to_html(md: str, replacements=None) -> str:
- md = autolink(md, replacements=replacements)
+def md_to_html(md: str, replacements=None) -> Tuple[str, Set[str]]:
+ md, linked_objects = autolink(md, replacements=replacements)
html = markdown.markdown(
md,
output_format="html",
@@ -22,25 +23,28 @@ def md_to_html(md: str, replacements=None) -> str:
tags=markdown_tags + custom_allowed_tags,
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:
from utils.urls import name2url
replacements = name2url()
links = {}
+ linked_objects = set()
i = 0
- for name, url in replacements.items():
+ for name, (url, obj) in replacements.items():
regex = r"\bWORD\b".replace("WORD", name)
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})"
i += 1
for placeholder, value in links.items():
md = md.replace(placeholder, value)
- return md
+ return md, linked_objects
class HTMLFilter(HTMLParser):
diff --git a/utils/urls.py b/utils/urls.py
index 914e7e7..d64996f 100644
--- a/utils/urls.py
+++ b/utils/urls.py
@@ -9,17 +9,17 @@ from notes.models import Note
def name2url() -> Dict[str, str]:
data = {}
- objects=[]
+ objects = []
objects.extend(Character.objects.all())
objects.extend(Location.objects.all())
objects.extend(Loot.objects.all())
objects.extend(Faction.objects.all())
objects.extend(Note.objects.all())
for object in objects:
- data[object.name] = object.get_absolute_url()
+ data[object.name] = (object.get_absolute_url(), object)
if object.aliases:
for alias in object.aliases:
- data[alias] = object.get_absolute_url()
+ data[alias] = (object.get_absolute_url(), object)
# longer replacements first
data = {k: v for k, v in sorted(data.items(), key=lambda item: -len(item[0]))}