#!/usr/bin/python # # flow2html.py version 1.0 # 8/29/2005 by W. Rhett Davis (rhett_davis@ncsu.edu) # # (c) 2005, North Carolina State University. All rights reserved. # # Use and copying of this software and preparation of derivative works based # upon this software are permitted. However, any distribution of this # software or derivative works must include the above copyright notice. # It is requested that modifications to this software be sent to the # original author for inclusion with the primary distribution. # # This software is made available AS IS, and NCSU does not make any Warranty # about the software, its performance or its conformity to any specification. import sys, os, re, string, time imagedir='/cygdrive/c/PROGRA~1/ATT/flow2html/images' class dirlist: def __init__(self,dirs=''): # Sets self.dirs to be a list of strings, each containing the # directory name (no slashes). If the first character in the # directory name is a slash, then the first entry in self.dirs # will be an empty string ''. if type(dirs)==str: self.dirs=string.split(dirs,'/') # Get rid of leading blank, if 1st char is not slash if self.dirs[0]=='': if len(dirs)==0 or dirs[0]!='/': self.dirs=self.dirs[1:] # Get rid of the trailing slash, if it exists if len(self.dirs)>0 and self.dirs[len(self.dirs)-1]=='': self.dirs.pop() elif type(dirs)==list: self.dirs=list(dirs) self.compress() def __str__(self): return string.join(self.dirs,'/') def __add__(self,adddir): if type(adddir)==str: adddir=dirlist(adddir) newdir=dirlist(self.dirs+adddir.dirs) elif type(adddir)==list: newdir=dirlist(self.dirs+adddir) elif isinstance(adddir,dirlist): newdir=dirlist(self.dirs+adddir.dirs) else: raise TypeError, str(type(adddir))+" is not one of the expected types (dirlist, str, or list)" return newdir def pop(self,num=1): if len(self.dirs)>=num or (self.dirs[0]=='' and len(self.dirs)>=num+1): self.dirs=self.dirs[:(len(self.dirs)-num)] return True else: return False def absolute(self): if len(self.dirs)>0 and self.dirs[0]=='': return True else: return False def compress(self): newdirlist=[] for d in self.dirs: if d=='..': if len(newdirlist)==0: newdirlist.append('..') elif newdirlist[0]=='..': newdirlist.append('..') elif newdirlist[0]=='': raise Exception, "No way to chdir to .. from /" else: newdirlist.pop() else: if d!='.': newdirlist.append(d) self.dirs=newdirlist def verify(self): for i in range(1,len(self.dirs)+1): if i==1 and self.dirs[0]=='': continue if not os.access(string.join(self.dirs[:i],'/'),os.R_OK): os.mkdir(string.join(self.dirs[:i],'/')) def get_dir_prefix(self,rundir): # Returns a dirlist instance which gives a path to the directory # specified by this instance when currently in the directory # specified by the [rundir] argument. For example: # # This directory rundir return value # /usr/local X11/bin ../.. # /usr/local X11/../bin .. # /usr/local /etc /usr/local # run/cds .. cds # run/cds ../dc ../cds # run/cds ../../.. (error - no way to know) # run/cds /etc (error - no way to know) if type(rundir)==str: rundir=dirlist(rundir) elif type(rundir)==list: rundir=dirlist(rundir) elif isinstance(rundir,dirlist): pass else: raise TypeError, str(type(rundir))+" is not one of the expected types (dirlist, str, or list)" if len(rundir.dirs)>0 and rundir.dirs[0]=='': if len(self.dirs)>0 and self.dirs[0]=='': return self else: raise Exception, "No way to get to "+str(self)+" from "+str(rundir) rundir.compress() if str(rundir)=='': return dirlist() prefix=[] workingdirs=list(self.dirs) dirs=[] for d in reversed(rundir.dirs): if d!='..': prefix.append('..') else: if len(workingdirs)==0: raise Exception, "Directory "+str(rundir)+" nonexistent" dirs.append(workingdirs.pop()) dirs=prefix+dirs return dirlist(dirs) def here_to_there(self,there): # Returns a dirlist instance which gives a path to the directory # specified by [there] from the directory specified by this instance. # For example: # # here there return value # run/cds run/dc ../dc # (none) run/dc run/dc # run/cds (none) ../.. # /usr /bin ../bin # /usr run/dc (error - no way to know) # run/cds /bin /bin if type(there)==str: there=dirlist(there) elif type(there)==list: there=dirlist(there) elif isinstance(there,dirlist): pass else: raise TypeError, str(type(there))+" is not one of the expected types (dirlist, str, or list)" there.compress() if (self.absolute() and not there.absolute()): raise Exception, "No way to get to "+str(there)+" from "+str(self) if (there.absolute() and not self.absolute()): return there heredirs=list(self.dirs) theredirs=list(there.dirs) # Advance to point where directories diverge while len(heredirs)>0: if len(theredirs)==0: break else: if heredirs[0]!=theredirs[0]: break if len(theredirs)>1: theredirs=theredirs[1:] else: theredirs.pop() if len(heredirs)>1: heredirs=heredirs[1:] else: heredirs.pop() pathdirs=[] for d in heredirs: pathdirs.append('..') return dirlist(pathdirs+theredirs) def out_of_date(dep,targ): # If forcing the run, automatically assume it's out of date. if force: return True # Else, check the modification time if not os.access(targ,os.F_OK): return True if not os.access(dep,os.F_OK): raise Exception, "File "+dep+" does not exist" if os.stat(dep)[8]>os.stat(targ)[8]: return True else: return False class htmlleftnav: def __init__(self,title,navlist,cwd=''): self.title=title self.navlist=navlist self.cwd=dirlist(cwd) self.imagedir=dirlist('images') self.cornerimage='flowdb_small.gif' self.navcolor='white' def setHeaderFooter(self): s="\n\n"+self.title+"" s=s+'''
'''+self.title+'''

