daboide Commit
Revision 216
Date: 2005-10-30 13:34:50 -0800 (Sun, 30 Oct 2005)
Author: paul
Changed:
A trunk/wizards/AppWizardX/
A trunk/wizards/AppWizardX/AppWizard.py
A trunk/wizards/AppWizardX/spec-App.py
A trunk/wizards/AppWizardX/spec-Biz.py
A trunk/wizards/AppWizardX/spec-BizBase.py
A trunk/wizards/AppWizardX/spec-Frm.py
A trunk/wizards/AppWizardX/spec-FrmBase.py
A trunk/wizards/AppWizardX/spec-MenFileOpen.py
A trunk/wizards/AppWizardX/spec-PagBase.py
A trunk/wizards/AppWizardX/spec-biz__init__.py
A trunk/wizards/AppWizardX/spec-db__init__.py
A trunk/wizards/AppWizardX/spec-main.py
A trunk/wizards/AppWizardX/spec-ui__init__.py
D trunk/wizards/AppWizardX.py
Log:
Moved AppWizardX into its own subdir and split out the templates into their
own files, which will make it easier to maintain.
Diff:
Property changes on: trunk/wizards/AppWizardX
___________________________________________________________________
Name: svn:ignore
+ *.pyc
Copied: trunk/wizards/AppWizardX/AppWizard.py (from rev 215,
trunk/wizards/AppWizardX.py)
===================================================================
--- trunk/wizards/AppWizardX.py 2005-10-30 18:18:43 UTC (rev 215)
+++ trunk/wizards/AppWizardX/AppWizard.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,950 @@
+#!/usr/bin/env python
+import os, time
+import dabo
+from dabo.dLocalize import _
+import dabo.dEvents as dEvents
+import dabo.dConstants as k
+dabo.ui.loadUI("wx")
+from dabo.ui.dialogs.WizardPage import WizardPage
+from dabo.ui.dialogs.Wizard import Wizard
+from dabo.ui import dLabel
+from dabo.lib.connParser import createXML
+
+
+class HSizer(dabo.ui.dSizer):
+ def __init__(self, *args, **kwargs):
+ super(HSizer, self).__init__("h", *args, **kwargs)
+
+
+class VSizer(dabo.ui.dSizer):
+ def __init__(self, *args, **kwargs):
+ super(VSizer, self).__init__("v", *args, **kwargs)
+
+
+class PageIntro(WizardPage):
+ def __init__(self, parent, title="Introduction"):
+ super(PageIntro, self).__init__(parent=parent, Title=title)
+
+ def createBody(self):
+ txt = """Use this wizard to quickly create an application
+for your database. Point the wizard at a database;
+specify a target directory; the wizard will create
+a full-fledged Dabo application with forms for each
+table in the database that allow you to run queries
+and edit the results.
+
+You can then edit the resulting application to fit
+your exact requirements. There is a tool included
+called 'FieldSpecEditor.py' that allows you to edit
+your application settings visually.
+
+Right now Dabo supports the MySQL, Firebird,
+PostgreSQL and SQLite databases. In time the
+number of supported databases will grow.
+
+Press 'Next' to enter database parameters."""
+ lbl = dLabel(self, Caption=txt)
+ self.Sizer.append(lbl)
+
+
+class PageDatabase(WizardPage):
+ def __init__(self, parent, title="Database Parameters"):
+ super(PageDatabase, self).__init__(parent=parent, Title=title)
+
+
+ def createBody(self):
+ sz = self.Sizer
+ lbl = dLabel(self, Caption="Enter the parameters here, and then
click 'Next'.")
+ sz.append(lbl)
+
+ lbl = dLabel(self, Caption="Profile:")
+
+ self.dbDefaults = {}
+ self.dbDefaults["MySQL"] = {
+ "DbType" : "MySQL",
+ "Host" : "dabodev.com",
+ "Database" : "webtest",
+ "User" : "webuser",
+ "Password" : "foxrocks",
+ "Port" : "3306" }
+ self.dbDefaults["Firebird"] = {
+ "DbType" : "Firebird",
+ "Host" : "dabodev.com",
+ "Database" : "webtest",
+ "User" : "webuser",
+ "Password" : "foxrox",
+ "Port" : "3050" }
+ self.dbDefaults["PostgreSQL"] = {
+ "DbType" : "PostgreSQL",
+ "Host" : "dabodev.com",
+ "Database" : "webtest",
+ "User" : "webuser",
+ "Password" : "foxrox",
+ "Port" : "5432" }
+
+ self.dbDefaults["SQLite"] = {
+ "DbType" : "SQLite",
+ "Database" : "webtest.sqlite"}
+
+ # List of all fields to create for the user to select
+ self.fieldNames = ("DbType", "Host", "Database", "User",
"Password", "Port")
+
+ # Now go through the profiles that the user may have saved in
the
+ # user settings file:
+ app = self.Application
+ userProfiles = app.getUserSettingKeys("appWizard.dbDefaults.")
+ dbDefaultKeys = self.dbDefaults.keys()
+ dbDefaultMap = {}
+ for key in dbDefaultKeys:
+ dbDefaultMap[key.lower()] = key
+
+ ## Default to MySQL first:
+ defaultProfileName = "MySQL"
+ defaultUserProfileName = None
+
+ for profile in userProfiles:
+ userDict = {}
+ for field in (self.fieldNames):
+ name = "appWizard.dbDefaults.%s.%s" % (profile,
field)
+ val = app.getUserSetting(name)
+ if val is None:
+ val = ""
+ userDict[field] = val
+ if profile in dbDefaultMap.keys():
+ profile = dbDefaultMap[profile]
+ self.dbDefaults[profile] = userDict
+
+ # Override the default with the last user profile:
+ defaultUserProfileName=profile
+
+ # Set up the dropdown list based on the keys in the dbDefaults
dict.
+ choiceParms = {"Choices": self.dbDefaults.keys(),
+ "Name": "dbChoice",
+ "ValueMode": "string"}
+ self.dbChoice = dabo.ui.dDropdownList(self, Name="dbChoice")
+ self.dbChoice.ValueMode = "string"
+ self.dbChoice.Choices = self.dbDefaults.keys()
+ if defaultUserProfileName is not None:
+ self.dbChoice.Value = defaultUserProfileName
+ else:
+ self.dbChoice.Value = defaultProfileName
+ self.dbChoice.bindEvent(dabo.dEvents.ValueChanged,
self.onDbChoice)
+
+ cmd = self.addObject(dabo.ui.dButton,
+ Caption="New Profile...", Name="cmdNewProfile")
+ cmd.bindEvent(dabo.dEvents.Hit, self.onNewProfile)
+
+ gs = dabo.ui.dGridSizer()
+ gs.MaxCols = 2
+ gs.setColExpand(True, 1)
+ gs.append(lbl)
+ hs = HSizer()
+ hs.append(self.dbChoice, 1)
+ hs.appendSpacer(8)
+ hs.append(cmd, 0)
+ gs.append(hs, "x")
+
+ for field in ("DbType", "Host", "Database", "User", "Password",
"Port"):
+ lbl = dLabel(self, Name=("lbl%s" % field), Width=75,
Caption=("%s:" % field) )
+ pw = (field.lower() == "password")
+ obj = dabo.ui.dTextBox(self, PasswordEntry=pw,
Name=("txt%s" % field) )
+ obj.bindEvent(dabo.dEvents.ValueChanged,
self.onParmValueChanged)
+
+ gs.append(lbl)
+ # Add a file search button. It will be hidden for all
+ # non-file-based backends.
+ if field == "Database":
+ self.btnSrch = dabo.ui.dButton(self,
Caption="...")
+ self.btnSrch.Width = (self.btnSrch.Height * 2)
+ self.btnSrch.bindEvent(dabo.dEvents.Hit,
self.onDbSearch)
+ hs = self.szDB = dabo.ui.dSizer("H")
+ hs.append1x(obj)
+ hs.append(self.btnSrch, border=10,
borderFlags="left")
+ gs.append(hs, "x")
+ else:
+ gs.append(obj, "x")
+ sz.append(gs, 1, "x")
+ self.onDbChoice()
+
+
+ def onDbSearch(self, evt):
+ """Select a file for the database"""
+ pth = dabo.ui.getFile(message="Select the database")
+ if pth:
+ self.txtDatabase.Value = pth
+ self.refresh()
+
+
+ def onParmValueChanged(self, evt):
+ # write the new value back to the user settings table.
+ object = evt.EventObject
+ app = object.Application
+ field = object.Name[3:]
+ name = "appWizard.dbDefaults.%s.%s" % (self.dbChoice.Value,
field)
+ app.setUserSetting(name, object.Value)
+ self.dbDefaults[self.dbChoice.Value][field] = object.Value
+
+
+ def onNewProfile(self, evt):
+ base = "New Profile"
+ i = 1
+ while True:
+ default = "%s %s" % (base, i)
+ if default in self.dbChoice.Choices:
+ i += 1
+ else:
+ break
+
+ name = dabo.ui.getString("Please enter a name for the profile",
defaultValue=default)
+ if name is not None:
+ self.dbDefaults[name] = {
+ "DbType" : "",
+ "Host" : "",
+ "Database" : "",
+ "User" : "",
+ "Password" : "",
+ "Port" : "" }
+ dbChoice = self.dbChoice
+ dbChoice.Choices = self.dbDefaults.keys()
+ dbChoice.Value = name
+ self.txtDbType.Value = "MySQL"
+ self.txtPort.Value = "3306"
+ self.txtDbType.SetFocus()
+
+
+ def onDbChoice(self, evt=None):
+ self.Form.dbType = self.dbChoice.Value
+ dbType = self.Form.dbType
+ dbdefs = self.dbDefaults[dbType]
+ for fld in self.fieldNames:
+ if dbdefs.has_key(fld):
+ val = dbdefs[fld]
+ exec("self.txt%s.Value = '%s' " % (fld, val) )
+ exec("self.txt%s.Visible = True " % fld )
+ else:
+ # Not a field used for this db type
+ exec("self.txt%s.Value = '' " % fld )
+ exec("self.txt%s.Visible = False " % fld )
+ # Enable the file search button if this is a file-based backend
+ if dbType in ("SQLite",):
+ self.szDB.showItem(self.btnSrch)
+ else:
+ self.szDB.hideItem(self.btnSrch)
+
+
+ def onLeavePage(self, direction):
+ if direction == "forward":
+ if len(self.Form.tableDict) > 0:
+ if not dabo.ui.areYouSure("Overwrite the
current table information?"):
+ return True
+
+ # Set the wizard's connect info based on the user input:
+ ci = self.Form.connectInfo
+ be = self.txtDbType.Value
+ try:
+ ci.DbType = be
+ except ValueError:
+ dabo.ui.stop("The database type '%s' is
invalid. "
+ "Please reenter and try again."
% be)
+ self.txtDbType.SetFocus()
+ return False
+ ci.Host = self.txtHost.Value
+ ci.Database = self.txtDatabase.Value
+ ci.User = self.txtUser.Value
+ ci.Password = ci.encrypt(self.txtPassword.Value)
+ try:
+ ci.Port = int(self.txtPort.Value)
+ except ValueError:
+ ci.Port = None
+ # Try to get a connection::
+ try:
+ conn = dabo.db.dConnection(ci)
+ cursor =
conn.getDaboCursor(ci.getDictCursorClass())
+ cursor.BackendObject = ci.getBackendObject()
+ tables = cursor.getTables()
+ except Exception, e:
+ dabo.ui.stop("Could not connect to the database
server. "
+ "Please check your parameters
and try again.")
+ return False
+ self.Form.tableDict = {}
+ tableOrder = 0
+ for table in tables:
+ # Firebird databases have system tables with
'$' in the name
+ if table.find("$") > -1:
+ continue
+ tableDict = {}
+ count = cursor.getTableRecordCount(table)
+ tableDict["name"] = table
+ tableDict["recordCount"] = count
+ tableDict["order"] = tableOrder
+ fields = cursor.getFields(table)
+ tableDict["fields"] = {}
+ fieldOrder = 0
+ for field in fields:
+ fieldName = field[0]
+ tableDict["fields"][fieldName] = {}
+ tableDict["fields"][fieldName]["name"]
= fieldName
+ tableDict["fields"][fieldName]["type"]
= field[1]
+ tableDict["fields"][fieldName]["order"]
= fieldOrder
+ tableDict["fields"][fieldName]["pk"] =
field[2]
+ fieldOrder += 1
+
+ self.Form.tableDict[table] = tableDict
+ tableOrder += 1
+ return True
+
+
+class PageRelations(WizardPage):
+ def __init__(self, parent, title="Table Relationships"):
+ super(PageRelations, self).__init__(parent=parent, Title=title)
+
+
+ def createBody(self):
+ txt = """If there are any relationships among your
+selected tables, specify them here."""
+ lbl = dLabel(self, Caption=txt)
+ self.Sizer.append(lbl)
+
+ self.selTabs = self.Form.selectedTables
+ hsz = HSizer()
+ szp = self.szrParent = VSizer()
+ szc = self.szrChild = VSizer()
+ szp.Border = szc.Border = 5
+ szp.BorderTop = szc.BorderTop = True
+
+ tx = dLabel(self, Caption="Parent", FontBold=True)
+ szp.append(tx, "expand", halign="centre")
+ tx2 = dLabel(self, Caption="Child", FontBold=True)
+ szc.append(tx2, "expand", halign="centre")
+
+ self.ddParent = dabo.ui.dDropdownList(self, Name="ddParent")
+ self.ddChild = dabo.ui.dDropdownList(self, Name="ddChild")
+ self.ddParent.bindEvent(dabo.dEvents.Hit,
self.onParentSelection)
+ self.ddChild.bindEvent(dabo.dEvents.Hit, self.onChildSelection)
+ szp.append(self.ddParent, "expand")
+ szc.append(self.ddChild, "expand")
+ self.ddPrFields = dabo.ui.dDropdownList(self, Name="ddPrFields")
+ self.ddChFields = dabo.ui.dDropdownList(self, Name="ddChFields")
+ szp.append(self.ddPrFields, "expand")
+ szc.append(self.ddChFields, "expand")
+
+ hsz.append(szp, 1, "expand")
+ hsz.append(szc, 1, "expand")
+ self.Sizer.append(hsz, 0, "expand", borderFlags="All",
border=10)
+
+ btnSet = dabo.ui.dButton(self, Caption="Set Relation")
+ btnSet.bindEvent(dabo.dEvents.Hit, self.onSetRelation)
+ self.Sizer.append(btnSet, 0, alignment="centre", border=5)
+
+ # Add the list of current relationships
+ self.lstRelations = dabo.ui.dListBox(self)
+ self.displayRelations()
+ self.Sizer.append(self.lstRelations, 1, "expand",
borderFlags="top", border=5)
+ btnRemove = dabo.ui.dButton(self, Caption="Remove Relation")
+ btnRemove.bindEvent(dabo.dEvents.Hit, self.onRemoveRelation)
+ self.Sizer.append(btnRemove, 0, alignment="centre",
borderFlags="top", border=5)
+
+
+ def onEnterPage(self, direction):
+ currSel = self.Form.selectedTables
+ if self.selTabs != currSel:
+ self.selTabs = currSel
+ # Table selection has changed. Update the child
dropdowns
+ self.populateDropDowns()
+
+
+ def onRelTypeChange(self, evt):
+ pass
+# if ( self.radRelType.Value == "Many-To-Many" ):
+# self.pgfType.SetSelection(1)
+# else:
+# self.pgfType.SetSelection(0)
+
+
+ def onT1Selection(self, evt): pass
+ def onT2Selection(self, evt): pass
+ def onAllocSelection(self, evt): pass
+
+
+ def populateDropDowns(self):
+ currPar = self.ddParent.Value
+ currChld = self.ddChild.Value
+
+ self.ddParent.removeAll()
+ self.ddChild.removeAll()
+ for tb in self.Form.selectedTables:
+ self.ddParent.appendItem(tb)
+ try:
+ self.ddParent.Value = currPar
+ except: pass
+ self.ddParent.PositionValue = max(0,
self.ddParent.PositionValue)
+ currPar = self.ddParent.Value
+
+ for tb in self.Form.selectedTables:
+ if tb != currPar:
+ self.ddChild.appendItem(tb)
+ try:
+ self.ddChild.Value = currChld
+ except: pass
+ self.ddChild.PositionValue = max(0, self.ddChild.PositionValue)
+
+# # # # # Not used without the M:M relations page
+# self.ddT1.Choices = self.Form.selectedTables
+# currT1 = self.ddT1.Value
+# currT2 = self.ddT2.Value
+# self.ddT2.Choices = self.Form.selectedTables
+# self.ddAlloc.Choices = [tb for tb in self.Form.selectedTables
+# if tb not in (currT1, currT2)]
+ # Now populate the field selectors
+ self.populateFields()
+
+
+ def populateFields(self):
+ currPar = self.ddParent.Value
+ currChld = self.ddChild.Value
+ td = self.Form.tableDict
+
+ parentFields = td[currPar]["fields"]
+ childFields = td[currChld]["fields"]
+
+ # decorate, sort, undecorate to get the fields in the same order
+ # as they are in the database:
+ spkeys, sckeys = [], []
+ for k,v in parentFields.items():
+ spkeys.append((v["order"], k))
+ for k,v in childFields.items():
+ sckeys.append((v["order"], k))
+
+ spkeys.sort()
+ sckeys.sort()
+
+ pkeys = [k[1] for k in spkeys]
+ ckeys = [k[1] for k in sckeys]
+
+ self.ddPrFields.Choices = pkeys
+ self.ddChFields.Choices = ckeys
+
+
+ def displayRelations(self):
+ trk = self.Form.tableRelationKeys
+ rels = []
+ rels = ["%s - %s.%s : %s.%s" % (tb[0], tb[1], trk[tb][0],
tb[2], trk[tb][1])
+ for tb in trk.keys() ]
+ self.lstRelations.Choices = rels
+
+
+ def onParentSelection(self, evt):
+ """ Parent selected; disable the matching entry in the child
dropdown"""
+ self.populateDropDowns()
+
+ def onChildSelection(self, evt):
+ self.populateFields()
+
+ def onSetRelation(self, evt):
+# if self.pgfType.GetSelection() == 0:
+ # One-To-Many
+ parent = self.ddParent.Value
+ chld = self.ddChild.Value
+ pFld = self.ddPrFields.Value
+ cFld = self.ddChFields.Value
+ pExp = "%s.%s" % (parent, pFld)
+ cExp = "%s.%s" % (chld, cFld)
+
+ relaTuple = ("1M", parent, chld)
+ keyTuple = (pFld, cFld)
+ if relaTuple in self.Form.tableRelations:
+ dabo.ui.info("The relationship between '%s' and
'%s' is already defined"
+ % relaTuple[1:])
+ else:
+ self.Form.tableRelations.append(relaTuple)
+ self.Form.tableRelationKeys[relaTuple] =
keyTuple
+ self.displayRelations()
+# else:
+# dabo.ui.stop("Sorry, not implemented yet", "Under
Construction")
+
+
+ def onRemoveRelation(self, evt):
+ selct = self.lstRelations.Value
+ if not selct:
+ return
+ typ,defs = selct.split(" - ")
+ sel = defs.strip().split(":")
+ tblTuple = ( typ.strip(), sel[0].strip().split(".")[0],
sel[1].strip().split(".")[0] )
+ self.Form.tableRelations.remove( tblTuple )
+ del self.Form.tableRelationKeys[tblTuple]
+ self.displayRelations()
+
+
+
+class PageTableSelection(WizardPage):
+ def __init__(self, parent, title="Table Selection"):
+ super(PageTableSelection, self).__init__(parent=parent,
Title=title)
+
+
+ def createBody(self):
+ self.chks = []
+ txt = """The connection to the database was successful.
+The following tables were found for that database.
+Please check all tables you want included in
+your application."""
+ lbl = dLabel(self, Caption=txt)
+ self.Sizer.append(lbl)
+
+
+ def onEnterPage(self, direction):
+ if direction == "forward":
+ tbls = [t for t in self.Form.getTables()]
+ tbls.sort()
+ if self.chks:
+ for i in range(len(self.chks)-1, -1, -1):
+ self.chks[i].release()
+ self.chks = []
+ try:
+ pn = self.chkPanel
+ except:
+ # Not created yet
+ self.chkPanel = dabo.ui.dScrollPanel(self)
+ self.chkPanel.SetSizer(VSizer())
+ self.Sizer.append(self.chkPanel, 1, "expand",
+ border=10, borderFlags=("top",
"left", "right"))
+ self.chkPanel.Sizer.Spacing = 4
+ for tb in tbls:
+ chk = dabo.ui.dCheckBox(self.chkPanel)
+ chk.Caption = tb
+ chk.Value = False
+ self.chkPanel.Sizer.append(chk)
+ self.chks.append(chk)
+ self.Sizer.Layout()
+
+ def getSelection(self):
+ return [chk.Caption for chk in self.chks if chk.Value]
+
+ def onLeavePage(self, direction):
+ if direction == "forward":
+ self.Form.selectedTables = self.getSelection()
+ if not self.Form.selectedTables:
+ dabo.ui.stop("No tables were selected. " +
+ "Please select the tables you
want to include in your application",
+ title="No Tables Selected")
+ return False
+ return True
+
+ def nextPage(self):
+ """ If they have selected only one table, skip the relations
page """
+ ## pkm: I'm not thinking about relations yet, so skip relations
page
+ ## unconditionally for now.
+ if True or len(self.getSelection()) == 1:
+ return 2
+ else:
+ return 1
+
+
+class PageTargetDirectory(WizardPage):
+ def __init__(self, parent, title="Output Directory"):
+ super(PageTargetDirectory, self).__init__(parent=parent,
Title=title)
+
+
+ def createBody(self):
+ txt = """Enter the directory where you wish to place your
+new application.
+
+You can always move the directory later."""
+ lbl = dLabel(self, Caption=txt)
+ self.Sizer.append(lbl)
+
+ hs = HSizer()
+
+ self.txtDir = dabo.ui.dTextBox(self)
+ self.txtDir.FontSize=10
+ self.txtDir.Value = ""
+ hs.Add(self.txtDir, 1)
+
+ self.cmdPick = dabo.ui.dButton(self)
+ self.cmdPick.Caption = "..."
+ self.cmdPick.Width = 30
+ self.cmdPick.Height = self.txtDir.Height
+ self.cmdPick.bindEvent(dEvents.Hit, self.onPick)
+ hs.Add(self.cmdPick, 0)
+ self.Sizer.append(hs, 1, "x")
+
+
+ def prevPage(self):
+ """ If they have selected only one table, skip the relations
page """
+ ## pkm: I'm not thinking about relations yet, so skip relations
page
+ ## unconditionally for now.
+ if True or len(self.Form.selectedTables) == 1:
+ return -2
+ else:
+ return -1
+
+
+ def onPick(self, evt):
+ dlg = dabo.ui.dFolderDialog(self, defaultPath =
self.txtDir.Value)
+ if dlg.show() == k.DLG_OK:
+ self.txtDir.Value = dlg.Path
+ dlg.release()
+
+
+ def onEnterPage(self, direction):
+ if direction == "forward":
+ if self.txtDir.Value == "":
+ self.txtDir.Value = os.path.abspath(
os.path.join( os.getcwd(),
+
self.Form.connectInfo.Database.split(os.path.sep)[-1]) )
+
+
+ def onLeavePage(self, direction):
+ if direction == "forward":
+ directory = self.txtDir.Value
+ if not os.path.exists(directory):
+ msg = "The target directory %s does not exist.
Do you want to create it now?" % directory
+ if dabo.ui.areYouSure(msg):
+ os.mkdir(directory)
+ else:
+ return False
+ self.Form.outputDirectory = directory
+ return True
+
+
+class PageGo(WizardPage):
+ def __init__(self, parent, title="Create Application"):
+ super(PageGo, self).__init__(parent=parent, Title=title)
+ txt = """Press 'Finish' to create your application, or
+'Back' to edit any information."""
+ lbl = dLabel(self, Caption=txt)
+ self.Sizer.append(lbl)
+
+
+ def onLeavePage(self, direction):
+ if direction == "forward":
+ if not self.Form.createApp():
+ return False
+ else:
+ dabo.ui.info("""
+Your application has been created. Please remember that the
+intent of this is to give you, the database developer, the
+ability to browse and edit all records in all tables of your
+database.
+
+As it stands now, there are no limits to what you can do with
+the data: add records, delete records, modify records. In other
+words, the application you just created IS DANGEROUS. Treat it
+as a demo, and if you do use it, use it with care and consider
+adding business rules and organizing your screens into
+master/childview parent/child relationships.
+
+We take no responsibility if your database gets hopelessly
+mangled, but at the same time I hope you find this wizard useful.
+
+To see your app in action, navigate to the target directory and
+type 'python main.py' at the commandline.
+
+pkm
+""")
+ return True
+
+
+class AppWizard(Wizard):
+ def __init__(self, parent=None, *args, **kwargs):
+ super(AppWizard, self).__init__(parent=parent, *args, **kwargs)
+
+ self.Caption = "Dabo Application Wizard"
+ self.Image = "daboIcon128"
+ self.Size = (520, 560)
+
+ self.tableDict = {}
+ self.selectedTables = []
+ self.tableRelations = []
+ self.tableRelationKeys = {}
+ self.outputDirectory = ""
+ self.connectInfo = dabo.db.dConnectInfo()
+ self.dbType = "MySQL" # default to MySQL
+
+ pgs = [PageIntro, PageDatabase, PageTableSelection,
PageRelations,
+ PageTargetDirectory, PageGo]
+ self.append(pgs)
+ self.start()
+
+
+ def getTables(self):
+ return self.tableDict.keys()
+
+ def getTableInfo(self):
+ ti = ""
+ td = self.tableDict
+ tables = td.keys()
+ tables.sort()
+ for table in tables:
+ count = td[table]["recordCount"]
+ ti = "".join((ti, "\n%s - %s record%s.\n" % (table,
count, count==1 and "" or "s")))
+
+ fields = td[table]["fields"].keys()
+ fields.sort()
+ for field in fields:
+ ti = "".join((ti, " %s (%s) %s\n" %
(td[table]["fields"][field]["name"],
+
td[table]["fields"][field]["type"],
+
td[table]["fields"][field]["pk"] == True and "PK" or "")))
+ return ti.strip()
+
+
+ def createApp(self):
+ directory = self.outputDirectory
+
+ if os.path.exists(directory):
+ td = self.tableDict
+ rd = self.tableRelationKeys
+ selTb = self.selectedTables
+ ci = self.connectInfo
+ oldDir = os.getcwd()
+ os.chdir(directory)
+ appName = os.path.split(self.outputDirectory)[-1]
+
+ ## Create the directory structure:
+ for d in ("biz", "db", "ui", "resources", "reports"):
+ if not os.path.exists(d):
+ os.mkdir(d)
+
+ ## Create the main script:
+ f = open("./%s.py" % appName, "w")
+ f.write(self.getMain("[EMAIL PROTECTED]" % (ci.User,
ci.Host), selTb[0]))
+ f.close()
+
+ ## Create App.py:
+ f = open("./App.py", "w")
+ f.write(self.getApp(appName))
+ f.close()
+
+ # Db module:
+ os.chdir("./db")
+ open("./__init__.py",
"w").write(self.getModuleInit_db())
+ open("./default.cnxml",
"w").write(self.getDbConnXML(ci))
+
+
+ # Biz module:
+ os.chdir("../biz")
+ open("./__init__.py",
"w").write(self.getModuleInit_biz())
+
+ # Write the base bizobj:
+ open("./Base.py", "w").write(self.getBaseBizobj())
+
+ # Write each bizobj:
+ for table in selTb:
+ f = open("./%s.py" % table.title(), "w")
+ f.write(self.getBizobj(td, table))
+ f.close()
+
+ # UI module:
+ os.chdir("../ui")
+ open("./__init__.py",
"w").write(self.getModuleInit_ui())
+
+ ## fieldSpecs, relationSpecs:
+ f = open("./fieldSpecs.fsxml", "w")
+ f.write(fieldSpecs(td, selTb))
+ f.close()
+
+ f = open("./relationSpecs.rsxml", "w")
+ f.write(relationSpecs(rd))
+ f.close()
+
+ ## file|open menu:
+ f = open("./MenFileOpen.py", "w")
+ f.write(self.getFileOpenMenu(selTb))
+ f.close()
+
+
+ ## base page:
+ open("./PagBase.py", "w").write(self.getPagBase())
+
+ ## base form:
+ open("./FrmBase.py", "w").write(self.getFrmBase())
+
+ # Write each form:
+ for table in selTb:
+ f = open("./Frm%s.py" % table.title(), "w")
+ f.write(self.getForm(table))
+ f.close()
+
+ return True
+
+ else:
+ dabo.ui.stop("The target directory does not exist.
Cannot continue.")
+ return False
+
+
+ def getFileOpenMenu(self, tables):
+ tables.sort()
+ forms = ""
+ for table in tables:
+ forms = "".join((forms, """("%s",
app.ui.Frm%s),\n\t\t\t\t""" %
+ (table.title(), table.title())))
+ forms = "".join((forms, """("-", None),\n\t\t\t\t"""))
+
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-MenFileOpen.py")).read() % locals()
+
+
+ def getForm(self, table):
+ tableName = table.title()
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-Frm.py")).read() % locals()
+
+
+ def getMain(self, dbConnectionDef, table):
+ tableName = table.title()
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-main.py")).read() % locals()
+
+
+ def getModuleInit_db(self):
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-db__init__.py")).read() % locals()
+
+
+ def getModuleInit_biz(self):
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-biz__init__.py")).read() % locals()
+
+
+ def getModuleInit_ui(self):
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-ui__init__.py")).read() % locals()
+
+
+ def getApp(self, appName):
+ appName = appName.title()
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-App.py")).read() % locals()
+
+
+ def getDbConnXML(self, ci):
+ cxnDict = {"dbtype" : ci.DbType, "host" : ci.Host, "database" :
ci.Database,
+ "user" : ci.User, "password" : ci.Password, "port" :
ci.Port}
+ return createXML(cxnDict)
+
+
+ def getBaseBizobj(self):
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-BizBase.py")).read() % locals()
+
+
+ def getBizobj(self, tableDict, table):
+ tableName = table.title()
+ tbInfo = tableDict[table]
+
+ # find the pk field, if any:
+ pkField = None
+ for field in tableDict[table]["fields"].keys():
+ if tableDict[table]["fields"][field]["pk"] == True:
+ pkField = "\"" + field + "\""
+ break
+
+ flds = tbInfo["fields"]
+
+ sortedFieldNames = []
+ for fld in flds.keys():
+ order = flds[fld]["order"]
+ sortedFieldNames.append((order, fld))
+ sortedFieldNames.sort()
+
+ fields = ""
+
+ for field in sortedFieldNames:
+ fields += """self.addField("%s.%s as %s")\n\t\t""" %
(table, field[1], field[1])
+
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-Biz.py")).read() % locals()
+
+
+ def getFrmBase(self):
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-FrmBase.py")).read() % locals()
+
+
+ def getPagBase(self):
+ return open(os.path.join(self.Application.HomeDirectory,
+ "spec-PagBase.py")).read() % locals()
+
+
+def relationSpecs(relaDict):
+ ret = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<daboRelationSpecs>
+ <relations>
+%s </relations>
+</daboRelationSpecs>
+"""
+ relaTemplate1M = """ <relation name="%s:%s"
relationType="1M"
+ source="%s" target="%s" sourceField="%s"
targetField="%s" />
+"""
+ relaTemplateMM = """ <relation name="%s:%s"
relationType="MM"
+ source="%s" sourceField="%s"
+ alloc="%s" allocSourceField="%s" allocTargetField="%s"
+ target="%s" targetField="%s" />
+"""
+ relaXML = ""
+ for key in relaDict.keys():
+ if key[0] == "1M":
+ typ, relaParent, relaChild = key
+ parentFld, childFld = relaDict[key]
+ relaData = (relaParent, relaChild, relaParent,
relaChild, parentFld, childFld)
+ relaXML += relaTemplate1M % relaData
+ return ret % relaXML
+
+
+
+def fieldSpecs(tbDict, tbls):
+ ret = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<daboAppSpecs>
+%s
+</daboAppSpecs>
+"""
+
+ tblTemplate = """ <table name="%s">
+ <fields>
+%s </fields>
+ </table>
+"""
+
+ fldTemplate = """ <field name="%s"
type="%s" caption="%s"
+ searchInclude="%s" searchOrder="%s"
wordSearch="%s"
+ listInclude="%s" listColWidth="%s"
listOrder="%s"
+ editInclude="%s" editReadOnly="%s"
editOrder="%s" />
+
+"""
+
+ typeConversion = {"I" : "int", "C" : "char", "M" : "memo", "D" :
"date",
+ "N" : "float", "F" : "float", "?" : "char", "L" :
"bool", "B" : "bool", "T" : "datetime"}
+ tableXML = ""
+ tables = tbDict.keys()
+ tables.sort()
+ for tb in tables:
+ if tb in tbls:
+ tbInfo = tbDict[tb]
+ flds = tbInfo["fields"]
+ fieldXML = ""
+ fldOrd = 0
+
+ sortedFieldNames = []
+ for fld in flds.keys():
+ order = flds[fld]["order"]
+ sortedFieldNames.append((order, fld))
+ sortedFieldNames.sort()
+
+ for tup in sortedFieldNames:
+ fld = tup[1]
+ fldInfo = flds[fld]
+ fldType = typeConversion[fldInfo["type"]]
+ fldData = (fld, fldType, fld, 1, fldOrd, 0, 1,
-1, fldOrd, 1, 0, fldOrd)
+ fieldXML += fldTemplate % fldData
+ fldOrd += 10
+
+ tableXML += tblTemplate % (tb, fieldXML)
+ return ret % tableXML
+
+
+
+if __name__ == "__main__":
+ app = dabo.dApp()
+ app.setAppInfo("appName", "Dabo Application Wizard")
+ app.setAppInfo("appName", "AppWizard")
+ app.MainFormClass = AppWizard
+ app.setup()
+ app.start()
Added: trunk/wizards/AppWizardX/spec-App.py
===================================================================
--- trunk/wizards/AppWizardX/spec-App.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-App.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,17 @@
+import dabo
+
+
+class App(dabo.dApp):
+
+ def initProperties(self):
+ self.MainFormClass = None
+
+ ## The following information can be used in various places in
your app:
+ self.setAppInfo("appName", "%(appName)s")
+ self.setAppInfo("companyName", "Your company name")
+ self.setAppInfo("companyAddress1", "Your company address")
+ self.setAppInfo("companyAddress2", "Your company CSZ")
+ self.setAppInfo("companyPhone", "Your company phone")
+ self.setAppInfo("companyEmail", "Your company email")
+ self.setAppInfo("companyUrl", "Your company url")
+
Property changes on: trunk/wizards/AppWizardX/spec-App.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-Biz.py
===================================================================
--- trunk/wizards/AppWizardX/spec-Biz.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-Biz.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,26 @@
+from Base import Base
+
+
+class %(tableName)s(Base):
+
+ def initProperties(self):
+ %(tableName)s.doDefault()
+ self.Caption = "%(tableName)s"
+ self.DataSource = "%(table)s"
+ self.KeyField = %(pkField)s
+ # Set the default values for new records added:
+ self.DefaultValues = {}
+
+
+ def afterInit(self):
+ %(tableName)s.doDefault()
+
+
+ def setBaseSQL(self):
+ # Set up the base SQL (the fields clause, the from clause,
etc.) The
+ # UI refresh() will probably modify the where clause and maybe
the
+ # limit clause, depending on what the runtime user chooses in
the
+ # select page.
+ self.addFrom("%(table)s")
+ self.setLimitClause("500")
+ %(fields)s
Property changes on: trunk/wizards/AppWizardX/spec-Biz.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-BizBase.py
===================================================================
--- trunk/wizards/AppWizardX/spec-BizBase.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-BizBase.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,8 @@
+import dabo.lib.datanav as datanav
+
+class Base(datanav.Bizobj):
+
+ def afterInit(self):
+ Base.doDefault()
+ self.setBaseSQL()
+
Property changes on: trunk/wizards/AppWizardX/spec-BizBase.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-Frm.py
===================================================================
--- trunk/wizards/AppWizardX/spec-Frm.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-Frm.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,37 @@
+import os
+from FrmBase import FrmBase
+
+
+class Frm%(tableName)s(FrmBase):
+
+ def initProperties(self):
+ Frm%(tableName)s.doDefault()
+ self.NameBase = "frm%(tableName)s"
+ self.Caption = "%(tableName)s"
+
+
+ def addEditPage(self, dataSource, title, pageClass=None):
+ # If you have an overridden edit page, stick it in here. eg:
+ #pageClass = self.Application.ui.PagEditClient
+ Frm%(tableName)s.doDefault(dataSource, title, pageClass)
+
+
+ def afterInit(self):
+ Frm%(tableName)s.doDefault()
+
+ # Instantiate the bizobj:
+ app = self.Application
+ primaryBizobj = app.biz.%(tableName)s(app.dbConnection)
+
+ # Register it with dForm:
+ self.addBizobj(primaryBizobj)
+
+ # Set up the field information for the form
+ fsXML = os.path.join(app.HomeDirectory, "ui",
"fieldSpecs.fsxml")
+ rsXML = os.path.join(app.HomeDirectory, "ui",
"relationSpecs.rsxml")
+ self.setFieldSpecs(fsXML, "%(table)s")
+ self.setRelationSpecs(rsXML, app.biz)
+
+ # The form now knows what it needs to create itself. Do it!
+ self.creation()
+
Property changes on: trunk/wizards/AppWizardX/spec-Frm.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-FrmBase.py
===================================================================
--- trunk/wizards/AppWizardX/spec-FrmBase.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-FrmBase.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,19 @@
+import dabo.ui
+import dabo.lib.datanav as datanav
+
+
+class FrmBase(datanav.Form):
+
+ def initProperties(self):
+ self.RequeryOnLoad = False
+
+
+ def afterSetMenuBar(self):
+ self.fillFileOpenMenu()
+
+
+ def fillFileOpenMenu(self):
+ app = self.Application
+ fileMenu = self.MenuBar.getMenu("File")
+ fileMenu.prependMenu(app.ui.MenFileOpen(fileMenu))
+
Property changes on: trunk/wizards/AppWizardX/spec-FrmBase.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-MenFileOpen.py
===================================================================
--- trunk/wizards/AppWizardX/spec-MenFileOpen.py 2005-10-30 18:18:43 UTC
(rev 215)
+++ trunk/wizards/AppWizardX/spec-MenFileOpen.py 2005-10-30 21:34:50 UTC
(rev 216)
@@ -0,0 +1,40 @@
+import dabo
+import dabo.dEvents as dEvents
+
+
+class MenFileOpen(dabo.ui.dMenu):
+
+ def afterInit(self):
+ self.Caption = "&Open\tCtrl+O"
+ self.HelpText = "Open a module"
+
+ # Define the forms you want in your open menu here. Insert a
("-", None)
+ # tuple and the code below will insert a separator in its
place. Explicitly
+ # set up which character has the hotkey by adding a & in front
of it and
+ # by turning off the autoHotKeys flag.
+ autoHotKeys = True
+ app = self.Application
+
+ forms = (%(forms)s)
+
+ for form in forms:
+ caption = form[0]
+ if caption == "-":
+ # insert separator instead:
+ self.appendSeparator()
+ else:
+ if autoHotKeys and "&" not in caption:
+ caption = "&%%s" %% caption
+ plainCaption = caption.replace("&", "")
+ itm = dabo.ui.dMenuItem(self, Caption=caption,
+ HelpText="Open the %%s module"
%% plainCaption,
+ Tag=form[1])
+ itm.bindEvent(dEvents.Hit, self.openForm)
+ self.appendItem(itm)
+
+
+ def openForm(self, evt):
+ app = self.Application
+ mainForm = app.MainForm
+ frm = evt.EventObject.Tag(mainForm)
+ frm.Show()
Property changes on: trunk/wizards/AppWizardX/spec-MenFileOpen.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-PagBase.py
===================================================================
--- trunk/wizards/AppWizardX/spec-PagBase.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-PagBase.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,5 @@
+import dabo.lib.datanav as datanav
+
+
+class PagBase(datanav.Page):
+ pass
Property changes on: trunk/wizards/AppWizardX/spec-PagBase.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-biz__init__.py
===================================================================
--- trunk/wizards/AppWizardX/spec-biz__init__.py 2005-10-30 18:18:43 UTC
(rev 215)
+++ trunk/wizards/AppWizardX/spec-biz__init__.py 2005-10-30 21:34:50 UTC
(rev 216)
@@ -0,0 +1,44 @@
+import os
+
+# Automatically import all classes from all .py files in this directory,
+# instead of having to explicitly import each of them like "from X import X":
+classes = []
+for f in os.listdir(__path__[0]):
+ if f[-3:] == ".py" and f != "__init__.py":
+ m = __import__(f[:-3], globals())
+ for i in dir(m):
+ c = m.__dict__[i]
+ if type(c) == type and c not in classes:
+ classes.append(c)
+
+# Put the classes in the module namespace, replacing the submodules:
+for c in classes:
+ globals()[c.__name__] = c
+
+# Clean up things that don't need to be exposed in the biz namespace:
+del(os)
+del(classes)
+try:
+ del(f)
+except NameError:
+ pass
+try:
+ del(m)
+except NameError:
+ pass
+try:
+ del(i)
+except NameError:
+ pass
+try:
+ del(c)
+except NameError:
+ pass
+
+
+
+## Place any explicit imports below this point.
+
+# example:
+# from myModule import MyClass
+
Property changes on: trunk/wizards/AppWizardX/spec-biz__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-db__init__.py
===================================================================
--- trunk/wizards/AppWizardX/spec-db__init__.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-db__init__.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0,0 +1,40 @@
+import os
+
+# Automatically import all names from all .py files in this directory,
+# instead of having to explicitly import each of them like "from X import X":
+names = []
+for f in os.listdir(__path__[0]):
+ if f[-3:] == ".py" and f != "__init__.py":
+ m = __import__(f[:-3], globals())
+ if m.__dict__.has_key(f[:-3]):
+ c = m.__dict__[f[:-3]]
+ if c not in names:
+ names.append(c)
+
+# Put the names in the module namespace, replacing the submodules:
+for c in names:
+ globals()[c.__name__] = c
+
+
+# Clean up things that don't need to be exposed in the ui namespace:
+del(os)
+del(names)
+try:
+ del(f)
+except NameError:
+ pass
+try:
+ del(m)
+except NameError:
+ pass
+try:
+ del(c)
+except NameError:
+ pass
+
+
+## Place any explicit imports below this point.
+
+# example:
+# from myModule import MyClass
+
Property changes on: trunk/wizards/AppWizardX/spec-db__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: trunk/wizards/AppWizardX/spec-main.py
===================================================================
--- trunk/wizards/AppWizardX/spec-main.py 2005-10-30 18:18:43 UTC (rev
215)
+++ trunk/wizards/AppWizardX/spec-main.py 2005-10-30 21:34:50 UTC (rev
216)
@@ -0
(2238 bytes were truncated as it was too long for the email (max 40000 bytes.)
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev