diff --git a/djaif/book/admin.py b/djaif/book/admin.py
index 0a8c93b..a154276 100644
--- a/djaif/book/admin.py
+++ b/djaif/book/admin.py
@@ -2,10 +2,16 @@
from djaif.book import models
+
class Admin(admin.ModelAdmin):
filter_horizontal = ('items',)
-admin.site.register(models.Book)
+
+class BookAdmin(admin.ModelAdmin):
+ change_form_template = 'admin/book_change_form.html'
+
+
+admin.site.register(models.Book, BookAdmin)
admin.site.register(models.BookPage, Admin)
admin.site.register(models.PageLink, Admin)
admin.site.register(models.BookProgress)
diff --git a/djaif/book/templates/admin/book_change_form.html b/djaif/book/templates/admin/book_change_form.html
new file mode 100644
index 0000000..ba95be9
--- /dev/null
+++ b/djaif/book/templates/admin/book_change_form.html
@@ -0,0 +1,7 @@
+{% extends "admin/change_form.html" %}
+{% load i18n admin_urls %}
+{% block after_related_objects %}
+
+
+
+{% endblock %}
diff --git a/djaif/book/urls.py b/djaif/book/urls.py
index 9115621..7065351 100644
--- a/djaif/book/urls.py
+++ b/djaif/book/urls.py
@@ -11,4 +11,5 @@
views.take_item,
name='take',
),
+ path('book//map.svg', views.view_book_map),
]
diff --git a/djaif/book/views.py b/djaif/book/views.py
index 4697727..ace419e 100644
--- a/djaif/book/views.py
+++ b/djaif/book/views.py
@@ -1,7 +1,10 @@
from functools import wraps
+from graphviz import Digraph
+
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
+from django.http import FileResponse
from djaif.book import models
@@ -83,3 +86,38 @@ def take_item(request, progress, book_id, page_id, item_id):
return redirect(
reverse('page', kwargs={'book_id': book_id, 'page_id': page_id}),
)
+
+
+def view_book_map(request, book_id):
+ book = get_object_or_404(models.Book, id=book_id)
+
+ g = Digraph('Map', filename='map.gv', directory='/tmp')
+
+ def pid(page):
+ return 'page_{id}'.format(id=page.id)
+
+ for page in book.bookpage_set.all():
+ g.node(
+ pid(page),
+ label='\n'.join(
+ [str(page.id), page.title] + [
+ i.name for i in page.items.all()
+ ]
+ ),
+ tooltip=page.body,
+ href='/admin/book/bookpage/{}/change'.format(page.id),
+ )
+
+ for link in models.PageLink.objects.filter(
+ from_page__book_id=book_id,
+ ).all():
+ g.edge(pid(link.from_page), pid(link.to_page), label='\n'.join(
+ [str(link.id), link.name[:10]] + [
+ i.name for i in link.items.all()
+ ]),
+ labeltooltip=link.name,
+ labelhref='/admin/book/pagelink/{}/change'.format(link.id),
+ )
+
+ g.render(quiet=True, view=False, format='svg')
+ return FileResponse(open('/tmp/map.gv.svg', 'rb'))
diff --git a/poetry.lock b/poetry.lock
index 7d68ced..277db00 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -340,6 +340,19 @@ version = "3.1.0"
[package.dependencies]
gitdb = ">=4.0.1,<5"
+[[package]]
+category = "main"
+description = "Simple Python interface for Graphviz"
+name = "graphviz"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
+version = "0.14"
+
+[package.extras]
+dev = ["tox (>=3)", "flake8", "pep8-naming", "wheel", "twine"]
+docs = ["sphinx (>=1.7)", "sphinx-rtd-theme"]
+test = ["mock (>=3)", "pytest (>=3.4,<3.10.0 || >3.10.0)", "pytest-mock (>=1.8)", "pytest-cov"]
+
[[package]]
category = "dev"
description = "Read metadata from Python packages"
@@ -398,30 +411,6 @@ optional = false
python-versions = "*"
version = "0.6.1"
-[[package]]
-category = "dev"
-description = "Optional static typing for Python"
-name = "mypy"
-optional = false
-python-versions = ">=3.5"
-version = "0.770"
-
-[package.dependencies]
-mypy-extensions = ">=0.4.3,<0.5.0"
-typed-ast = ">=1.4.0,<1.5.0"
-typing-extensions = ">=3.7.4"
-
-[package.extras]
-dmypy = ["psutil (>=4.0)"]
-
-[[package]]
-category = "dev"
-description = "Experimental type system extensions for programs checked with the mypy typechecker."
-name = "mypy-extensions"
-optional = false
-python-versions = "*"
-version = "0.4.3"
-
[[package]]
category = "dev"
description = "A Python Parser"
@@ -666,7 +655,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
testing = ["jaraco.itertools", "func-timeout"]
[metadata]
-content-hash = "47b7e6a224018c57be561205db7b19d56cf4087428c1a241af76f5fc802ae8c9"
+content-hash = "1a99a4fd337e731a0173033098fe70e48e7390cc90d1a9e949992014e82a1c89"
python-versions = "^3.6"
[metadata.files]
@@ -781,6 +770,10 @@ gitpython = [
{file = "GitPython-3.1.0-py3-none-any.whl", hash = "sha256:43da89427bdf18bf07f1164c6d415750693b4d50e28fc9b68de706245147b9dd"},
{file = "GitPython-3.1.0.tar.gz", hash = "sha256:e426c3b587bd58c482f0b7fe6145ff4ac7ae6c82673fc656f489719abca6f4cb"},
]
+graphviz = [
+ {file = "graphviz-0.14-py2.py3-none-any.whl", hash = "sha256:cb0e878f90378489f17aab140b68e64e44b79e4cb59a530c8863d84bf2e2e5f5"},
+ {file = "graphviz-0.14.zip", hash = "sha256:e104ba036c8aef84320ec80560e544cd3cad68c9f90394b4e2b87bc44ab09791"},
+]
importlib-metadata = [
{file = "importlib_metadata-1.6.0-py2.py3-none-any.whl", hash = "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f"},
{file = "importlib_metadata-1.6.0.tar.gz", hash = "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"},
@@ -797,26 +790,6 @@ mccabe = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
]
-mypy = [
- {file = "mypy-0.770-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:a34b577cdf6313bf24755f7a0e3f3c326d5c1f4fe7422d1d06498eb25ad0c600"},
- {file = "mypy-0.770-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:86c857510a9b7c3104cf4cde1568f4921762c8f9842e987bc03ed4f160925754"},
- {file = "mypy-0.770-cp35-cp35m-win_amd64.whl", hash = "sha256:a8ffcd53cb5dfc131850851cc09f1c44689c2812d0beb954d8138d4f5fc17f65"},
- {file = "mypy-0.770-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:7687f6455ec3ed7649d1ae574136835a4272b65b3ddcf01ab8704ac65616c5ce"},
- {file = "mypy-0.770-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3beff56b453b6ef94ecb2996bea101a08f1f8a9771d3cbf4988a61e4d9973761"},
- {file = "mypy-0.770-cp36-cp36m-win_amd64.whl", hash = "sha256:15b948e1302682e3682f11f50208b726a246ab4e6c1b39f9264a8796bb416aa2"},
- {file = "mypy-0.770-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:b90928f2d9eb2f33162405f32dde9f6dcead63a0971ca8a1b50eb4ca3e35ceb8"},
- {file = "mypy-0.770-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c56ffe22faa2e51054c5f7a3bc70a370939c2ed4de308c690e7949230c995913"},
- {file = "mypy-0.770-cp37-cp37m-win_amd64.whl", hash = "sha256:8dfb69fbf9f3aeed18afffb15e319ca7f8da9642336348ddd6cab2713ddcf8f9"},
- {file = "mypy-0.770-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:219a3116ecd015f8dca7b5d2c366c973509dfb9a8fc97ef044a36e3da66144a1"},
- {file = "mypy-0.770-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7ec45a70d40ede1ec7ad7f95b3c94c9cf4c186a32f6bacb1795b60abd2f9ef27"},
- {file = "mypy-0.770-cp38-cp38-win_amd64.whl", hash = "sha256:f91c7ae919bbc3f96cd5e5b2e786b2b108343d1d7972ea130f7de27fdd547cf3"},
- {file = "mypy-0.770-py3-none-any.whl", hash = "sha256:3b1fc683fb204c6b4403a1ef23f0b1fac8e4477091585e0c8c54cbdf7d7bb164"},
- {file = "mypy-0.770.tar.gz", hash = "sha256:8a627507ef9b307b46a1fea9513d5c98680ba09591253082b4c48697ba05a4ae"},
-]
-mypy-extensions = [
- {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
- {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
-]
parso = [
{file = "parso-0.7.0-py2.py3-none-any.whl", hash = "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0"},
{file = "parso-0.7.0.tar.gz", hash = "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c"},
diff --git a/pyproject.toml b/pyproject.toml
index e5a2a31..258d553 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,6 +8,7 @@ authors = ["Your Name "]
python = "^3.6"
django = "^3.0.5"
Pillow = "^7.1.1"
+graphviz = "^0.14"
[tool.poetry.dev-dependencies]
wemake-python-styleguide = "^0.14.0"
diff --git a/setup.cfg b/setup.cfg
index e8c66b4..19d9f4b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -25,7 +25,13 @@ exclude =
*.egg
**/migrations/**
-ignore = D100, D101, D103, D105, D106, WPS326, WPS306, WPS317
+ignore =
+ # documentation isn't so important for now
+ D100, D101, D102, D103, D105, D106,
+ # yes, we have bad names here and there
+ WPS110, WPS111,
+ # annoying stuff
+ WPS326, WPS306, WPS317,
per-file-ignores =
settings.py: WPS407, E501, C812, WPS221, WPS226