# Python Script for Adaptive Path # Convert Mental Model Excel WorkSheet or Word Doc to Visio Chart # # 4-6-2006 Gary Wang firepotter@gmail.com # #---------------------------------------------------------------------------------------- from win32com.client import Dispatch import string, os, sys filePath = '' # leave these blank if you want to use the default excelFilename = '' """ These are the printable characters: 0123456789abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ """ boxInTopDownOrder = 1 generateDebugFiles = 1 #set to 1 to generate a debug output file # # Color Generator Function def makeColor( red=1, green=1, blue=1 ): rc = int( red*255 ) % 256 gc = int( green*255 ) % 256 bc = int( blue*255 ) % 256 return ( bc*0x010000 + gc*0x000100 + rc*0x000001 ) colorBlack = makeColor(red=0,green=0,blue=0) colorRed = makeColor(red=1,green=0,blue=0) colorGreen = makeColor(red=0,green=1,blue=0) colorYellow = makeColor(red=1,green=1,blue=0) colorBlue = makeColor(red=0,green=0,blue=1) colorMagenta = makeColor(red=1,green=0,blue=1) colorCyan = makeColor(red=0,green=1,blue=1) colorWhite = makeColor(red=1,green=1,blue=1) colorLightCyan = makeColor(red=0.5,green=1,blue=1) # Chart Property Constants filePath = '' # leave these blank if you want to use the default wordFilename = '' #unit = inch PageLength=8.5 #PageWidth=88 PageMargin = 0.25 ChartStartX=1+5.0/8 HorizontalLinePosition = PageLength/2.0-0.125 Box2BoxSpacing = 0.0625 Box2TowerSpacing = 0.125 Tower2TowerSpacing = 0.125 Tower2SectionSpacing = 0.125 TowerHeaderExtra = 0.250+0.125 SectionHeight = PageLength/2-PageMargin - 0.25 MaxBoxNumberPerColumn = 7 BOXHEIGHT=0.375 BOXWIDTH=0.625 TOWERWIDTH=0.875 DefaultLineWeight = 0.06 SectionFontPoint = 12 SectionLineWeight = 0.01 SectionFill = 0 SectionFillColor = colorWhite SectionLine = 0 SectionLineColor = colorWhite TowerFontPoint = 6 TowerLineWeight = 0.01 TowerFill = 1 TowerFillColor = colorLightCyan TowerLine = 0 TowerLineColor = colorLightCyan BoxFontPoint = 6 BoxLineWeight = 0.01 BoxFill = 1 BoxFillColor = colorWhite BoxLine = 1 BoxLineColor = colorBlack # # Global Variables sectionIndex = 0 towerIndex = 0 boxIndex = 0 sectionX=2 sectionY=0 sectionNextX = ChartStartX towerX=0 towerY=0 styleList = {} #---------------------------------------------------------------------------------------- import string, os, sys, time allowedFileTypeList = ['.xml'] operatingSystem = os.environ['OS'] print 'OS=',operatingSystem if operatingSystem in ['Windows_NT']: allowedFileTypeList.append('.xls') allowedFileTypeList.append('.doc') # from xml.sax import saxutils from xml.sax import ContentHandler from xml.sax import make_parser from xml.sax.handler import feature_namespaces def hex2Integer( s='' ): s = string.lower(s) sum = 0 for c in s: i = string.find( string.hexdigits, c ) if i>=0: sum = sum*16 + i return sum def normalize_whitespace(text): "Remove redundant whitespace from a string" return ' '.join(text.split()) class ProcessData(ContentHandler): def __init__(self): self.inDataContent = 0 self.inTargetSheet = 0 self.sID = "Default" self.styleList = {} self.dataList = {} def startElement(self, name, attrs): try: if name=='Style': self.sID = attrs.get('ss:ID', None) self.styleList[self.sID]={} if name=='Font': self.styleList[self.sID]['Family']=attrs.get('x:Family', None) self.styleList[self.sID]['Size'] = attrs.get('ss:Size', None) self.styleList[self.sID]['Color'] = attrs.get('ss:Color', '#FFFFFF') self.styleList[self.sID]['Italic'] = attrs.get('ss:Italic', None) self.styleList[self.sID]['Bold'] = attrs.get('ss:Bold', None) self.styleList[self.sID]['Underline'] = attrs.get('ss:Underline', None) if name=='Interior': self.styleList[self.sID]['iColor'] = attrs.get('ss:Color', '#FFFFFF') self.styleList[self.sID]['iPattern'] = attrs.get('ss:Pattern', None) # if name=='Worksheet': self.currentSheet = attrs.get('ss:Name', None) self.currentRow = 0 if name=='Row': self.currentRow = self.currentRow + 1 self.currentCell = 0 if name=='Cell': self.sID = attrs.get('ss:StyleID', None) sIx = attrs.get('ss:Index', None) if sIx==None: self.currentCell = self.currentCell + 1 else: #print sIx self.currentCell = string.atoi(sIx) if name=='Data': self.inDataContent = 1 self.dataContent = "" except: pass def characters(self, ch): if self.inDataContent: #XML special characters (< > & ") must be encoded (< > & ") #to be used in python code. This, unfortunately, is unavoidable. if ch=='&': ch = '&' if ch=='<': ch = '<' if ch=='>': ch = '>' if ch=='"': ch = '"' self.dataContent = self.dataContent + ch def endElement(self, name): if name=='Data': self.inDataContent = 0 self.dataContent = normalize_whitespace(self.dataContent) data = (self.currentRow, self.currentCell, self.sID, self.dataContent) if not self.dataList.has_key(self.currentSheet): self.dataList[self.currentSheet] = [] self.dataList[self.currentSheet].append( data ) def readXMLDoc( filename, debug=1 ): # Create a parser parser = make_parser() # Tell the parser we are not interested in XML namespaces parser.setFeature(feature_namespaces, 0) # Create the handler dh = ProcessData() # Tell the parser to use our handler parser.setContentHandler(dh) # Parse the input parser.parse(filename) tmpFilename = filename[:-4]+'tmp2139' fd = open( tmpFilename+".txt", "w" ) operatingSystem = os.environ['OS'] fd.write('\nOS=%s\n' % (operatingSystem) ) fd.write('Script=%s\n' % (sys.argv[0]) ) fd.write('Date=%s\n' % (time.asctime() ) ) fd.flush() for key in dh.styleList.keys(): ss = '%s' % (key) for skey in dh.styleList[key].keys(): ss = ss + ', %s:%s ' % (skey, dh.styleList[key][skey]) if debug: print ss fd.write(ss+'\n') fd.write('\n\n') workSheetKeys = dh.dataList.keys() #print workSheetKeys stack = {} for worksheet in workSheetKeys: (chart, section, tower) = ([],[],[]) (sectionIndex, towerIndex, boxIndex) = (0,0,0) #print worksheet for (row, col, sID, text) in dh.dataList[worksheet]: if row==1 and text=='Mental Space': sectionIndex = col if row==1 and text=='Task Tower': towerIndex = col if row==1 and text=='Task': boxIndex = col if row>1 and (sectionIndex, towerIndex, boxIndex) == (0,0,0): break text = '%s::::%s' % (text , sID) if row>1 and col==sectionIndex: if len(section)>0: if len(tower)>0: section.append(tower) chart.append(section) section=[text] tower = [] if row>1 and col==towerIndex: if len(tower)>0: section.append(tower) tower=[text] if row>1 and col==boxIndex: tower.append( text ) if len(section)>0: if len(tower)>0: section.append(tower) chart.append(section) if len(chart)>0: stack[worksheet]=chart fd.close() if not debug: os.remove( tmpFilename+".txt" ) return (dh.styleList, stack) def process( s ): s1 = s[10:] s2 = '' flag = 0 for i in range(0,len(s1)): bStart = (s1[i]=='<') bEnd = (s1[i]=='>') if bStart: flag = 1 if not flag: if s1[i] in string.printable: s2 = s2 + s1[i] if bEnd: flag = 0 s3 = string.split(s2,'>') s4 = string.split(s3[0],' ') return (s4[0],s3[-1]) def readWordDocFast( fname='', debug=0 ): #Faster Method. #Save to a *.htm file and parse it. print fname chart=[] if fname=='': return chart filename = fname[:-4] style = {} style['APHeading2'] = 'section' style['APHeading3'] = 'tower' style['APHeading4'] = 'box' tmpFilename = filename+'tmp2138' filteredHTMLFormat = 8 app = Dispatch("Word.Application") """ #using word's File Open GUI ????? #app.Visible = 1 #app.Dialogs(80).Show() app.Dialogs(80).Display() print 'Name is', app.Dialogs(80).Name """ app.Visible = 0 doc = app.Documents.Open( filename + ".doc" ) doc.SaveAs(tmpFilename+".htm",filteredHTMLFormat) doc.Close(0) del app fd = open( tmpFilename+".htm", 'r') lines=fd.readlines() fd.close() os.remove( tmpFilename+"_files\\filelist.xml" ) os.remove( tmpFilename+"_files\\header.htm" ) os.rmdir( tmpFilename+"_files") if not debug: os.remove( tmpFilename+".htm" ) fd = open( tmpFilename+".txt", "w" ) output = [] flag = 0 tFlag = 0 s = '' for line in lines: pStart = string.find(line,'

