CMS 3D CMS Logo

List of all members | Public Member Functions | Public Attributes
DOTExport.DotExport Class Reference
Inheritance diagram for DOTExport.DotExport:

Public Member Functions

def __init__ (self)
 
def dotIndenter (self, dot)
 
def export (self, data, filename, filetype)
 
def get_png_size (self, filename)
 
def processMap (self, mapdata)
 
def selectNode (self, dotdata, node, depth_s)
 
def write_output (self, dot, filename, filetype)
 

Public Attributes

 include_nodes
 
 shapes
 

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().

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

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().

309  def dotIndenter(self,dot):
310  """
311  Simple indenter for dot output, mainly to prettify it for human reading.
312  """
313  spaces = lambda d: ''.join([space]*d)
314  newdot = ""
315  depth = 0
316  space = ' '
317  for line in dot.splitlines():
318  if '{' in line:
319  newdot += spaces(depth)+line+'\n'
320  depth += 1
321  elif '}' in line:
322  depth -= 1
323  newdot += spaces(depth)+line+'\n'
324  else:
325  newdot += spaces(depth)+line+'\n'
326  return newdot
327 
def dotIndenter(self, dot)
Definition: DOTExport.py:309
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, eventsfwlite.Events.options, confdb.HLTProcess.options, production_tasks.Task.options, VisualizationOptions.options(), fireworks::OptionNode.options(), edmIntegrityCheck.IntegrityCheck.options, DOTExport.DotExport.selectNode(), DOTExport.DotProducer.shapes, EcalLaserAnalyzer2.shapes, DOTExport.DotExport.shapes, and DOTExport.DotExport.write_output().

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

Definition at line 489 of file DOTExport.py.

Referenced by DOTExport.DotExport.write_output().

489  def get_png_size(self,filename):
490  png_header = '\x89PNG\x0d\x0a\x1a\x0a'
491  ihdr = 'IHDR'
492  filedata = open(filename,'r').read(24)
493  png_data = struct.unpack('>8s4s4sII',filedata)
494  if not (png_data[0]==png_header and png_data[2]==ihdr):
495  raise 'PNG header or IHDR not found'
496  return png_data[3],png_data[4]
497 
def get_png_size(self, filename)
Definition: DOTExport.py:489
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 createfilelist.int, join(), and genParticles_cff.map.

Referenced by DOTExport.DotExport.write_output().

393  def processMap(self,mapdata):
394  """
395  Re-process the client-side image-map produces when png+map is selected.
396  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.
397 
398  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.
399 
400  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.
401 
402  The dictionary should have structure:
403  {
404  split_x:#,
405  split_y:#,
406  scale_x:#,
407  scale_y:#,
408  cells:[
409  {
410  top:#,
411  left:#,
412  width:#,
413  height:#,
414  html_attribute1:"...",
415  html_attribute2:"..."
416  ]
417  }
418  The imagemap is first scaled in size by scale_x and scale_y.
419  It is then split into split_x*split_y rectangular cells.
420  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.
421  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.
422 
423  This will probably be quite sensitive to the presence of special characters, complex splitting schemes, etc. Use with caution.
424 
425  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.
426  """
427  new_areas=[]
428  area_default = {'split_x':1,'scale_x':1.,'split_y':1,'scale_y':1.,'cells':[]}
429  cell_default = {'top':0,'left':0,'width':1,'height':1,'html_href':'#'}
430  re_area = re.compile('<area.*?/>',re.DOTALL)
431  #sometimes DOT comes up with negative coordinates, so we need to deal with them here (so all the other links will work at least)
432  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)
433  re_htmlunquote = re.compile('&#([0-9]{1,3});')
434  mapdata = re_htmlunquote.sub(lambda x: chr(int(x.group(1))),mapdata)
435  areas = re_area.findall(mapdata)
436  for area in areas:
437  #print area
438  data = re_content.search(area)
439  baseurl = data.group(1)
440  x1,y1,x2,y2 = map(int,(data.group(2),data.group(3),data.group(4),data.group(5)))
441  rad_x,rad_y = int((x2-x1)*0.5),int((y2-y1)*0.5)
442  centre_x,centre_y = x1+rad_x,y1+rad_y
443  basedict = eval(baseurl)
444  for ad in area_default:
445  if not ad in basedict:
446  basedict[ad]=area_default[ad]
447  rad_x = int(rad_x*basedict['scale_x'])
448  rad_y = int(rad_y*basedict['scale_y'])
449  top_x,top_y = centre_x-rad_x,centre_y-rad_y
450  split_x,split_y = int((2*rad_x)/basedict['split_x']),int((2*rad_y)/basedict['split_y'])
451 
452  for cell in basedict['cells']:
453  for cd in cell_default:
454  if not cd in cell:
455  cell[cd]=cell_default[cd]
456  x1,y1 = top_x+split_x*cell['left'],top_y+split_y*cell['top']
457  x2,y2 = x1+split_x*cell['width'],y1+split_y*cell['height']
458  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_')]))
459  new_areas.append(area_html)
460  return '<map id="configbrowse" name="configbrowse">\n%s\n</map>'%('\n'.join(new_areas))
461 
462 
def processMap(self, mapdata)
Definition: DOTExport.py:393
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__(), funct.abs(), and createfilelist.int.

Referenced by DOTExport.DotExport.export().

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

Definition at line 498 of file DOTExport.py.

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

Referenced by DOTExport.DotExport.export().

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

Member Data Documentation

DOTExport.DotExport.include_nodes

Definition at line 373 of file DOTExport.py.

DOTExport.DotExport.shapes

Definition at line 299 of file DOTExport.py.

Referenced by DOTExport.DotExport.export().