summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2019-07-04 16:11:34 +0200
committerÉtienne Loks <etienne.loks@iggdrasil.net>2019-07-04 22:34:41 +0200
commita9b0c688f57f676f90dd27fb65520d2e1ad92f64 (patch)
tree423c5e22b1aea6c68bbbdc6e1aaeb694fc47d039
parenteda4554eeca077f2357235414f6d59708d1b9944 (diff)
downloadIshtar-a9b0c688f57f676f90dd27fb65520d2e1ad92f64.tar.bz2
Ishtar-a9b0c688f57f676f90dd27fb65520d2e1ad92f64.zip
Graphs: save results, options and tests
- can generate only above and only bellow - save dot files - option for no color for current - tests
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--archaeological_context_records/migrations/0048_auto_20190704_1526.py96
-rw-r--r--archaeological_context_records/tests.py97
-rw-r--r--archaeological_operations/migrations/0057_auto_20190704_1526.py96
-rw-r--r--example_project/settings.py4
-rwxr-xr-xinstall/ishtar-install4
-rw-r--r--ishtar_common/models.py32
-rw-r--r--ishtar_common/utils.py166
8 files changed, 431 insertions, 66 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 65f39ebb3..ec1e9a845 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,6 @@
before_script:
- apt-get update
- - apt-get install -q -y git sed python3-pip libpq-dev python3-dev libjpeg-dev zlib1g-dev libxml2-dev libxslt1-dev libgeos-dev python3-cairocffi tidy libtidy-dev binutils libproj-dev gdal-bin libpangocairo-1.0-0 pandoc
+ - apt-get install -q -y git sed python3-pip libpq-dev python3-dev libjpeg-dev zlib1g-dev libxml2-dev libxslt1-dev libgeos-dev python3-cairocffi tidy libtidy-dev binutils libproj-dev gdal-bin libpangocairo-1.0-0 pandoc graphviz
- apt-get install -q -y locales
- echo "fr_FR.UTF-8 UTF-8" >> /etc/locale.gen
- dpkg-reconfigure --frontend=noninteractive locales && update-locale LANG=$LANG
diff --git a/archaeological_context_records/migrations/0048_auto_20190704_1526.py b/archaeological_context_records/migrations/0048_auto_20190704_1526.py
new file mode 100644
index 000000000..9e65a4b31
--- /dev/null
+++ b/archaeological_context_records/migrations/0048_auto_20190704_1526.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.18 on 2019-07-04 15:26
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_context_records', '0047_auto_20190628_1257'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_bitmap_image',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_bitmap_image_above',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated above relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_bitmap_image_bellow',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated bellow relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_dot',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_dot_above',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated above relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_dot_bellow',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated bellow relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_image_above',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated above relation image (SVG)'),
+ ),
+ migrations.AddField(
+ model_name='contextrecord',
+ name='relation_image_bellow',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated bellow relation image (SVG)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_bitmap_image',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_bitmap_image_above',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated above relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_bitmap_image_bellow',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated bellow relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_dot',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_dot_above',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated above relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_dot_bellow',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated bellow relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_image_above',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated above relation image (SVG)'),
+ ),
+ migrations.AddField(
+ model_name='historicalcontextrecord',
+ name='relation_image_bellow',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated bellow relation image (SVG)'),
+ ),
+ ]
diff --git a/archaeological_context_records/tests.py b/archaeological_context_records/tests.py
index 79d60bf09..a0a76ba78 100644
--- a/archaeological_context_records/tests.py
+++ b/archaeological_context_records/tests.py
@@ -37,6 +37,8 @@ from archaeological_operations.tests import OperationInitTest, \
from archaeological_operations import models as models_ope
from archaeological_context_records import models
+from ishtar_common.utils import generate_relation_graph
+
from ishtar_common.tests import WizardTest, WizardTestFormData as FormData, \
create_superuser, create_user, TestCase, AutocompleteTestBase, AcItem
@@ -865,3 +867,98 @@ class AutocompleteTest(AutocompleteTestBase, TestCase):
label=base_name
)
return item, None
+
+
+class GraphGenerationTest(ContextRecordInit, TestCase):
+ fixtures = CONTEXT_RECORD_TOWNS_FIXTURES
+
+ def setUp(self):
+ self.sym_rel_type, __ = models.RelationType.objects.get_or_create(
+ symmetrical=True, txt_idx='sym', logical_relation='equal')
+ self.rel_type_bellow, __ = models.RelationType.objects.get_or_create(
+ symmetrical=False, txt_idx='bellow', logical_relation='bellow')
+ self.rel_type_above, __ = models.RelationType.objects.get_or_create(
+ symmetrical=False, txt_idx='above', logical_relation='above')
+ self.create_context_record({"label": u"CR 1"})
+ self.create_context_record({"label": u"CR 2"})
+ self.create_context_record({"label": u"CR 3"})
+ self.create_context_record({"label": u"CR 4"})
+ self.create_context_record({"label": u"CR 1B"})
+ self.create_context_record({"label": u"CR 2B"})
+ self.create_context_record({"label": u"CR 3B"})
+
+ cr_1 = self.context_records[0]
+ self.cr_2 = cr_2 = self.context_records[1]
+ cr_3 = self.context_records[2]
+ cr_4 = self.context_records[3]
+ cr_1B = self.context_records[4]
+ cr_2B = self.context_records[5]
+ cr_3B = self.context_records[6]
+
+ models.RecordRelations.objects.create(
+ left_record=cr_2, right_record=cr_2B,
+ relation_type=self.sym_rel_type)
+
+ models.RecordRelations.objects.create(
+ left_record=cr_1, right_record=cr_2,
+ relation_type=self.rel_type_bellow)
+ models.RecordRelations.objects.create(
+ left_record=cr_3, right_record=cr_2,
+ relation_type=self.rel_type_above)
+ models.RecordRelations.objects.create(
+ left_record=cr_3, right_record=cr_4,
+ relation_type=self.rel_type_bellow)
+
+ models.RecordRelations.objects.create(
+ left_record=cr_1B, right_record=cr_2B,
+ relation_type=self.rel_type_bellow)
+ models.RecordRelations.objects.create(
+ left_record=cr_3B, right_record=cr_2B,
+ relation_type=self.rel_type_above)
+
+ def test_gen_relation_full(self):
+ generate_relation_graph(self.cr_2)
+ cr_2 = models.ContextRecord.objects.get(pk=self.cr_2.pk)
+ self.assertIsNotNone(cr_2.relation_image)
+ self.assertIsNotNone(cr_2.relation_bitmap_image)
+ self.assertIsNotNone(cr_2.relation_dot)
+ content = open(cr_2.relation_dot.path).read()
+ self.assertIn('"CR 1"', content)
+ self.assertIn('"CR 1B"', content)
+ self.assertIn('"CR 2B"', content)
+ self.assertIn('"CR 2",style=filled,fillcolor="#C6C0C0"', content)
+ self.assertIn('"CR 3"', content)
+ self.assertIn('"CR 3B"', content)
+ self.assertIn('"CR 4"', content)
+
+ def test_gen_relation_above(self):
+ generate_relation_graph(self.cr_2, render_above=False)
+ cr_2 = models.ContextRecord.objects.get(pk=self.cr_2.pk)
+ self.assertIsNotNone(cr_2.relation_image_above)
+ self.assertIsNotNone(cr_2.relation_bitmap_image_above)
+ self.assertIsNotNone(cr_2.relation_dot_above)
+ content = open(cr_2.relation_dot_above.path).read()
+ self.assertIn('"CR 1"', content)
+ self.assertIn('"CR 1B"', content)
+ self.assertIn('"CR 2B"', content)
+ self.assertIn('"CR 2",style=filled,fillcolor="#C6C0C0"', content)
+ self.assertNotIn('"CR 3B"', content)
+ self.assertNotIn('"CR 3"', content)
+ self.assertNotIn('"CR 4"', content)
+
+ def test_gen_relation_bellow(self):
+ generate_relation_graph(self.cr_2, render_bellow=False)
+ cr_2 = models.ContextRecord.objects.get(pk=self.cr_2.pk)
+ self.assertIsNotNone(cr_2.relation_image_bellow)
+ self.assertIsNotNone(cr_2.relation_bitmap_image_bellow)
+ self.assertIsNotNone(cr_2.relation_dot_bellow)
+ content = open(cr_2.relation_dot_bellow.path).read()
+ self.assertNotIn('"CR 1"', content)
+ self.assertNotIn('"CR 1B"', content)
+ self.assertIn('"CR 2B"', content)
+ self.assertIn('"CR 2",style=filled,fillcolor="#C6C0C0"', content)
+ self.assertIn('"CR 3"', content)
+ self.assertIn('"CR 3B"', content)
+ self.assertIn('"CR 4"', content)
+
+
diff --git a/archaeological_operations/migrations/0057_auto_20190704_1526.py b/archaeological_operations/migrations/0057_auto_20190704_1526.py
new file mode 100644
index 000000000..291821d36
--- /dev/null
+++ b/archaeological_operations/migrations/0057_auto_20190704_1526.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.18 on 2019-07-04 15:26
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import ishtar_common.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('archaeological_operations', '0056_auto_20190628_1257'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_bitmap_image',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_bitmap_image_above',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated above relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_bitmap_image_bellow',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated bellow relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_dot',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_dot_above',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated above relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_dot_bellow',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated bellow relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_image_above',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated above relation image (SVG)'),
+ ),
+ migrations.AddField(
+ model_name='historicaloperation',
+ name='relation_image_bellow',
+ field=models.TextField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', max_length=100, null=True, verbose_name='Generated bellow relation image (SVG)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_bitmap_image',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_bitmap_image_above',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated above relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_bitmap_image_bellow',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated bellow relation image (PNG)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_dot',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_dot_above',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated above relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_dot_bellow',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated bellow relation image (DOT)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_image_above',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated above relation image (SVG)'),
+ ),
+ migrations.AddField(
+ model_name='operation',
+ name='relation_image_bellow',
+ field=models.FileField(blank=True, help_text='La taille maximale supportée pour le fichier est de 100 Mo.', null=True, upload_to=ishtar_common.models.get_image_path, verbose_name='Generated bellow relation image (SVG)'),
+ ),
+ ]
diff --git a/example_project/settings.py b/example_project/settings.py
index 267370945..15ed14559 100644
--- a/example_project/settings.py
+++ b/example_project/settings.py
@@ -273,8 +273,8 @@ MAX_ATTEMPTS = 1 # django background tasks
MAX_UPLOAD_SIZE = 100 # in Mo
-# if you want to generate relation graph provide the path to the "dot" program
-DOT_BINARY = ""
+# path to the "dot" program to generate graph
+DOT_BINARY = "/usr/bin/dot"
TEST_RUNNER = 'ishtar_common.tests.ManagedModelTestRunner'
diff --git a/install/ishtar-install b/install/ishtar-install
index fd1934db5..f3dbfe189 100755
--- a/install/ishtar-install
+++ b/install/ishtar-install
@@ -455,7 +455,7 @@ EOF
python3-django-registration libpangocairo-1.0-0 python3-requests \
python3-bs4 python3-cffi python3-django-compressor pandoc libjs-jquery\
python3-tidylib python3-lxml python3-pil python3-html5lib \
- python3-psycopg2 python3-gdal gettext memcached \
+ python3-psycopg2 python3-gdal gettext memcached graphviz \
python3-memcache python3-dbf python3-markdown \
python3-reportlab python3-django-extensions python3-unidecode' )
fi
@@ -467,7 +467,7 @@ EOF
django-extensions==1.7.4' )
( set -x; $sh_c 'sleep 3; apt-get install -y -q \
libpangocairo-1.0-0 python3-requests \
- python3-bs4 python3-cffi pandoc libjs-jquery \
+ python3-bs4 python3-cffi pandoc libjs-jquery graphviz \
python3-tidylib python3-lxml python3-imaging python3-html5lib \
python3-psycopg2 python3-gdal gettext memcached \
python3-memcache python3-dbf python3-markdown \
diff --git a/ishtar_common/models.py b/ishtar_common/models.py
index 5e7f6e9b3..e8b6fc518 100644
--- a/ishtar_common/models.py
+++ b/ishtar_common/models.py
@@ -1205,6 +1205,38 @@ class RelationItem(models.Model):
_("Generated relation image (SVG)"), null=True, blank=True,
upload_to=get_image_path, help_text=max_size_help()
)
+ relation_bitmap_image = models.FileField(
+ _("Generated relation image (PNG)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
+ relation_dot = models.FileField(
+ _("Generated relation image (DOT)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
+ relation_image_above = models.FileField(
+ _("Generated above relation image (SVG)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
+ relation_dot_above = models.FileField(
+ _("Generated above relation image (DOT)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
+ relation_bitmap_image_above = models.FileField(
+ _("Generated above relation image (PNG)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
+ relation_image_bellow = models.FileField(
+ _("Generated bellow relation image (SVG)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
+ relation_dot_bellow = models.FileField(
+ _("Generated bellow relation image (DOT)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
+ relation_bitmap_image_bellow = models.FileField(
+ _("Generated bellow relation image (PNG)"), null=True, blank=True,
+ upload_to=get_image_path, help_text=max_size_help()
+ )
class Meta:
abstract = True
diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py
index 83bc56279..4f8dce853 100644
--- a/ishtar_common/utils.py
+++ b/ishtar_common/utils.py
@@ -17,6 +17,7 @@
# See the file COPYING for details.
+from cairosvg import svg2png
from csv import QUOTE_ALL
import datetime
from functools import wraps
@@ -1089,7 +1090,8 @@ def create_default_areas(models=None, verbose=False):
def get_relations_for_graph(rel_model, obj_pk, above_relations=None,
- equal_relations=None, treated=None, styles=None):
+ equal_relations=None, treated=None, styles=None,
+ render_above=True, render_bellow=True):
"""
Get all above and equal relations of an object (get all child and parent
relations)
@@ -1099,6 +1101,8 @@ def get_relations_for_graph(rel_model, obj_pk, above_relations=None,
:param equal_relations: list of current equal_relations
:param treated: treated relation list to prevent circular call
:param styles: current styles
+ :param render_above: render relation above the current object
+ :param render_bellow: render relation bellow the current object
:return: above and equal relations list (each containing lists of two
members)
"""
@@ -1114,63 +1118,81 @@ def get_relations_for_graph(rel_model, obj_pk, above_relations=None,
return above_relations, equal_relations, styles
treated.append(obj_pk)
- q = rel_model.objects.filter(
- left_record_id=obj_pk,
- relation_type__logical_relation__isnull=False
- ).values('right_record_id', 'relation_type__logical_relation')
- if not q.count():
- return [], []
- for relation in q.all():
- logical_relation = relation['relation_type__logical_relation']
- right_record = relation['right_record_id']
- if not logical_relation:
- continue
- elif logical_relation == 'above'and \
- (obj_pk, right_record) not in above_relations:
- above_relations.append((obj_pk, right_record))
- elif logical_relation == 'bellow' and \
- (right_record, obj_pk) not in above_relations:
- above_relations.append((right_record, obj_pk))
- elif logical_relation == 'equal' and \
- (right_record, obj_pk) not in equal_relations and \
- (obj_pk, right_record) not in equal_relations:
- equal_relations.append((obj_pk, right_record))
- else:
- continue
- ar, er, substyles = get_relations_for_graph(
- rel_model, right_record, above_relations, equal_relations, treated,
- styles
- )
- styles.update(substyles)
- error_style = "color=red"
- for r in ar:
- if r not in above_relations:
- above_relations.append(r)
- reverse_rel = tuple(reversed(r))
- if reverse_rel in above_relations:
- # circular
- if r not in styles:
- styles[r] = []
- if reverse_rel not in styles:
- styles[reverse_rel] = []
-
- if error_style not in styles[r]:
- styles[r].append(error_style)
- if error_style not in styles[reverse_rel]:
- styles[reverse_rel].append(error_style)
- if r[0] == r[1]:
- # same entity
- if r not in styles:
- styles[r] = []
- if error_style not in styles[r]:
- styles[r].append("color=red")
- for r in er:
- if r not in equal_relations:
- equal_relations.append(r)
+
+ for q, inverse in (
+ (rel_model.objects.filter(
+ left_record_id=obj_pk,
+ relation_type__logical_relation__isnull=False), False),
+ (rel_model.objects.filter(
+ right_record_id=obj_pk,
+ relation_type__logical_relation__isnull=False), True)):
+ q = q.values("left_record_id",'right_record_id',
+ 'relation_type__logical_relation')
+ get_above, get_bellow = render_above, render_bellow
+ if inverse and (not render_above or not render_bellow):
+ get_above, get_bellow = not render_above, not render_bellow
+
+ for relation in q.all():
+ logical_relation = relation['relation_type__logical_relation']
+ left_record = relation['left_record_id']
+ right_record = relation['right_record_id']
+ if not logical_relation:
+ continue
+ elif get_bellow and logical_relation == 'above' and \
+ (left_record, right_record) not in above_relations:
+ above_relations.append((left_record, right_record))
+ elif get_above and logical_relation == 'bellow' and \
+ (right_record, left_record) not in above_relations:
+ above_relations.append((right_record, left_record))
+ elif logical_relation == 'equal' and \
+ (right_record, left_record) not in equal_relations and \
+ (left_record, right_record) not in equal_relations:
+ equal_relations.append((left_record, right_record))
+ else:
+ continue
+
+ if right_record == obj_pk:
+ other_record = left_record
+ else:
+ other_record = right_record
+
+ ar, er, substyles = get_relations_for_graph(
+ rel_model, other_record, above_relations, equal_relations,
+ treated, styles, render_above=render_above,
+ render_bellow=render_bellow
+ )
+ styles.update(substyles)
+ error_style = "color=red"
+ for r in ar:
+ if r not in above_relations:
+ above_relations.append(r)
+ reverse_rel = tuple(reversed(r))
+ if reverse_rel in above_relations:
+ # circular
+ if r not in styles:
+ styles[r] = []
+ if reverse_rel not in styles:
+ styles[reverse_rel] = []
+
+ if error_style not in styles[r]:
+ styles[r].append(error_style)
+ if error_style not in styles[reverse_rel]:
+ styles[reverse_rel].append(error_style)
+ if r[0] == r[1]:
+ # same entity
+ if r not in styles:
+ styles[r] = []
+ if error_style not in styles[r]:
+ styles[r].append("color=red")
+ for r in er:
+ if r not in equal_relations:
+ equal_relations.append(r)
return above_relations, equal_relations, styles
-def generate_relation_graph(obj, debug=False):
+def generate_relation_graph(obj, highlight_current=True,
+ render_above=True, render_bellow=True,
+ debug=False):
if not settings.DOT_BINARY:
return
@@ -1179,8 +1201,8 @@ def generate_relation_graph(obj, debug=False):
# get relations
above_relations, equal_relations, styles = get_relations_for_graph(
- rel_model, obj.pk)
- print(styles)
+ rel_model, obj.pk, render_above=render_above,
+ render_bellow=render_bellow)
if not above_relations and not equal_relations:
obj.relation_image = None
obj.save()
@@ -1201,14 +1223,14 @@ def generate_relation_graph(obj, debug=False):
described.append(left_pk)
left = model.objects.get(pk=left_pk)
style = 'label="{}"'.format(left.relation_label)
- if left.pk == obj.pk:
+ if left.pk == obj.pk and highlight_current:
style += ',style=filled,fillcolor="#C6C0C0"'
dot_str += u'item{}[{}];\n'.format(left.pk, style)
if right_pk not in described:
described.append(right_pk)
right = model.objects.get(pk=right_pk)
style = 'label="{}"'.format(right.relation_label)
- if right.pk == obj.pk:
+ if right.pk == obj.pk and highlight_current:
style += ',style=filled,fillcolor="#C6C0C0"'
dot_str += u'item{}[{}];\n'.format(right.pk, style)
if not directed: # on the same level
@@ -1226,6 +1248,18 @@ def generate_relation_graph(obj, debug=False):
with open(dot_name, 'w') as dot_file:
dot_file.write(dot_str)
+ if not render_above:
+ suffix = "_above"
+ elif not render_bellow:
+ suffix = "_bellow"
+ else:
+ suffix = ""
+
+ with open(dot_name, "r") as dot_file:
+ django_file = File(dot_file)
+ attr = "relation_dot" + suffix
+ getattr(obj, attr).save("relations.dot", django_file, save=True)
+
# execute dot program
args = (settings.DOT_BINARY, "-Tsvg", dot_name)
@@ -1234,10 +1268,20 @@ def generate_relation_graph(obj, debug=False):
with open(svg_tmp_name, "w") as svg_file:
popen = subprocess.Popen(args, stdout=svg_file)
popen.wait()
-
with open(svg_tmp_name, "r") as svg_file:
django_file = File(svg_file)
- obj.relation_image.save("relations.svg", django_file, save=True)
+ attr = "relation_image" + suffix
+ getattr(obj, attr).save("relations.svg", django_file, save=True)
+
+ png_name = tempdir + os.path.sep + "relations.png"
+
+ with open(png_name, "wb") as png_file:
+ svg2png(open(svg_tmp_name, 'rb').read(),
+ write_to=png_file)
+ with open(png_name, "rb") as png_file:
+ django_file = File(png_file)
+ attr = "relation_bitmap_image" + suffix
+ getattr(obj, attr).save("relations.png", django_file, save=True)
if debug:
print(u"DOT file: {}. Tmp SVG file: {}.".format(dot_name, svg_tmp_name))