')>=0 tStart = string.find(line,'=0 tEnd = string.find(line,'')>=0 if tStart: tFlag = 1 if tEnd: tFlag = 0 pStart = 0 s = '' flag = 0 if pStart: flag = 1 if (pStart or pEnd or flag) and (not tFlag): s = s + ' ' + string.strip(line) if pEnd: flag = 0 if len(s)>0: (key,t) = process(s) if key in style.keys(): t = string.strip(t) #t = string.replace(t,'&','&') #t = string.replace(t,'x',"'") if style[key]=='section': section = t chart.append([section]) sectionIndex = len(chart)-1 if style[key]=='tower': tower = t chart[sectionIndex].append([tower]) towerIndex = len(chart[sectionIndex])-1 if style[key]=='box': chart[sectionIndex][towerIndex].append(t) ss = "%s %7s %s" % (key, style[key], t) print ss fd.write( ss+'\n' ) fd.write( s+'\n' ) s = '' fd.close() if not debug: os.remove( tmpFilename+".txt" ) return chart def readWordDocSlow( filename='' ): from win32com.client import Dispatch #Get the info from Word Objects #This is slower than readWordDoc2 chart=[] if filename=='': return chart style = {} style['AP Heading 2'] = 'section' style['AP Heading 3'] = 'tower' style['AP Heading 4'] = 'box' app = Dispatch("Word.Application") app.Visible = 0 doc = app.Documents.Open(filename) for para in doc.paragraphs: s = para.format.style.nameLocal if s in style.keys(): t = string.strip(para.Range.Text) t = string.replace(t,'&','&') if style[key]=='section': section = t chart.append([section]) sectionIndex = len(chart)-1 if style[key]=='tower': tower = t chart[sectionIndex].append([tower]) towerIndex = len(chart[sectionIndex])-1 if style[key]=='box': chart[sectionIndex][towerIndex].append(t) print "%s %7s %s" % (key, style[key], t) doc.Close(0) del app return chart def drawVisio( filename, shapes, width=88, length=PageLength ): fd = open( filename, 'w' ) fd.write("\n") fd.write("\n") fd.write("\n") fd.write("\n") fd.write("\n") fd.write("\n") fd.write("%.2f\n" % (width) ) fd.write("%.2f\n" % (length) ) fd.write("1\n") fd.write("3\n") fd.write("0\n") fd.write("\n") fd.write("\n") fd.write("8\n") fd.write("8\n") fd.write("4\n") fd.write("4\n") fd.write("\n") fd.write("\n") fd.write("\n") shapeID = 1 for (X, Y, Width, Height, Text, vAlign, TextColor, TextFont, TextPoint, TextStyle, Line, \ LineColor, LineWeight, Fill, FillColor) in shapes: shapeWidth = Width shapeHeight = Height shapeX = X shapeY = Y fillColor = FillColor lineWeight = LineWeight lineColor = LineColor textMarginHorizontal = 0.02 textMarginVertical = 0.02 verticalAlignment = vAlign fill = Fill line = Line text = Text pSize = TextPoint*(0.11111111111/8) if string.find(text,'::::')>=0: tlist = string.split(text,'::::') text = tlist[0] try: fillColor = string.atoi(styleList[tlist[-1]]['iColor'][1:],16) except: pass x1 = 0 x2 = shapeWidth y1 = 0 y2 = shapeHeight shapeCenterX = shapeX + shapeWidth/2.0 shapeCenterY = shapeY + shapeHeight/2.0 #LineStyle='6' FillStyle='6' TextStyle='6' fd.write("\n" % (shapeID, shapeID) ) fd.write("\n") fd.write("%.4f\n" % (shapeCenterX) ) fd.write("%.4f\n" % (shapeCenterY) ) fd.write("%.4f\n" % (shapeWidth) ) fd.write("%.4f\n" % (shapeHeight) ) fd.write("%.4f\n" % (shapeWidth/2.0) ) fd.write("%.4f\n" % (shapeHeight/2.0) ) fd.write("\n") fd.write("%.3f\n" % (lineWeight) ) if lineColor!=0: fd.write("#%x\n" % (lineColor) ) fd.write("\n") fd.write("#%x\n" % (fillColor) ) fd.write("\n") fd.write("0\n") fd.write("0\n") fd.write("\n" % (TextStyle) ) fd.write("0\n") fd.write("0\n") fd.write("%.10f\n" % (pSize) ) fd.write("\n") fd.write("\n") fd.write("%.3f\n" % (textMarginHorizontal) ) fd.write("%.3f\n" % (textMarginHorizontal) ) fd.write("%.3f\n" % (textMarginVertical) ) fd.write("%.3f\n" % (textMarginVertical) ) fd.write("%d\n" % (verticalAlignment) ) fd.write("\n") fd.write("\n") fd.write("%d\n" % (1-fill) ) fd.write("%d\n" % (1-line) ) fd.write("0\n") fd.write("0\n") fd.write("\n%.4f\n%.4f\n\n" % (x1,y1) ) fd.write("\n%.4f\n%.4f\n\n" % (x2,y1) ) fd.write("\n%.4f\n%.4f\n\n" % (x2,y2) ) fd.write("\n%.4f\n%.4f\n\n" % (x1,y2) ) fd.write("\n%.4f\n%.4f\n\n" % (x1,y1) ) fd.write("\n") try: fd.write("%s\n" % (text) ) except: fd.write("%s\n" % ('ERROR???') ) fd.write("\n") shapeID = shapeID + 1 fd.write("\n\n\n\n") fd.close() return 1 def shape( X, Y, Width=1, Height=1, Text='', vAlign=1, TextColor=0xffff80, TextFont='arial', \ TextPoint=6, TextStyle=0, Line=1, LineColor=0x000000, LineWeight=0.02, \ Fill=0, FillColor=0xffffff): return((X, Y, Width, Height, Text, vAlign, TextColor, TextFont, TextPoint, TextStyle,\ Line, LineColor, LineWeight, Fill, FillColor)) def line( x1, y1, x2, y2, Text='' ): Width=x2-x1 Height=y2-y1 return shape( x1, y1, Width, Height, Text, LineWeight=DefaultLineWeight ) def sectionBox( section ): global sectionIndex, towerIndex global sectionX, sectionY, sectionNextX global towerX, towerY Text = section[0] numberOfTowers = len(section)-1 sectionIndex = sectionIndex + 1 Width = Tower2TowerSpacing for tower in section[1:]: m = int(((len(tower)-1)-0.1)/MaxBoxNumberPerColumn) + 1 Width = Width + (0.1+(TOWERWIDTH-0.1)*m) + Tower2TowerSpacing if numberOfTowers==0: Width = Width+TOWERWIDTH+Tower2TowerSpacing Height= SectionHeight sectionX = sectionNextX sectionNextX = sectionNextX + Width sectionY = PageLength/2.0 towerIndex = 0 return shape( sectionX, sectionY, Width, Height, Text, vAlign=0, TextStyle=1, \ LineWeight=SectionLineWeight, Fill=SectionFill, Line=SectionLine,\ FillColor=SectionFillColor, LineColor=SectionLineColor, TextPoint=SectionFontPoint ) def towerBox( tower ): global towerIndex, boxIndex global sectionX, sectionY global towerX, towerY Text=tower[0] boxNumber=len(tower)-1 widthMultiplier = 1 + int((boxNumber-0.1)/7) towerIndex = towerIndex + 1 boxIndex = 0 towerX=sectionX+Tower2SectionSpacing+(towerIndex-1)*(TOWERWIDTH+Tower2TowerSpacing) towerY=sectionY bn = boxNumber if bn>7: bn=7 Height = bn*(BOXHEIGHT+Box2BoxSpacing)+TowerHeaderExtra Width = 2*Box2TowerSpacing-Box2BoxSpacing+(BOXWIDTH+Box2BoxSpacing)*widthMultiplier return shape( towerX, towerY, Width, Height, Text, vAlign=0, \ LineWeight=TowerLineWeight, Fill=TowerFill, Line=TowerLine,\ FillColor=TowerFillColor, LineColor=TowerLineColor, TextPoint=TowerFontPoint ) def boxBox( Text='Box' ): global boxIndex global sectionX, sectionY global towerX, towerY boxIndex = boxIndex + 1 if boxIndex>MaxBoxNumberPerColumn: towerX = towerX + BOXWIDTH + Box2BoxSpacing towerY = sectionY sectionX = sectionX + BOXWIDTH + Box2BoxSpacing boxIndex = 1 boxX = towerX + Box2TowerSpacing boxY = towerY + ((BOXHEIGHT+Box2BoxSpacing)*(boxIndex-1)) + Box2BoxSpacing Width=BOXWIDTH Height=BOXHEIGHT return shape( boxX, boxY, Width, Height, Text, vAlign=1, \ LineWeight=BoxLineWeight, Fill=BoxFill, Line=BoxLine,\ FillColor=BoxFillColor, LineColor=BoxLineColor, TextPoint=BoxFontPoint ) def drawSection( section ): s = [] s.append( sectionBox( section ) ) for tower in section[1:]: s = s + drawTower( tower, topDown=boxInTopDownOrder ) return s def drawTower( tower, topDown=0 ): s = [] s.append( towerBox( tower ) ) #towerList = tower towerList = [] for i in range(1,len(tower),MaxBoxNumberPerColumn): tmp = tower[i:] if len(tmp)>MaxBoxNumberPerColumn: tmp = tower[i:i+MaxBoxNumberPerColumn] if topDown: tmp.reverse() towerList = towerList + tmp for boxText in towerList: s.append( boxBox( boxText ) ) return s if __name__ == '__main__': #get filename from user if len(filePath)==0: filePath = string.replace( os.path.abspath('') ,'\\','\\\\') +'\\\\' if len(excelFilename)==0: files = [] for file in os.listdir( filePath ): if file[-4:] in allowedFileTypeList and file[0]!='~': files.append(file) files.append('QUIT') i = 0 for file in files: print "%2d: %s" % (i, file) i = i + 1 while 1: try: j = string.atoi(raw_input('Select File: ')) excelFilename = files[j] break except: pass if excelFilename=='QUIT': sys.exit(0) print 'Processing', excelFilename, '...' filename = os.path.abspath('')+'\\'+excelFilename[:-4]+'.xml' fileType = excelFilename[-4:] if fileType=='.xls': # convert file to xml format, window system only, will not work on mac or linux from win32com.client import Dispatch app = Dispatch("Excel.Application") app.Visible = 0 doc = app.Workbooks.Open( filePath+excelFilename ) print 'Saving File As', filename type = 46 doc.SaveAs(filename, type) doc.Close(0) del app if fileType in ['.xml','.xls']: (styleList, stack) = readXMLDoc( filename, debug=generateDebugFiles ) #print styleList.keys() if fileType=='.doc': filename = os.path.abspath('')+'\\'+excelFilename chart = readWordDocFast( filename, debug=generateDebugFiles ) stack = { '%FORWORDDOC%': chart } print stack.keys() vFileList = [] for key in stack.keys(): ChartStartX=1+5.0/8 sectionIndex = 0 towerIndex = 0 boxIndex = 0 sectionX=2 sectionY=0 sectionNextX = ChartStartX towerX=0 towerY=0 chart = stack[key] #print key #print chart s = [] for section in chart: s = s + drawSection( section ) s.append( line(sectionNextX, PageMargin, sectionNextX, PageLength-PageMargin) ) PageWidth= sectionNextX + PageMargin s.append( line(ChartStartX, HorizontalLinePosition, PageWidth-PageMargin, HorizontalLinePosition) ) tag = '-'+key if key=='%FORWORDDOC%': tag = '' visioFilename = filename[:-4]+tag+ '.vdx' print visioFilename vFileList.append(visioFilename) drawVisio( visioFilename, s, width=PageWidth, length=PageLength ) # Launch Visio and Open the file visioFilename = vFileList[0] print '\nLaunching Visio and Opening ' + visioFilename appVisio = Dispatch("Visio.Application") appVisio.Visible = 1 doc = appVisio.Documents.Open( visioFilename)