|
32 | 32 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
33 | 33 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | 34 |
|
| 35 | +import os |
35 | 36 | import itertools |
| 37 | +from tempfile import mkstemp |
36 | 38 |
|
37 | 39 | from django.shortcuts import get_object_or_404, render_to_response |
| 40 | +from django.template.loader import render_to_string |
38 | 41 | from django.template import RequestContext |
39 | 42 | from django.http import HttpResponse |
40 | 43 | from django.conf import settings |
41 | 44 | from django.core.urlresolvers import reverse as urlreverse |
| 45 | +from django.db.models import Q |
42 | 46 |
|
43 | 47 | from ietf.doc.views_search import SearchForm, retrieve_search_results |
44 | 48 | from ietf.group.models import Group, GroupURL, Role |
|
47 | 51 | from ietf.group.utils import get_charter_text |
48 | 52 | from ietf.doc.templatetags.ietf_filters import clean_whitespace |
49 | 53 | from ietf.ietfauth.utils import has_role |
| 54 | +from ietf.utils.pipe import pipe |
50 | 55 |
|
51 | 56 | def roles(group, role_name): |
52 | 57 | return Role.objects.filter(group=group, name=role_name).select_related("email", "person") |
@@ -286,3 +291,163 @@ def history(request, acronym): |
286 | 291 | construct_group_menu_context(request, group, "history", { |
287 | 292 | "events": events, |
288 | 293 | }), RequestContext(request)) |
| 294 | + |
| 295 | + |
| 296 | +def nodename(name): |
| 297 | + return name.replace('-','_') |
| 298 | + |
| 299 | +class Edge(object): |
| 300 | + def __init__(self,relateddocument): |
| 301 | + self.relateddocument=relateddocument |
| 302 | + |
| 303 | + def __hash__(self): |
| 304 | + return hash("|".join([str(hash(nodename(self.relateddocument.source.name))), |
| 305 | + str(hash(nodename(self.relateddocument.target.document.name))), |
| 306 | + self.relateddocument.relationship.slug])) |
| 307 | + |
| 308 | + def __eq__(self,other): |
| 309 | + return self.__hash__() == other.__hash__() |
| 310 | + |
| 311 | + def sourcename(self): |
| 312 | + return nodename(self.relateddocument.source.name) |
| 313 | + |
| 314 | + def targetname(self): |
| 315 | + return nodename(self.relateddocument.target.document.name) |
| 316 | + |
| 317 | + def styles(self): |
| 318 | + |
| 319 | + # Note that the old style=dotted, color=red styling is never used |
| 320 | + |
| 321 | + if self.relateddocument.is_downref(): |
| 322 | + return { 'color':'red','arrowhead':'normalnormal' } |
| 323 | + else: |
| 324 | + styles = { 'refnorm' : { 'color':'blue' }, |
| 325 | + 'refinfo' : { 'color':'green' }, |
| 326 | + 'refold' : { 'color':'orange' }, |
| 327 | + 'refunk' : { 'style':'dashed' }, |
| 328 | + 'replaces': { 'color':'pink', 'style':'dashed', 'arrowhead':'diamond' }, |
| 329 | + } |
| 330 | + return styles[self.relateddocument.relationship.slug] |
| 331 | + |
| 332 | +def get_node_styles(node,group): |
| 333 | + |
| 334 | + styles=dict() |
| 335 | + |
| 336 | + # Shape and style (note that old diamond shape is never used |
| 337 | + |
| 338 | + styles['style'] = 'filled' |
| 339 | + |
| 340 | + if node.get_state('draft').slug == 'rfc': |
| 341 | + styles['shape'] = 'box' |
| 342 | + elif node.get_state('draft-iesg') and not node.get_state('draft-iesg').slug in ['watching','dead']: |
| 343 | + styles['shape'] = 'parallelogram' |
| 344 | + elif node.get_state('draft').slug == 'expired': |
| 345 | + styles['shape'] = 'house' |
| 346 | + styles['style'] ='solid' |
| 347 | + styles['peripheries'] = 3 |
| 348 | + elif node.get_state('draft').slug == 'repl': |
| 349 | + styles['shape'] = 'ellipse' |
| 350 | + styles['style'] ='solid' |
| 351 | + styles['peripheries'] = 3 |
| 352 | + else: |
| 353 | + pass # quieter form of styles['shape'] = 'ellipse' |
| 354 | + |
| 355 | + # Color (note that the old 'Flat out red' is never used |
| 356 | + if node.group.acronym == 'none': |
| 357 | + styles['color'] = '"#FF800D"' # orangeish |
| 358 | + elif node.group == group: |
| 359 | + styles['color'] = '"#0AFE47"' # greenish |
| 360 | + else: |
| 361 | + styles['color'] = '"#9999FF"' # blueish |
| 362 | + |
| 363 | + # Label |
| 364 | + label = node.name |
| 365 | + if label.startswith('draft-'): |
| 366 | + if label.startswith('draft-ietf-'): |
| 367 | + label=label[11:] |
| 368 | + else: |
| 369 | + label=label[6:] |
| 370 | + try: |
| 371 | + t=label.index('-') |
| 372 | + label="%s\\n%s" % (label[:t],label[t+1:]) |
| 373 | + except: |
| 374 | + pass |
| 375 | + if node.group.acronym != 'none' and node.group != group: |
| 376 | + label = "(%s) %s"%(node.group.acronym,label) |
| 377 | + if node.get_state('draft').slug == 'rfc': |
| 378 | + label = "%s\\n(%s)"%(label,node.canonical_name()) |
| 379 | + styles['label'] = '"%s"'%label |
| 380 | + |
| 381 | + return styles |
| 382 | + |
| 383 | +def make_dot(group): |
| 384 | + |
| 385 | + references = Q(source__group=group,source__type='draft',relationship__slug__startswith='ref') |
| 386 | + both_rfcs = Q(source__states__slug='rfc',target__document__states__slug='rfc') |
| 387 | + inactive = Q(source__states__slug__in=['expired','repl']) |
| 388 | + attractor = Q(target__name__in=['rfc5000','rfc5741']) |
| 389 | + removed = Q(source__states__slug__in=['auth-rm','ietf-rm']) |
| 390 | + relations = RelatedDocument.objects.filter(references).exclude(both_rfcs).exclude(inactive).exclude(attractor).exclude(removed) |
| 391 | + |
| 392 | + edges = set() |
| 393 | + for x in relations: |
| 394 | + target_state = x.target.document.get_state_slug('draft') |
| 395 | + if target_state!='rfc' or x.is_downref(): |
| 396 | + edges.add(Edge(x)) |
| 397 | + |
| 398 | + replacements = RelatedDocument.objects.filter(relationship__slug='replaces',target__document__in=[x.relateddocument.target.document for x in edges]) |
| 399 | + |
| 400 | + for x in replacements: |
| 401 | + edges.add(Edge(x)) |
| 402 | + |
| 403 | + nodes = set([x.relateddocument.source for x in edges]).union([x.relateddocument.target.document for x in edges]) |
| 404 | + |
| 405 | + for node in nodes: |
| 406 | + node.nodename=nodename(node.name) |
| 407 | + node.styles = get_node_styles(node,group) |
| 408 | + |
| 409 | + return render_to_string('wginfo/dot.txt', |
| 410 | + dict( nodes=nodes, edges=edges ) |
| 411 | + ) |
| 412 | + |
| 413 | +def dependencies_dot(request, acronym): |
| 414 | + |
| 415 | + group = get_object_or_404(Group, acronym=acronym) |
| 416 | + |
| 417 | + return HttpResponse(make_dot(group), |
| 418 | + content_type='text/plain; charset=UTF-8' |
| 419 | + ) |
| 420 | + |
| 421 | +def dependencies_pdf(request, acronym): |
| 422 | + |
| 423 | + group = get_object_or_404(Group, acronym=acronym) |
| 424 | + |
| 425 | + dothandle,dotname = mkstemp() |
| 426 | + os.close(dothandle) |
| 427 | + dotfile = open(dotname,"w") |
| 428 | + dotfile.write(make_dot(group)) |
| 429 | + dotfile.close() |
| 430 | + |
| 431 | + unflathandle,unflatname = mkstemp() |
| 432 | + os.close(unflathandle) |
| 433 | + |
| 434 | + pshandle,psname = mkstemp() |
| 435 | + os.close(pshandle) |
| 436 | + |
| 437 | + pdfhandle,pdfname = mkstemp() |
| 438 | + os.close(pdfhandle) |
| 439 | + |
| 440 | + pipe("%s -f -l 10 -o %s %s" % (settings.UNFLATTEN_BINARY,unflatname,dotname)) |
| 441 | + pipe("%s -Tps -Gsize=10.5,8.0 -Gmargin=0.25 -Gratio=auto -Grotate=90 -o %s %s" % (settings.DOT_BINARY,psname,unflatname)) |
| 442 | + pipe("%s %s %s" % (settings.PS2PDF_BINARY,psname,pdfname)) |
| 443 | + |
| 444 | + pdfhandle = open(pdfname,"r") |
| 445 | + pdf = pdfhandle.read() |
| 446 | + pdfhandle.close() |
| 447 | + |
| 448 | + os.unlink(pdfname) |
| 449 | + os.unlink(psname) |
| 450 | + os.unlink(unflatname) |
| 451 | + os.unlink(dotname) |
| 452 | + |
| 453 | + return HttpResponse(pdf, content_type='application/pdf') |
0 commit comments