#!/usr/bin/env python
"""Doc.py
> python Doc.py -h
"""
import os, shutil, sys, time
from UserDict import UserDict
import FixPath
import MiddleKit
from MiscUtils import StringTypes
from MiscUtils.Funcs import valueForString
from WebUtils.Funcs import htmlEncode
try: # for Python < 2.3
True, False
except NameError:
True, False = 1, 0
class Doc:
sourceStyleSheetFilename = 'GenDocStyles.css' # exists in MiddleKit/Design/
destStyleSheetFilename = 'Styles.css'
otherKeys = 'isDerived Min Max Enums'.split()
def main(self, args=None):
self.progPath = os.path.join(os.getcwd(), sys.argv[0])
self.otherKeys = self.otherKeys # pick up class level value
self.parseArgs(args)
modelName = self.args.get('model', None)
if not modelName:
classesName = self.args.get('classes', None)
if not classesName:
self.usage('No model or classes file specified.')
outDir = self.args.get('outDir', os.curdir)
self.requireDir(outDir)
outDir = os.path.join(outDir, 'GeneratedDocs')
self.requireDir(outDir)
self.outDir = outDir
from MiddleKit.Core.Model import Model
if modelName:
self.model = Model(modelName, havePythonClasses=0)
else:
self.model = Model(classesFilename=classesName, havePythonClasses=0)
source = os.path.join(os.path.dirname(self.progPath), self.sourceStyleSheetFilename)
dest = os.path.join(outDir, self.destStyleSheetFilename)
shutil.copyfile(source, dest)
#self.dump()
self.generateHTML()
def generateHTML(self):
path = os.path.join(self.outDir, self.model.name()+'.html')
file = open(path, 'w')
wr = file.write
wr('''
<html>
<head>
<link rel="stylesheet" type="text/css" href="%s">
</head>
<body>
<a name=#top></a>
''' % self.destStyleSheetFilename)
wr('<div class="head1">%s Model (MiddleKit)</div>\n' % self.model.name())
wr('Generated on %s <br>\n' % time.asctime())
wr('From %s <br>\n' % self.model.filename())
wr('<br>\n')
wr('<table border="0" cellpadding="2" cellspacing="2">\n')
wr('<tr class="Class"><td class="ClassName" colspan="3">Classes</td></tr>\n')
wr('<tr class="AttrTitles"><td class=AttrTitles>In Alpha Order</td>'
'<td class="AttrTitles">In Inheritance Order</td></tr>\n')
wr('<tr><td valign=top>\n')
klasses = self.model.allKlassesInOrder()[:]
klasses.sort(lambda a, b: cmp(a.name().lower(), b.name().lower()))
for klass in klasses:
name = klass.name()
wr('<a href="#%s">%s</a><br>\n' % (name, name))
wr('<td valign="top">')
for klass in self.model.allKlassesInOrder():
if not klass.superklass():
self.writeKlassLinkAndRecurse(wr, klass)
wr('</table>\n')
for klass in self.model.allKlassesInOrder():
name = klass.name()
wr('''
<a name="%(name)s"></a>
<table class="Class" cellspacing="2" cellpadding="2">
<tr class="ClassName">
<td class="ClassName" colspan="7">
<table border="0" cellpadding="0" cellspacing="0" width="100%%">
<tr>
<td class="ClassName">%(name)s</td>
<td class="Top" align="right"><a href="#top">top</a></td>
</tr>
</table>
</td>
</tr>
''' % locals())
wr('<tr class="ClassInfo"><td class="ClassInfo" colspan="7">\n')
# ancestor classes
wr('<table border="0" cellpadding="3" cellspacing="0">\n')
if klass.get('isAbstract'):
wr('<tr><td valign="top">abstract:</td><td valign="top">yes</td></tr>\n')
wr('<tr><td valign="top">ancestors:</td><td valign="top">')
ancestor = klass.superklass()
if ancestor:
while ancestor:
name = ancestor.name()
wr(' <a href="#%s">%s</a> ' % (name, name))
ancestor = ancestor.superklass()
else:
wr('none')
wr('</td></tr>\n')
# subclasses
wr('<tr><td valign="top">subclasses:</td><td valign="top">\n')
if klass.subklasses():
for subklass in klass.subklasses():
name = subklass.name()
wr('<a href="#%s">%s</a> ' % (name, name))
else:
wr('none')
wr('</td></tr>\n')
# notes
wr('<tr> <td valign="top">notes:</td><td valign="top">')
if klass.get('Notes'):
wr(htmlEncode(klass['Notes']))
else:
wr('none')
wr('</td></tr>\n')
wr('</table>\n')
wr('''
<tr class="AttrTitles">
<td class="AttrTitles">Name</td>
<td class="AttrTitles">Type</td>
<td class="AttrTitles">IsRequired</td>
<td class="AttrTitles">Default</td>
<td class="AttrTitles">Notes</td>
</tr>
''')
for attr in klass.allAttrs():
# print attr
values = Values(attr)
if attr.klass() is klass:
values['Prefix'] = ''
else:
values['Prefix'] = 'Inh'
values['Type'] = attr.htmlForType()
wr('''
<tr class="Attr">
<td class="%(Prefix)sAttrName">%(Name)s</td>
<td class="%(Prefix)sAttr">%(Type)s</td>
<td class="%(Prefix)sAttr">%(isRequired)s</td>
<td class="%(Prefix)sAttr">%(Default)s</td>
''' % values)
notes = []
if attr.get('Notes'):
notes.append(htmlEncode(attr.get('Notes')))
for key in self.otherKeys:
if attr.get(key) is not None:
notes.append('%s = %s' % (key, attr[key]))
if notes:
notes = '<br>'.join(notes)
notes = mystr(notes)
values['Notes'] = notes
# wr('<tr class=Attr><td class=%(Prefix)sAttr> </td>'
# '<td class=%(Prefix)sAttrNotes colspan=7>%(Notes)s</td>'
# '</tr>\n' % values)
wr('<td class="%(Prefix)sAttrNotes">%(Notes)s</td>\n' % values)
else:
wr('<td class="%(Prefix)sAttrNotes"> </td>\n' % values)
wr('</tr>')
wr('</table>\n')
wr('</body></html>\n')
def writeKlassLinkAndRecurse(self, wr, klass, level=0):
wr(' '*level*4)
name = klass.name()
wr('<a href="#%s">%s</a><br>\n' % (name, name))
level += 1
for klass in klass.subklasses():
self.writeKlassLinkAndRecurse(wr, klass, level)
def dump(self):
"""Dump the class and attribute definitions in order.
This is a simple example that you could expand to generate your own
brand of HTML or some other output format.
"""
for klass in self.model.allKlassesInOrder():
print klass
for attr in klass.allAttrs():
print attr
def requireDir(self, d):
if not os.path.exists(d):
os.mkdir(d)
elif not os.path.isdir(d):
print 'Error: Output target, %s, is not a directory.' % d
def parseArgs(self, args):
self.parseGenericArgs(args)
args = self.args
if args.has_key('otherkeys'):
self.otherKeys = args['otherkeys']
if type(self.otherKeys) in StringTypes:
self.otherKeys = self.otherKeys.split()
if args.has_key('morekeys'):
moreKeys = args['morekeys']
if type(moreKeys) in StringTypes:
moreKeys = moreKeys.split()
self.otherKeys += list(moreKeys)
def parseGenericArgs(self, args):
if args is None:
args = sys.argv[1:]
values = {}
for arg in args:
parts = arg.split('=', 1)
if len(parts) == 2:
values[parts[0].lower()] = valueForString(parts[1])
else:
values[parts[0].lower()] = 1
self.args = values
def usage(self, errorMsg=None):
progName = os.path.basename(sys.argv[0])
if errorMsg:
print '%s: error: %s' % (progName, errorMsg)
print '''
Usage:
%(progName)s model=FILENAME [outDir=DIRNAME] [moreKeys="foo bar"] [otherKeys="foo bar"]
%(progName)s classes=FILENAME [outDir=DIRNAME] [moreKeys="foo bar"] [otherKeys="foo bar"]
* If outDir is not specified, then the base filename (sans extension) is
used.
* GeneratedDocs is appended to the output directory.
* The Notes column will also include the other keys: isDerived, Min, Max
and Enums.
* You can redefine those keys with the otherKeys argument.
* But more typically, you would use moreKeys to _add_ keys specific to
your model (i.e., user-defined rather than MiddleKit-defined).
''' % locals()
print
sys.exit(1)
class Values(UserDict):
def __init__(self, data):
UserDict.__init__(self, data)
if self.get('isRequired') not in (False, '0', 0, 0.0, None):
self['isRequired'] = 'required'
else:
self['isRequired'] = ''
def __getitem__(self, key):
try:
value = self.data[key]
except KeyError:
value = None
if value is None:
value = ' '
return value
def mystr(s):
"""Convert a Unicode string to a basic string."""
try:
UnicodeError
except NameError:
# older Python. irrelevant
return str(s)
try:
return str(s)
except UnicodeError:
s = s.replace(u'\u201c', '"')
s = s.replace(u'\u201d', '"')
try:
return str(s)
except UnicodeError:
parts = []
for c in s:
try:
parts.append(str(c))
except UnicodeError:
parts.append(repr(c))
return ''.join(parts)
def htmlForType(self):
return self['Type']
from MiddleKit.Core.Attr import Attr
Attr.htmlForType = htmlForType
def htmlForType(self):
return '<a href="#%s" class="ClassRef">%s</a>' % (self['Type'], self['Type'])
from MiddleKit.Core.ObjRefAttr import ObjRefAttr
ObjRefAttr.htmlForType = htmlForType
def htmlForType(self):
return 'list of <a href="#%s" class="ClassRef">%s</a>' % (self.className(), self.className())
from MiddleKit.Core.ListAttr import ListAttr
ListAttr.htmlForType = htmlForType
if __name__ == '__main__':
Doc().main(sys.argv)
|