datatracker/ietf/group/dot.py

136 lines
4.6 KiB
Python

# Copyright The IETF Trust 2017-2019, All Rights Reserved
# -*- coding: utf-8 -*-
# -*- check-flake8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from django.db.models import Q
from django.template.loader import render_to_string
from ietf.doc.models import RelatedDocument
class Edge(object):
def __init__(self, relateddocument):
self.relateddocument = relateddocument
def __hash__(self):
return hash("|".join([str(hash(nodename(self.relateddocument.source.name))),
str(hash(nodename(self.relateddocument.target.document.name))),
self.relateddocument.relationship.slug]))
def __eq__(self, other):
return self.__hash__() == other.__hash__()
def sourcename(self):
return nodename(self.relateddocument.source.name)
def targetname(self):
return nodename(self.relateddocument.target.document.name)
def styles(self):
# Note that the old style=dotted, color=red styling is never used
if self.relateddocument.is_downref():
return { 'color': 'red', 'arrowhead': 'normalnormal' }
else:
styles = { 'refnorm' : { 'color': 'blue' },
'refinfo' : { 'color': 'green' },
'refold' : { 'color': 'orange' },
'refunk' : { 'style': 'dashed' },
'replaces': { 'color': 'pink', 'style': 'dashed', 'arrowhead': 'diamond' },
}
return styles[self.relateddocument.relationship.slug]
def nodename(name):
return name.replace('-', '_')
def get_node_styles(node, group):
styles = dict()
# Shape and style (note that old diamond shape is never used
styles['style'] = 'filled'
if node.get_state('draft').slug == 'rfc':
styles['shape'] = 'box'
elif not node.get_state('draft-iesg').slug in ['idexists', 'watching', 'dead']:
styles['shape'] = 'parallelogram'
elif node.get_state('draft').slug == 'expired':
styles['shape'] = 'house'
styles['style'] = 'solid'
styles['peripheries'] = 3
elif node.get_state('draft').slug == 'repl':
styles['shape'] = 'ellipse'
styles['style'] = 'solid'
styles['peripheries'] = 3
else:
pass # quieter form of styles['shape'] = 'ellipse'
# Color (note that the old 'Flat out red' is never used
if node.group.acronym == 'none':
styles['color'] = '"#FF800D"' # orangeish
elif node.group == group:
styles['color'] = '"#0AFE47"' # greenish
else:
styles['color'] = '"#9999FF"' # blueish
# Label
label = node.name
if label.startswith('draft-'):
if label.startswith('draft-ietf-'):
label = label[11:]
else:
label = label[6:]
try:
t = label.index('-')
label = r"%s\n%s" % (label[:t], label[t+1:])
except:
pass
if node.group.acronym != 'none' and node.group != group:
label = "(%s) %s" % (node.group.acronym, label)
if node.get_state('draft').slug == 'rfc':
label = "%s\\n(%s)" % (label, node.canonical_name())
styles['label'] = '"%s"' % label
return styles
def make_dot(group):
references = Q(source__group=group, source__type='draft', relationship__slug__startswith='ref')
both_rfcs = Q(source__states__slug='rfc', target__docs__states__slug='rfc')
inactive = Q(source__states__slug__in=['expired', 'repl'])
attractor = Q(target__name__in=['rfc5000', 'rfc5741'])
removed = Q(source__states__slug__in=['auth-rm', 'ietf-rm'])
relations = ( RelatedDocument.objects.filter(references).exclude(both_rfcs)
.exclude(inactive).exclude(attractor).exclude(removed) )
edges = set()
for x in relations:
target_state = x.target.document.get_state_slug('draft')
if target_state != 'rfc' or x.is_downref():
edges.add(Edge(x))
replacements = RelatedDocument.objects.filter(relationship__slug='replaces',
target__docs__in=[x.relateddocument.target.document for x in edges])
for x in replacements:
edges.add(Edge(x))
nodes = set([x.relateddocument.source for x in edges]).union([x.relateddocument.target.document for x in edges])
for node in nodes:
node.nodename = nodename(node.name)
node.styles = get_node_styles(node, group)
return render_to_string('group/dot.txt',
dict( nodes=nodes, edges=edges )
)