\n' s=s+'
''' s=s+'' s=s+'LINKS
' s=s+'\n' for e in self.navlist: if len(e)==2: s=s+'' elif len(e)==3: d=str(self.cwd.here_to_there(dirlist(e[1]))) if d=='': s=s+'' else: s=s+'' s=s+''+str(e[0]) s=s+'

\n' s=s+'''

''' self.header=s s='''
















































''' self.footer=s def chdir(self,dirs): self.cwd=self.cwd+dirs def popdir(self,num=1): for i in range(num): self.cwd.dirs.pop() def write(self,filename,body): self.setHeaderFooter() f=open(filename,"w") f.write(self.header+body+self.footer) f.close() class dobj: def __init__(self,name): self.name=name self.doc='' class stepType: def __init__(self,id,name): self.id=id self.dotid=re.sub(r"[^a-zA-Z0-9_]","_",id) self.name=name self.doc='' self.flow=False def writedotfile(self,filename,debug=False): f=open(filename,'w') f.write("digraph G {\n") f.write(" node [fontsize=10,height=0.1];\n") f.write(" nodesep = 0.1;\n") if debug: f.write(' '+self.dotid+' [style=filled,fillcolor=green,label="' \ +self.dotid+'"];\n') else: f.write(' '+self.dotid+' [style=filled,fillcolor=green,label="' \ +self.name+'"];\n') f.write('}\n') f.close() def html(self): s='

Step Type:

\n

'+self.id+'
\n' s=s+self.name+'

\n' s=s+'

\n' s=s+self.doc return s class fileType: def __init__(self,id,name): self.id=id self.dotid=re.sub(r"[^a-zA-Z0-9_]","_",id) self.name=name self.doc='' self.flow=False def writedotfile(self,filename,debug=False): f=open(filename,'w') f.write("digraph G {\n") f.write(" node [fontsize=10,height=0.1];\n") f.write(" nodesep = 0.1;\n") if debug: f.write(' '+self.dotid+' [shape=box,label="'+self.dotid+'"];\n') else: f.write(' '+self.dotid+' [shape=box,label="'+self.name+'"];\n') f.write('}\n') f.close() def html(self): s='

File Type:

\n

'+self.id+'
\n' s=s+self.name+'

\n' s=s+'

\n' s=s+self.doc return s class subStep: def __init__(self,id,steptype,name): self.id=id # This id also acts as a pointer to the step object self.dotid=re.sub(r"[^a-zA-Z0-9_]","_",id) self.steptype=steptype self.name=name self.doc='' self.cmd=False self.rundir=False self.rank=False class fileNode: def __init__(self,id,filetype,filename,name): self.id=id #This id is valid only in the context of a step self.dotid=re.sub(r"[^a-zA-Z0-9_]","_",id) self.filetype=filetype # pointer to the list of filetypes self.filename=filename self.name=name self.doc='' self.rank=False class observation: def __init__(self,id,name): self.id=id #This id is valid only in the context of a step self.dotid=re.sub(r"[^a-zA-Z0-9_]","_",id) self.name=name self.doc='' self.rank=False class step: def __init__(self,id,steptype,filename,name): self.id=id self.dotid=re.sub(r"[^a-zA-Z0-9_]","_",id) self.steptype=steptype self.filename=filename self.name=name self.doc='' self.flow=False self.args=[] self.techvars=[] self.substeps=[] self.filenodes=[] self.observations=[] self.edges=[] def writedotfile(self,filename,fl,debug=False): f=open(filename,'w') f.write("digraph G {\n") f.write(" node [fontsize=10,height=0.1];\n") f.write(" nodesep = 0.1;\n") if len(self.substeps+self.filenodes)==0: if debug: f.write(' '+self.dotid+ \ ' [style=filled,fillcolor=green,label="' \ +self.dotid+'"];\n') else: f.write(' '+self.dotid+ \ ' [style=filled,fillcolor=green,label="' \ +self.name+'"];\n') else: for n in self.substeps: #if fl.findStep(n.id): # link=n.id+'.html' #else: # link='#'+n.dotid if n.rank: f.write(' { rank='+n.rank+';\n') link='#'+n.dotid if debug: f.write(' '+n.dotid+ \ ' [style=filled,fillcolor=palegreen,label="' \ +n.dotid+'",URL="'+link+'"];\n') else: f.write(' '+n.dotid+ \ ' [style=filled,fillcolor=palegreen,label="' \ +n.name+'",URL="'+link+'"];\n') if n.rank: f.write(' }\n') for n in self.filenodes: if n.rank: f.write(' { rank='+n.rank+';\n') link='#'+n.dotid if debug: f.write(' '+n.dotid+ \ ' [shape=box,label="'+n.dotid+'",URL="'+ \ link+'"];\n') else: f.write(' '+n.dotid+ \ ' [shape=box,label="'+n.name+'",URL="'+ \ link+'"];\n') if n.rank: f.write(' }\n') for n in self.observations: if n.rank: f.write(' { rank='+n.rank+';\n') link='#'+n.dotid if debug: f.write(' '+n.dotid+ \ ' [style=filled,fillcolor=lightblue,shape=diamond,label="'+n.dotid+'",URL="'+ \ link+'"];\n') else: f.write(' '+n.dotid+ \ ' [style=filled,fillcolor=lightblue,shape=diamond,label="'+n.name+'",URL="'+ \ link+'"];\n') if n.rank: f.write(' }\n') for e in self.edges: f.write(' '+e+';\n') f.write('}\n') f.close() def html(self,f,home): s='
Step:\n' s=s+'

'+self.id+'
\n' s=s+self.name+'

\n' steptype=f.findStepType(self.steptype) if not steptype: print "WARNING: could not find definition for STEPTYPE", \ self.steptype,"in step",self.id else: s=s+'
Type:'+steptype.name+'\n' s=s+'
File:'+self.filename+'\n' s=s+'

\n' s=s+'
\n' s=s+'\n' fp=open(str(home)+'/steps/'+self.id+'.cmap','r') for line in fp: s=s+line fp.close() s=s+'


\n' s=s+self.doc+'\n
\n' if len(self.args)>0: s=s+'

Arguments:

\n' for arg in self.args: s=s+'
'+arg.name+'\n' s=s+''+arg.doc s=s+'

\n' if len(self.techvars)>0: s=s+'

Technology Variables:

\n' for var in self.techvars: s=s+'
'+var.name+'\n' s=s+''+var.doc s=s+'

\n' if len(self.filenodes)>0: s=s+'

Files:

\n' for n in self.filenodes: s=s+'' s=s+'\n' s=s+'
File:\n' s=s+''+n.id+'
' s=s+re.sub(r"\\n",' ',n.name)+'
' filetype=f.findFileType(n.filetype) if not filetype: print "WARNING: could not find definition for FILETYPE", \ n.filetype,"in FILE",n.id else: s=s+'
Type:'+filetype.name+'\n' s=s+'
Name:\n' s=s+''+n.filename+'\n' s=s+'
\n
'+n.doc+'

\n' s=s+'
\n' if len(self.substeps)>0: s=s+'

Substeps:

\n' for n in self.substeps: s=s+'' s=s+'\n' s=s+'
Substep:\n' stepobj=f.findStep(n.id) if stepobj: link='' else: link='' s=s+''+link+''+n.id+'
' s=s+re.sub(r"\\n",' ',n.name)+'
' if stepobj: s=s+'' steptype=f.findStepType(stepobj.steptype) else: steptype=f.findStepType(n.steptype) if not steptype: print "WARNING: could not find definition for STEPTYPE", \ n.steptype,"in SUBSTEP",n.id else: s=s+'
Type:'+steptype.name+'\n' if n.cmd: s=s+'
Command:\n' s=s+''+n.cmd.name+'\n' if n.rundir: s=s+'
Run Directory:\n' s=s+''+n.rundir.name+'\n' s=s+'
\n
'+n.doc+'

\n' s=s+'
\n' return s class flow: def __init__(self): self.id=False self.top=False self.name='' self.doc='' self.date=False self.insturl=False self.instname=False self.filename=False self.authors=[] self.steps=[] self.steptypes=[] self.filetypes=[] self.subflows=[] def readfile(self,filename): lineno=0 co=False # Initialize the current object cs=False # Initialize the current step css=False # Initialize the current substep self.filename=filename f=open(filename,'r') for line in f: lineno=lineno+1 m=re.search(r"^\s*#(.*)$",line) # Comment if m: if co: co.doc=co.doc+m.group(1).strip()+'\n' # Otherwise, it's a comment at the beginning of the # File, and these are ignored continue m=re.search(r"^\s*INCLUDE\s+(.*)$",line) if m: newdir=dirlist(filename) newdir.pop() newdir=newdir+m.group(1).strip() newdir.compress() newflow=flow() newflow.readfile(str(newdir)) self.subflows.append(newflow) for n in newflow.steps: self.steps.append(n) for n in newflow.steptypes: self.steptypes.append(n) for n in newflow.filetypes: self.filetypes.append(n) for n in newflow.subflows: self.subflows.append(n) continue m=re.search(r"^\s*FLOW\s+(\S+)\s+(\S+)\s+(.*)$",line) if m: if self.id: print "WARNING: Ignoring FLOW redefinition on line",lineno continue self.id=m.group(1) self.top=m.group(2) self.name=m.group(3).strip() co=self continue m=re.search(r"^\s*AUTHOR\s+(\S+)\s+(.*)$",line) if m: self.authors.append([m.group(1),m.group(2).strip()]) continue m=re.search(r"^\s*INSTITUTION\s+(\S+)\s+(.*)$",line) if m: self.insturl=m.group(1) self.instname=m.group(2).strip() continue m=re.search(r"^\s*DATE\s+(\d+\s+[A-Z][a-z][a-z]\s+\d+)\s*$",line) if m: self.date=time.strptime(m.group(1),'%d %b %Y') #s=s+time.strftime('%d %b %Y',self.date) continue m=re.search(r"^\s*STEPTYPE\s+(\S+)\s+(.*)$",line) if m: co=stepType(m.group(1),m.group(2).strip()) co.flow=self for st in self.steptypes: if st.id==co.id: print "WARNING: STEPTYPE",m.group(1),"redefined", print "on line "+str(lineno)+", ignoring" continue self.steptypes.append(co) cs=False # re-Initialize cs & css css=False continue m=re.search(r"^\s*FILETYPE\s+(\S+)\s+(.*)$",line) if m: co=fileType(m.group(1),m.group(2).strip()) co.flow=self for ft in self.filetypes: if ft.id==co.id: print "WARNING: FILETYPE",m.group(1),"redefined", print "on line "+str(lineno)+", ignoring" continue self.filetypes.append(co) cs=False # re-Initialize cs & css css=False continue m=re.search(r"^\s*STEP\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$",line) if m: co=step(m.group(1),m.group(2),m.group(3),m.group(4).strip()) co.flow=self for s in self.steps: if s.id==co.id: print "WARNING: STEP",m.group(1),"redefined", print "on line "+str(lineno)+", ignoring" continue self.steps.append(co) cs=co # re-Initialize cs & css css=False continue m=re.search(r"^\s*ARG\s+(.*)$",line) if m: if not cs: print "WARNING: ARG",m.group(1),"found outside a STEP", print " on line",lineno continue co=dobj(m.group(1).strip()) cs.args.append(co) css=False continue m=re.search(r"^\s*TECHVAR\s+(.*)$",line) if m: if not cs: print "WARNING: TECHVAR",m.group(1),"found outside a STEP", print " on line",lineno continue co=dobj(m.group(1).strip()) cs.techvars.append(co) css=False continue m=re.search(r"^\s*SUBSTEP\s+(\S+)\s+(\S+)\s+(.*)$",line) if m: if not cs: print "WARNING: SUBSTEP",m.group(1),"found outside a STEP", print " on line",lineno continue co=subStep(m.group(1),m.group(2),m.group(3).strip()) for n in cs.substeps+cs.filenodes+cs.observations: if n.dotid==co.dotid: print "WARNING: SUBSTEP",m.group(1), \ "aliases previously defined",n.id, \ "on line "+str(lineno) print "Both will be called",n.dotid, \ "in dot file" break cs.substeps.append(co) css=co continue m=re.search(r"^\s*CMD\s+(.*)$",line) if m: if not css: print "WARNING: CMD",m.group(1),"found outside a SUBSTEP", print " on line",lineno continue co=dobj(m.group(1).strip()) css.cmd=co continue m=re.search(r"^\s*RUNDIR\s+(.*)$",line) if m: if not css: print "WARNING: RUNDIR",m.group(1),"found outside a SUBSTEP", print " on line",lineno continue co=dobj(m.group(1).strip()) css.rundir=co continue m=re.search(r"^\s*FILE\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)$",line) if m: if not cs: print "WARNING: FILE",m.group(1),"found outside a STEP", print " on line",lineno continue co=fileNode(m.group(1),m.group(2),m.group(3),m.group(4).strip()) for n in cs.substeps+cs.filenodes+cs.observations: if n.dotid==co.dotid: print "WARNING: FILE",m.group(1), \ "aliases previously defined",n.id, \ "on line "+str(lineno) print "Both will be called",n.dotid, \ "in dot file" break cs.filenodes.append(co) css=False continue m=re.search(r"^\s*OBSV\s+(\S+)\s+(.*)$",line) if m: if not cs: print "WARNING: OBSV",m.group(1),"found outside a STEP", print " on line",lineno continue co=observation(m.group(1),m.group(2).strip()) for n in cs.substeps+cs.filenodes+cs.observations: if n.dotid==co.dotid: print "WARNING: OBSV",m.group(1), \ "aliases previously defined",n.id, \ "on line "+str(lineno) print "Both will be called",n.dotid, \ "in dot file" break cs.observations.append(co) css=False continue m=re.search(r"^\s*RANK\s+(\S+)",line) if m: if co: co.rank=m.group(1) continue m=re.search(r"\-\>",line) if m: if not cs: print "WARNING: edge",line.strip(),"found outside a STEP", print " on line",lineno continue cs.edges.append(line.strip()) continue m=re.search(r"^\s*$",line) if m: continue # Ignore blank lines print "WARNING: line",lineno,"not recognized:",line f.close() def findStep(self,id): for s in self.steps: if s.id==id: return s break return False def findStepType(self,id): for s in self.steptypes: if s.id==id: return s break return False def findFileType(self,id): for s in self.filetypes: if s.id==id: return s break return False def publish(self): home=dirlist(self.id) home.verify() subdir=home+'images' subdir.verify() os.system('cp '+imagedir+'/flowdb_small.gif '+str(subdir)+ \ '/flowdb_small.gif') subdir=home+'steptypes' subdir.verify() n=[['Top','','index.html'], \ ['Steps','steps','index.html'], \ ['Step Types','steptypes','index.html'], \ ['File Types','filetypes','index.html'], \ ['Flow Index','http://www.ece.ncsu.edu/muse/flowdb']] h=htmlleftnav(self.name,n) # Write index.html s='

\n'+self.id+'

\n' s=s+'

\n'+self.name+'

\n' s=s+'' if self.date: s=s+'
Date:'+time.strftime('%d %B %Y',self.date)+'\n' if len(self.authors)>0: s=s+'
Authors:\n' for n in self.authors: s=s+''+n[1]+'
\n' if self.instname: s=s+'
Institution:\n' s=s+''+self.instname+'' s=s+'
\n' s=s+'

'+self.doc+'\n' s=s+'

Top-Level Step: ' stp=self.findStep(self.top) if not stp: raise Exception, "ERROR: Top level step id "+self.top+" not found" s=s+stp.name+'

\n' h.write(str(home)+'/index.html',s) # Write steptypes/index.html subdir=home+'steptypes' subdir.verify() s='

Step Types

\n\n' self.steptypes.sort(key=lambda x: x.name) for n in self.steptypes: s=s+''+n.name+'
\n' h.chdir('steptypes') h.write(str(subdir)+'/index.html',s) for n in self.steptypes: gif=str(subdir)+'/'+n.id+'.gif' dot=str(subdir)+'/'+n.id+'.dot' cmap=str(subdir)+'/'+n.id+'.cmap' html=str(subdir)+'/'+n.id+'.html' if n.flow and out_of_date(n.flow.filename,html): n.writedotfile(dot) os.system('dot -Tgif '+dot+' -o '+gif) os.system('dot -Tcmap '+dot+' -o '+cmap) h.write(html,n.html()) h.popdir() # Write filetypes/index..html subdir=home+'filetypes' subdir.verify() s='

File Types

\n\n' self.filetypes.sort(key=lambda x: x.name) for n in self.filetypes: s=s+'
'+n.name+'
\n' h.chdir('filetypes') h.write(str(subdir)+'/index.html',s) for n in self.filetypes: gif=str(subdir)+'/'+n.id+'.gif' dot=str(subdir)+'/'+n.id+'.dot' cmap=str(subdir)+'/'+n.id+'.cmap' html=str(subdir)+'/'+n.id+'.html' if n.flow and out_of_date(n.flow.filename,html): n.writedotfile(dot) os.system('dot -Tgif '+dot+' -o '+gif) os.system('dot -Tcmap '+dot+' -o '+cmap) h.write(html,n.html()) h.popdir() # Write steps/index..html subdir=home+'steps' subdir.verify() s='

Steps

\n\n' self.steps.sort(key=lambda x: x.name) for n in self.steps: s=s+'
'+n.name+'
\n' h.chdir('steps') h.write(str(subdir)+'/index.html',s) for n in self.steps: gif=str(subdir)+'/'+n.id+'.gif' dot=str(subdir)+'/'+n.id+'.dot' cmap=str(subdir)+'/'+n.id+'.cmap' html=str(subdir)+'/'+n.id+'.html' if n.flow and out_of_date(n.flow.filename,html): n.writedotfile(dot,self) os.system('dot -Tgif '+dot+' -o '+gif) os.system('dot -Tcmap '+dot+' -o '+cmap) h.write(html,n.html(self,home)) h.popdir() topfile='' force=0 # Parse arguments args=sys.argv[1:] while (len(args)>0): arg=args[0] args=args[1:] if (arg=="-force"): force=1 elif not topfile: topfile=arg else: print "Unrecognized argument "+arg f=flow() f.readfile(topfile) f.publish()