CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
List of all members | Public Member Functions | Public Attributes | Static Public Attributes
DOTExport.DotExport Class Reference
Inheritance diagram for DOTExport.DotExport:

Public Member Functions

def __init__
 
def dotIndenter
 
def export
 
def get_png_size
 
def processMap
 
def selectNode
 
def write_output
 

Public Attributes

 include_nodes
 
 shapes
 

Static Public Attributes

tuple file_types = ('bmp','dot','eps','gif','jpg','pdf','png','ps','svg','tif','png+map','stdout')
 
dictionary option_types
 
string plugin_name = 'DOT Export'
 

Detailed Description

Export a CMSSW config file to DOT (http://www.graphviz.org) markup, either as raw markup or by invoking the dot program, as an image.

Definition at line 261 of file DOTExport.py.

Constructor & Destructor Documentation

def DOTExport.DotExport.__init__ (   self)

Definition at line 295 of file DOTExport.py.

Referenced by DOTExport.DotExport.selectNode().

296  def __init__(self):
297  FileExportPlugin.__init__(self)
298 
299  #could make these customizeable in the general options dict
300  self.shapes={}
301  self.shapes['EDProducer']='box'
302  self.shapes['EDFilter']='invhouse'
303  self.shapes['EDAnalyzer']='house'
304  self.shapes['OutputModule']='invtrapezium'
305  self.shapes['ESSource']='Mdiamond'
306  self.shapes['ESProducer']='Msquare'
307  self.shapes['Source']='ellipse'
308  self.shapes['Service']='diamond'

Member Function Documentation

def DOTExport.DotExport.dotIndenter (   self,
  dot 
)
Simple indenter for dot output, mainly to prettify it for human reading.

Definition at line 309 of file DOTExport.py.

References join().

Referenced by DOTExport.DotExport.export().

310  def dotIndenter(self,dot):
311  """
312  Simple indenter for dot output, mainly to prettify it for human reading.
313  """
314  spaces = lambda d: ''.join([space]*d)
315  newdot = ""
316  depth = 0
317  space = ' '
318  for line in dot.splitlines():
319  if '{' in line:
320  newdot += spaces(depth)+line+'\n'
321  depth += 1
322  elif '}' in line:
323  depth -= 1
324  newdot += spaces(depth)+line+'\n'
325  else:
326  newdot += spaces(depth)+line+'\n'
327  return newdot
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def DOTExport.DotExport.export (   self,
  data,
  filename,
  filetype 
)

Definition at line 463 of file DOTExport.py.

References DOTExport.DotExport.dotIndenter(), FileExportPlugin.FileExportPlugin.options, cmsswPreprocessor.CmsswPreprocessor.options, DOTExport.DotProducer.options, confdb.HLTProcess.options, production_tasks.Task.options, fireworks::OptionNode.options(), edmIntegrityCheck.IntegrityCheck.options, DOTExport.DotExport.selectNode(), DOTExport.DotProducer.shapes, EcalLaserAnalyzer2.shapes, DOTExport.DotExport.shapes, and DOTExport.DotExport.write_output().

464  def export(self,data,filename,filetype):
465  #if not data.process():
466  # raise "DOTExport requires a cms.Process object"
467 
468  #dot = self.produceDOT(data)
469  dot_producer = DotProducer(data,self.options,self.shapes)
470  dot = dot_producer()
471 
472  if len(dot_producer.nodes)>0:
473 
474  if self.options['node_graphs']:
475  nodes = [n for n in dot_producer.nodes if data.type(dot_producer.nodes[n]['obj']) in ('EDAnalyzer','EDFilter','EDProducer','OutputModule')]
476  for n in nodes:
477  if self.options['node_graphs_restrict'] in n:
478  try:
479  node_dot = self.selectNode(dot,n,self.options['node_depth'])
480  self.write_output(node_dot,filename.replace('.','_%s.'%n),filetype)
481  except:
482  pass
483  else:
484  dot = self.dotIndenter(dot)
485  self.write_output(dot,filename,filetype)
486  else:
487  print "WARNING: Empty image. Not saved."
488 
def DOTExport.DotExport.get_png_size (   self,
  filename 
)

Definition at line 489 of file DOTExport.py.

Referenced by DOTExport.DotExport.write_output().

490  def get_png_size(self,filename):
491  png_header = '\x89PNG\x0d\x0a\x1a\x0a'
492  ihdr = 'IHDR'
493  filedata = open(filename,'r').read(24)
494  png_data = struct.unpack('>8s4s4sII',filedata)
495  if not (png_data[0]==png_header and png_data[2]==ihdr):
496  raise 'PNG header or IHDR not found'
497  return png_data[3],png_data[4]
def DOTExport.DotExport.processMap (   self,
  mapdata 
)
Re-process the client-side image-map produces when png+map is selected.
DOT will only ever put a single URL in the imagemap corresponding to a node, with the 'url' parameter (after html encoding) as the url, and the 'title' parameter as the title. This isn't useful behaviour for our purposes. We want probably several link areas, or a javascript link to make a menu appear, or other more complex behaviour.

If the option 'urlprocess' is turned on, this function is called, and it expects to find a dictionary it can eval in the url parameter. I can't think of a less messy way of passing data to this function without having inner access to DOT at the moment.

This function iterates through all the areas in the mapfile, replacing each one with one or more areas according to the rules in the dictionary stored in the URL parameter.

The dictionary should have structure:
{
  split_x:#,
  split_y:#,
  scale_x:#,
  scale_y:#,
  cells:[
      {
        top:#,
        left:#,
        width:#,
        height:#,
        html_attribute1:"...",
        html_attribute2:"..."
    ]
}
The imagemap is first scaled in size by scale_x and scale_y.
It is then split into split_x*split_y rectangular cells.
New areas are created for each defined cell with the defined top,left location and width,height. This will not check you aren't making new areas that overlap if you define them as such.
The areas then get attributes defined by html_attribute fields - eg, 'html_href':'mypage.htm' becomes 'href'='mypage.htm' in the area. Probably you want as a minimum to define html_href and html_alt. It would also be useful to set html_class to allow highlighting of different link types, or html_onclick/onmouseover for more exotic behaviour.

This will probably be quite sensitive to the presence of special characters, complex splitting schemes, etc. Use with caution.

This may be somewhat replaceable with the <html_label> and cut-down table format that graphviz provides, but I haven't had much of an experiment with that.

Definition at line 393 of file DOTExport.py.

References join().

Referenced by DOTExport.DotExport.write_output().

394  def processMap(self,mapdata):
395  """
396  Re-process the client-side image-map produces when png+map is selected.
397  DOT will only ever put a single URL in the imagemap corresponding to a node, with the 'url' parameter (after html encoding) as the url, and the 'title' parameter as the title. This isn't useful behaviour for our purposes. We want probably several link areas, or a javascript link to make a menu appear, or other more complex behaviour.
398 
399  If the option 'urlprocess' is turned on, this function is called, and it expects to find a dictionary it can eval in the url parameter. I can't think of a less messy way of passing data to this function without having inner access to DOT at the moment.
400 
401  This function iterates through all the areas in the mapfile, replacing each one with one or more areas according to the rules in the dictionary stored in the URL parameter.
402 
403  The dictionary should have structure:
404  {
405  split_x:#,
406  split_y:#,
407  scale_x:#,
408  scale_y:#,
409  cells:[
410  {
411  top:#,
412  left:#,
413  width:#,
414  height:#,
415  html_attribute1:"...",
416  html_attribute2:"..."
417  ]
418  }
419  The imagemap is first scaled in size by scale_x and scale_y.
420  It is then split into split_x*split_y rectangular cells.
421  New areas are created for each defined cell with the defined top,left location and width,height. This will not check you aren't making new areas that overlap if you define them as such.
422  The areas then get attributes defined by html_attribute fields - eg, 'html_href':'mypage.htm' becomes 'href'='mypage.htm' in the area. Probably you want as a minimum to define html_href and html_alt. It would also be useful to set html_class to allow highlighting of different link types, or html_onclick/onmouseover for more exotic behaviour.
423 
424  This will probably be quite sensitive to the presence of special characters, complex splitting schemes, etc. Use with caution.
425 
426  This may be somewhat replaceable with the <html_label> and cut-down table format that graphviz provides, but I haven't had much of an experiment with that.
427  """
428  new_areas=[]
429  area_default = {'split_x':1,'scale_x':1.,'split_y':1,'scale_y':1.,'cells':[]}
430  cell_default = {'top':0,'left':0,'width':1,'height':1,'html_href':'#'}
431  re_area = re.compile('<area.*?/>',re.DOTALL)
432  #sometimes DOT comes up with negative coordinates, so we need to deal with them here (so all the other links will work at least)
433  re_content = re.compile('href="(.*?)" title=".*?" alt="" coords="(-?[0-9]{1,6}),(-?[0-9]{1,6}),(-?[0-9]{1,6}),(-?[0-9]{1,6})"',re.DOTALL)
434  re_htmlunquote = re.compile('&#([0-9]{1,3});')
435  mapdata = re_htmlunquote.sub(lambda x: chr(int(x.group(1))),mapdata)
436  areas = re_area.findall(mapdata)
437  for area in areas:
438  #print area
439  data = re_content.search(area)
440  baseurl = data.group(1)
441  x1,y1,x2,y2 = map(int,(data.group(2),data.group(3),data.group(4),data.group(5)))
442  rad_x,rad_y = int((x2-x1)*0.5),int((y2-y1)*0.5)
443  centre_x,centre_y = x1+rad_x,y1+rad_y
444  basedict = eval(baseurl)
445  for ad in area_default:
446  if not ad in basedict:
447  basedict[ad]=area_default[ad]
448  rad_x = int(rad_x*basedict['scale_x'])
449  rad_y = int(rad_y*basedict['scale_y'])
450  top_x,top_y = centre_x-rad_x,centre_y-rad_y
451  split_x,split_y = int((2*rad_x)/basedict['split_x']),int((2*rad_y)/basedict['split_y'])
452 
453  for cell in basedict['cells']:
454  for cd in cell_default:
455  if not cd in cell:
456  cell[cd]=cell_default[cd]
457  x1,y1 = top_x+split_x*cell['left'],top_y+split_y*cell['top']
458  x2,y2 = x1+split_x*cell['width'],y1+split_y*cell['height']
459  area_html = '<area shape="rect" coords="%s,%s,%s,%s" %s />' % (x1,y1,x2,y2,' '.join(['%s="%s"'%(key.split('_',1)[1],value) for key, value in cell.items() if key.startswith('html_')]))
460  new_areas.append(area_html)
461  return '<map id="configbrowse" name="configbrowse">\n%s\n</map>'%('\n'.join(new_areas))
462 
static std::string join(char **cmd)
Definition: RemoteFile.cc:18
def DOTExport.DotExport.selectNode (   self,
  dotdata,
  node,
  depth_s 
)

Definition at line 328 of file DOTExport.py.

References DOTExport.DotExport.__init__(), and funct.abs().

Referenced by DOTExport.DotExport.export().

329  def selectNode(self,dotdata,node,depth_s):
330  depth = int(depth_s)
331  backtrace=False
332  if depth<0:
333  depth = abs(depth)
334  backtrace=True
335  re_link = re.compile(r'^\s*?(\w*?)->(\w*?)(?:\[.*?\])?$',re.MULTILINE)
336  re_nodedef = re.compile(r'^\s*?(\w*?)(?:\[.*?\])?$',re.MULTILINE)
337  re_title = re.compile(r'^label=\"(.*?)\"$',re.MULTILINE)
338  re_nodeprops = re.compile(r'^\s*?('+node+r')\[(.*?)\]$',re.MULTILINE)
339 
340  nodes = re_nodedef.findall(dotdata)
341  if not node in nodes:
342  raise Exception("Selected node (%s) not found" % (node))
343  links_l = re_link.findall(dotdata)
344  links = {}
345  for link in links_l:
346  if not backtrace:
347  if link[0] in links:
348  links[link[0]] += [link[1]]
349  else:
350  links[link[0]] = [link[1]]
351  if link[1] in links:
352  links[link[1]] += [link[0]]
353  else:
354  links[link[1]] = [link[0]]
355 
356  def node_recursor(links,depthleft,start):
357  if start in links:
358  if depthleft==0:
359  return links[start]+[start]
360  else:
361  result = [start]
362  for l in links[start]:
363  result.extend(node_recursor(links,depthleft-1,l))
364  return result
365  else:
366  return [start]
367 
368 
369  include_nodes = set(node_recursor(links,depth-1,node))
370  include_nodes.add(node)
371 
372  class link_replacer:
373  def __init__(self,include_nodes):
374  self.include_nodes=include_nodes
375  def __call__(self,match):
376  if match.group(1) in self.include_nodes and match.group(2) in self.include_nodes:
377  return match.group(0)
378  return ''
379  class node_replacer:
380  def __init__(self,include_nodes):
381  self.include_nodes=include_nodes
382  def __call__(self,match):
383  if match.group(1) in self.include_nodes:
384  return match.group(0)
385  return ''
386 
387  dotdata = re_link.sub(link_replacer(include_nodes),dotdata)
388  dotdata = re_nodedef.sub(node_replacer(include_nodes),dotdata)
389  dotdata = re_title.sub(r'label="\g<1>\\nDepth '+str(depth_s)+r' from node ' +node+r'"',dotdata,1)
390  dotdata = re_nodeprops.sub('\\g<1>[\\g<2>,color="red"]',dotdata,1)
391 
392  return dotdata
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
def DOTExport.DotExport.write_output (   self,
  dot,
  filename,
  filetype 
)

Definition at line 498 of file DOTExport.py.

References DOTExport.DotExport.get_png_size(), bookConverter.max, FileExportPlugin.FileExportPlugin.options, cmsswPreprocessor.CmsswPreprocessor.options, DOTExport.DotProducer.options, confdb.HLTProcess.options, production_tasks.Task.options, fireworks::OptionNode.options(), edmIntegrityCheck.IntegrityCheck.options, and DOTExport.DotExport.processMap().

Referenced by DOTExport.DotExport.export().

499  def write_output(self,dot,filename,filetype):
500  #don't use try-except-finally here, we want any errors passed on so the enclosing program can decide how to handle them
501  if filetype=='dot':
502  dotfile = open(filename,'w')
503  dotfile.write(dot)
504  dotfile.close()
505  elif filetype=='stdout':
506  print result
507  elif filetype=='pdf':
508  dot_p = subprocess.Popen(['dot','-Tps2'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
509  ps2 = dot_p.communicate(dot)[0]
510  if not dot_p.returncode==0:
511  raise "dot returned non-zero exit code: %s"%dot_p.returncode
512  pdf_p = subprocess.Popen(['ps2pdf','-',filename],stdin=subprocess.PIPE)
513  pdf_p.communicate(ps2)
514  if not pdf_p.returncode==0:
515  raise "ps2pdf returned non-zero exit code: %s"%pdf_p.returncode
516  elif filetype=='png+map':
517  if '.' in filename:
518  filename = filename.split('.')[0]
519  dot_p = subprocess.Popen(['dot','-Tpng','-o','%s.png'%filename,'-Tcmapx_np'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
520  mapdata = dot_p.communicate(dot)[0]
521  if not dot_p.returncode==0:
522  raise "dot returned non-zero exit code: %s"%dot_p.returncode
523  if self.options['urlprocess']:
524  mapdata = self.processMap(mapdata)
525  mapfile = open('%s.map'%filename,'w')
526  mapfile.write(mapdata)
527  mapfile.close()
528  filesize = self.get_png_size('%s.png'%filename)
529  if max(filesize) > self.options['png_max_size']:
530  print "png image is too large (%s pixels/%s max pixels), deleting" % (filesize,self.options['png_max_size'])
531  os.remove('%s.png'%filename)
532  os.remove('%s.map'%filename)
533  elif filetype=='png':
534  dot_p = subprocess.Popen(['dot','-T%s'%(filetype),'-o',filename],stdin=subprocess.PIPE)
535  dot_p.communicate(dot)
536  if not dot_p.returncode==0:
537  raise "dot returned non-zero exit code: %s"%dot_p.returncode
538  filesize = self.get_png_size(filename)
539  if max(filesize) > self.options['png_max_size']:
540  print "png image is too large (%s pixels/%s max pixels), deleting" % (filesize,self.options['png_max_size'])
541  os.remove(filename)
542  else:
543  dot_p = subprocess.Popen(['dot','-T%s'%(filetype),'-o',filename],stdin=subprocess.PIPE)
544  dot_p.communicate(dot)
545  if not dot_p.returncode==0:
546  raise "dot returned non-zero exit code: %s"%dot_p.returncode
547 
548 
549 

Member Data Documentation

tuple DOTExport.DotExport.file_types = ('bmp','dot','eps','gif','jpg','pdf','png','ps','svg','tif','png+map','stdout')
static

Definition at line 294 of file DOTExport.py.

Referenced by FileExportPlugin.FileExportPlugin.fileTypes().

DOTExport.DotExport.include_nodes

Definition at line 373 of file DOTExport.py.

dictionary DOTExport.DotExport.option_types
static
Initial value:
1 = {
2  'legend':('Show Legend','boolean',True),
3  'source':('Show Source','boolean',True),
4  'es':('Show Event Setup','boolean',False),
5  'tagconnect':('Connect InputTags','boolean',True),
6  'seqconnect':('Connect Module Sequence','boolean',False),
7  'services':('Show Services','boolean',False),
8  'endpath':('Show EndPaths','boolean',True),
9  'seq':('Group Sequences','boolean',True),
10  'class':('Show Class','boolean',True),
11  'file':('Show File','boolean',True),
12  'schedule':('Show Schedule','boolean',False),
13  'taglabel':('Show Tag Labels','boolean',True),
14  'color_path':('Path Color','color','#ff00ff'),
15  'color_endpath':('EndPath Color','color','#ff0000'),
16  'color_sequence':('Sequence Color','color','#00ff00'),
17  'color_inputtag':('InputTag Color','color','#0000ff'),
18  'color_schedule':('Schedule Color','color','#00ffff'),
19  'url':('Include URLs','boolean',False), #this is only purposeful for png+map mode
20  'urlprocess':('Postprocess URL (for client-side imagemaps)','boolean',False), #see processMap documentation; determines whether to treat 'urlbase' as a dictionary for building a more complex imagemap or a simple URL
21  'urlbase':('URL to generate','string',"{'split_x':1,'split_y':2,'scale_x':1.,'scale_y':1.,'cells':[{'top':0,'left':0,'width':1,'height':1,'html_href':'http://cmslxr.fnal.gov/lxr/ident/?i=$classname','html_alt':'LXR','html_class':'LXR'},{'top':1,'left':0,'width':1,'height':1,'html_href':'http://cmssw.cvs.cern.ch/cgi-bin/cmssw.cgi/CMSSW/$pypath?view=markup#$pyline','html_alt':'CVS','html_class':'CVS'}]}"), #CVS markup view doesn't allow line number links, only annotate view (which doesn't then highlight the code...)
22  'node_graphs':('Produce individual graphs focussing on each node','boolean',False),
23  'node_graphs_restrict':('Select which nodes to make node graphs for','string',''),
24  'node_depth':('Search depth for individual node graphs','int',1),
25  'font_name':('Font name','string','Times-Roman'),
26  'font_size':('Font size','int',8),
27  'png_max_size':('Maximum edge for png image','int',16768)
28  }

Definition at line 265 of file DOTExport.py.

Referenced by FileExportPlugin.FileExportPlugin.listOptions().

string DOTExport.DotExport.plugin_name = 'DOT Export'
static

Definition at line 293 of file DOTExport.py.

Referenced by FileExportPlugin.FileExportPlugin.pluginName().

DOTExport.DotExport.shapes

Definition at line 299 of file DOTExport.py.

Referenced by DOTExport.DotExport.export().