[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
r6801 - in trunk/gnue-common/src/schema: . scripter
From: |
johannes |
Subject: |
r6801 - in trunk/gnue-common/src/schema: . scripter |
Date: |
Mon, 13 Dec 2004 07:11:41 -0600 (CST) |
Author: johannes
Date: 2004-12-13 07:11:40 -0600 (Mon, 13 Dec 2004)
New Revision: 6801
Modified:
trunk/gnue-common/src/schema/GSParser.py
trunk/gnue-common/src/schema/Objects.py
trunk/gnue-common/src/schema/scripter/Scripter.py
Log:
gnue-schema asks before it modifies the backend database.
gnue-schema is now able to automatically order table operations to avoid
referential constraint conflicts
gnue-schema is now able to automatically order data insertions (even for
tables with multiple fishooks). So the order of tabledata does no longer
matter.
Modified: trunk/gnue-common/src/schema/GSParser.py
===================================================================
--- trunk/gnue-common/src/schema/GSParser.py 2004-12-12 11:14:20 UTC (rev
6800)
+++ trunk/gnue-common/src/schema/GSParser.py 2004-12-13 13:11:40 UTC (rev
6801)
@@ -1,6 +1,9 @@
+# GNU Enterprise Common - GNUe Schema Definition - Sax base XML Parser
#
-# This file is part of GNU Enterprise.
+# Copyright 2001-2004 Free Software Foundation
#
+# This file is part of GNU Enterprise
+#
# GNU Enterprise is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
@@ -16,60 +19,45 @@
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
-# Copyright 2002-2004 Free Software Foundation
-#
-# FILE:
-# GSParser.py
-#
-# DESCRIPTION:
-# Class that contains a SAX-based xml processor for GNUe Schema Definitions
-#
-# NOTES:
-#
# $Id$
import Objects
+import copy
+import types
+
from gnue.common.formatting import GTypecast
from gnue.common.definitions import GParser
-import copy, types
+xmlElements = None
+# =============================================================================
+# load an XML object tree from a given stream and return it's root object
+# =============================================================================
+def loadFile (buffer, app = None, initialize = True):
+ """
+ This function loads an XML object tree from a given stream and return it's
+ root object.
+ """
-
-########
-######## Please keep this file neat !!!
-########
-
-
-
-
-
-
-
-#######################################################
-# This method loads a report from an XML file and returns
-# a GSSchema object.
-#######################################################
-
-def loadFile(buffer, app=None, initialize=1):
return GParser.loadXMLObject (buffer, xmlSchemaHandler, 'GSSchema', 'schema',
- initialize, attributes={'_app': app})
+ initialize, attributes = {'_app': app})
+# =============================================================================
+# Build a dictionary tree with all available XML elements
+# =============================================================================
-xmlElements = None
+def getXMLelements ():
+ """
+ This function creates a dictionary tree with all valid xml elements. This
+ dictionary tree is available via global variable 'xmlElements'
+ """
-
-def getXMLelements():
-
global xmlElements
- if xmlElements == None:
-
- #
- #
+ if xmlElements is None:
xmlElements = {
'schema': {
'BaseClass': Objects.GSSchema,
@@ -213,7 +201,7 @@
'Required': 1,
'Typecast': GTypecast.name },
'unique': {
- 'Default': 0,
+ 'Default': 0,
'Typecast': GTypecast.boolean } },
'ParentTags': ('indexes',) },
@@ -287,7 +275,7 @@
'Default' : 0}
},
'ParentTags': ('row',),
- 'MixedContent': 1,
+ 'MixedContent': 1,
'KeepWhitespace': 1},
'description': {
@@ -299,21 +287,21 @@
}
- return GParser.buildImportableTags('schema',xmlElements)
+ return GParser.buildImportableTags ('schema', xmlElements)
-#######################################################
-#
-# xmlSchemaHandler
-#
-# This class is called by the XML parser to
-# process the xml file.
-#
-#######################################################
+# =============================================================================
+# Class called by the XML parser to process the XML file
+# =============================================================================
class xmlSchemaHandler (GParser.xmlHandler):
- def __init__(self):
- GParser.xmlHandler.__init__(self)
- self.xmlElements = getXMLelements()
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+ def __init__ (self):
+
+ GParser.xmlHandler.__init__(self)
+ self.xmlElements = getXMLelements ()
+
Modified: trunk/gnue-common/src/schema/Objects.py
===================================================================
--- trunk/gnue-common/src/schema/Objects.py 2004-12-12 11:14:20 UTC (rev
6800)
+++ trunk/gnue-common/src/schema/Objects.py 2004-12-13 13:11:40 UTC (rev
6801)
@@ -1,6 +1,9 @@
+# GNU Enterprise Common - GNUe Schema Definition - XML Object Tree elements
#
-# This file is part of GNU Enterprise.
+# Copyright 2001-2004 Free Software Foundation
#
+# This file is part of GNU Enterprise
+#
# GNU Enterprise is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
@@ -16,22 +19,13 @@
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
-# Copyright 2002-2004 Free Software Foundation
-#
-# FILE:
-# Objects.py
-#
-# DESCRIPTION:
-# GObjects for the Schema definitions
-#
-# NOTES:
-#
# $Id$
from gnue.common.definitions.GObjects import GObj
from gnue.common.definitions.GRootObj import GRootObj
from gnue.common.schema.GSData import verifyDataType, valueToNative
from gnue.common.apps import errors
+
import GSParser
import types
@@ -140,6 +134,9 @@
if not self.type in ["unique", "foreignkey"]:
raise ConstraintTypeError, self.type
+ return
+
+ # TODO: verify which constraint-checks are still usefull in here
csFields = self.findChildrenOfType ('GSConstraintField')
self.__checkFields (None, csFields)
Modified: trunk/gnue-common/src/schema/scripter/Scripter.py
===================================================================
--- trunk/gnue-common/src/schema/scripter/Scripter.py 2004-12-12 11:14:20 UTC
(rev 6800)
+++ trunk/gnue-common/src/schema/scripter/Scripter.py 2004-12-13 13:11:40 UTC
(rev 6801)
@@ -1,6 +1,9 @@
+# GNU Enterprise Common - GNUe Schema Definition - Schema & Script Generator
#
-# This file is part of GNU Enterprise.
+# Copyright 2001-2004 Free Software Foundation
#
+# This file is part of GNU Enterprise
+#
# GNU Enterprise is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
@@ -16,32 +19,22 @@
# write to the Free Software Foundation, Inc., 59 Temple Place
# - Suite 330, Boston, MA 02111-1307, USA.
#
-# Copyright 2002-2004 Free Software Foundation
-#
# $Id$
-from gnue.common import VERSION
+import string
+
from gnue.common.schema import GSParser
from gnue.common.utils.FileUtils import openResource
from gnue.common.apps.GClientApp import *
-from gnue.common.datasources import GDataSource, GConditions
+from gnue.common.datasources import GDataSource
from gnue.common.apps import errors
-from time import strftime
-from string import join
-import sys
-import os
-import re
-import copy
-import types
-
-
# =============================================================================
# Exceptions
# =============================================================================
-class Error (gException):
+class Error (errors.SystemError):
pass
class MissingKeyError (errors.ApplicationError):
@@ -49,16 +42,27 @@
msg = u_("Data row of table '%s' has no key fields") % tableName
errors.ApplicationError.__init__ (self, msg)
+class CircularReferenceError (errors.ApplicationError):
+ def __init__ (self):
+ msg = u_("Tables have circular or unresolveable references")
+ errors.ApplicationError.__init__ (self, msg)
+
+class CircularFishHookError (errors.ApplicationError):
+ def __init__ (self, table):
+ msg = u_("The table '%s' contains circular data-references") % table
+ errors.ApplicationError.__init__ (self, msg)
+
# =============================================================================
# Load GNUe Schema Definition files and create a database schema for it
# =============================================================================
class Scripter (GClientApp):
- VERSION = VERSION
- COMMAND = "gnue-schema"
- NAME = "GNUe Schema Scripter"
- USAGE = "[options] gsd-file [gsd-file gsd-file ...]"
+ from gnue.common import VERSION
+
+ COMMAND = "gnue-schema"
+ NAME = "GNUe Schema Scripter"
+ USAGE = "[options] gsd-file [gsd-file gsd-file ...]"
SUMMARY = _("GNUe Schema Scripter creates database schemas based on GNUe "
"Schema Definitions.")
@@ -99,6 +103,11 @@
"command line. This user becomes the owner of the database "
"and will be implicitly created."))
+ self.addCommandOption ('yes', 'y', default = False,
+ help = _("If this option is set, the program runs in batch-mode, "
+ "which means all questions are answered with 'yes' "
+ "automatically."))
+
ConfigOptions = {}
GClientApp.__init__ (self, connections, 'schema', ConfigOptions)
@@ -116,49 +125,58 @@
"""
self.__checkOptions ()
+ self.__loadInputFiles ()
- try:
- self.tables = []
- self.tabledata = []
- for item in range (len (self._files)):
- print o (u_("Loading gsd file '%s' ...") % self.ARGUMENTS [item])
+ # If we have to process data rows let's have a look if there's a key
+ # defined for all tables and rows
+ if self.__doData and len (self.tabledata.keys ()):
+ self.verifyDataKeys ()
+
+ # If the user wants to create the database, first go and ask if she's
+ # really sure about
+ if self.OPTIONS ['createdb']:
+ res = self.__ask (u_("You are about to create the new database '%s'. " \
+ "Continue") % self.connection.name,
+ [u_("y"), u_("n")], "n")
+ if res == u_("n"):
+ return
+
+ creator = self.connection.getSchemaCreator ()
+ if creator is not None:
try:
- schema = GSParser.loadFile (self._files [item])
- schema.walk (self.__iterateObjects)
+ creator.createDatabase ()
finally:
- self._files [item].close ()
+ creator.close ()
- except Exception:
- print sys.exc_info () [1]
+ # Process schema information (if requested)
+ asked = False
+ if self.__doSchema:
+ if not self.OPTIONS ['file-only']:
+ asked = True
+ res = self.__ask (u_("You are about to change the database '%s'. " \
+ "Continue") % self.connection.name, [u_("y"), u_("n")], u_("n"))
+ if res == u_("n"):
+ return
- else:
- try:
- if self.__doData and len (self.tabledata):
- self.verifyDataKeys ()
+ self.executeAndGenerateCode ()
- if self.OPTIONS ['createdb']:
- creator = self.connection.getSchemaCreator ()
- if creator is not None:
- try:
- creator.createDatabase ()
- finally:
- creator.close ()
+ # Process data information (if requested)
+ if self.__doData and len (self.tabledata.keys ()):
+ if not asked:
+ res = self.__ask (u_("You are about to change the database '%s'. " \
+ "Continue") % self.connection.name, [u_("y"), u_("n")], u_("n"))
+ if res == u_("n"):
+ return
- if self.__doSchema:
- self.executeAndGenerateCode ()
+ self.updateData ()
- if self.__doData and len (self.tabledata):
- self.updateData ()
- except Exception:
- print sys.exc_info () [1]
-
# ---------------------------------------------------------------------------
# Walk through all command line options
# ---------------------------------------------------------------------------
@@ -216,8 +234,106 @@
self.connection.parameters ['password'] = password
+ # ---------------------------------------------------------------------------
+ # Load all input files
+ # ---------------------------------------------------------------------------
+ def __loadInputFiles (self):
+ """
+ This function loads all files given in the argument list and builds the
+ dictionaries with the tabledefinition and -data. The sequence 'tableOrder'
+ lists all tablenames in an order to be created without violating
+ referential constraints.
+ """
+
+ self.tables = {}
+ self.tabledata = {}
+
+ for item in range (len (self._files)):
+ print o (u_("Loading gsd file '%s' ...") % self.ARGUMENTS [item])
+
+ try:
+ schema = GSParser.loadFile (self._files [item])
+ schema.walk (self.__iterateObjects)
+
+ finally:
+ self._files [item].close ()
+
+ tables = {}
+ self.fishes = {}
+
+ for table in self.tables.values ():
+ # first add the table itself to the dictionary
+ tablename = table ['name'].lower ()
+ if not tables.has_key (tablename):
+ tables [tablename] = []
+
+ # if the table has constraints, add a reference to this table
+ if table.has_key ('constraints') and len (table ['constraints']):
+ for constraint in table ['constraints']:
+ ref = constraint.get ('reftable')
+ if ref:
+ refname = ref.lower ()
+ if refname == tablename:
+ if not self.fishes.has_key (tablename):
+ self.fishes [tablename] = []
+
+ self.fishes [tablename].append ((constraint ['fields'],
+ constraint ['reffields']))
+ else:
+ tables [tablename].append (refname)
+
+ # now get the proper order to create/modify the tables
+ self.tableOrder = []
+ add = self.__shrinkList (tables, CircularReferenceError)
+ while len (add):
+ self.tableOrder.extend (add)
+ add = self.__shrinkList (tables, CircularReferenceError)
+
+
# ---------------------------------------------------------------------------
+ # Return all items from the dictionary which have no dependencies
+ # ---------------------------------------------------------------------------
+
+ def __shrinkList (self, dataDict, circularError, *args):
+ """
+ This function returns a sequence of all keys without any dependencies.
+ If no such items were found but there are still entries in the dictionary
+ a CircularReferenceError will be raised.
+
+ @param dataDict: dictionary to extract items from. The values are sequences
+ with keys which are referencing to an item
+ @param circularError: exception class which will be raised if there are
+ circular references
+ @return: sequence of all keys which do not have any dependency
+ """
+
+ result = []
+
+ for (key, references) in dataDict.items ():
+ if not len (references):
+ # if an item has no dependencies add it to the result
+ result.append (key)
+
+ # remove it from all other entries it is referred to
+ for ref in dataDict.values ():
+ while key in ref:
+ ref.remove (key)
+
+ # and finally remove it from the dictionary
+ del dataDict [key]
+
+ # if no entry without a dependency was found, but there are still entries
+ # in the dictionary, they *must* have circular references
+ if not len (result) and len (dataDict.keys ()):
+ raise circularError, args
+
+ return result
+
+
+
+
+ # ---------------------------------------------------------------------------
# Get a dictionary with all keys listed in tags and values from sObject
# ---------------------------------------------------------------------------
@@ -248,15 +364,23 @@
@param sObject: current Schema object to be processed
"""
+
if sObject._type == "GSTable":
- self.tables.append ({'name': sObject.name})
- sObject.walk (self.__schemaFields, defs = self.tables [-1])
+ tableKey = sObject.name.lower ()
+ if not self.tables.has_key (tableKey):
+ self.tables [tableKey] = {'name': sObject.name}
+ sObject.walk (self.__schemaFields, defs = self.tables [tableKey])
+
elif sObject._type == "GSTableData":
- self.tabledata.append ({'name': sObject.tablename, 'rows': [],
- 'defi': {'fields': [], 'key': []}})
- sObject.walk (self.__dataRows, defs = self.tabledata [-1]['rows'])
+ tableKey = sObject.tablename.lower ()
+ if not self.tabledata.has_key (tableKey):
+ self.tabledata [tableKey] = {'name': sObject.tablename,
+ 'rows': [],
+ 'defi': {'fields': [], 'key': []}}
+ sObject.walk (self.__dataRows, defs = self.tabledata [tableKey])
+
return
@@ -281,8 +405,16 @@
'defaultwith', 'length', 'precision', 'nullable'])
if not defs.has_key ('fields'):
defs ['fields'] = []
- defs ['fields'].append (fDef)
+ found = False
+ for item in defs ['fields']:
+ if item ['name'].lower () == sObject.name.lower ():
+ found = True
+ break
+
+ if not found:
+ defs ['fields'].append (fDef)
+
# add a primary key definition to the table definition
elif sObject._type == "GSPrimaryKey":
pkDef = {'name': sObject.name, 'fields': []}
@@ -294,13 +426,20 @@
if not defs.has_key ('indices'):
defs ['indices'] = []
- indexDef = {'name': sObject.name,
- 'unique': hasattr (sObject, "unique") and sObject.unique,
- 'fields': []}
- defs ['indices'].append (indexDef)
+ found = False
+ for item in defs ['indices']:
+ if item ['name'].lower () == sObject.name.lower ():
+ found = True
+ break
- sObject.walk (self.__schemaIndex, defs = indexDef)
+ if not found:
+ indexDef = {'name' : sObject.name,
+ 'unique': hasattr (sObject, "unique") and sObject.unique,
+ 'fields': []}
+ defs ['indices'].append (indexDef)
+ sObject.walk (self.__schemaIndex, defs = indexDef)
+
# create constraints
elif sObject._type == "GSConstraint":
# for unique-constraints we use a 'unique index'
@@ -308,23 +447,38 @@
if not defs.has_key ('indices'):
defs ['indices'] = []
- cDef = {'name' : sObject.name,
- 'unique': True,
- 'fields': []}
- defs ['indices'].append (cDef)
+ found = False
+ for item in defs ['indices']:
+ if item ['name'].lower () == sObject.name.lower ():
+ found = True
+ break
+ if not found:
+ cDef = {'name' : sObject.name,
+ 'unique': True,
+ 'fields': []}
+ defs ['indices'].append (cDef)
+
# for all other types of constraints we use a ConstraintDefinition
else:
if not defs.has_key ('constraints'):
defs ['constraints'] = []
- cDef = self.fetchTags (sObject, ['name', 'type'])
- cDef ['fields'] = []
- defs ['constraints'].append (cDef)
+ found = False
+ for item in defs ['constraints']:
+ if item ['name'].lower () == sObject.name.lower ():
+ found = True
+ break
- sObject.walk (self.__schemaConstraint, defs = cDef)
+ if not found:
+ cDef = self.fetchTags (sObject, ['name', 'type'])
+ cDef ['fields'] = []
+ defs ['constraints'].append (cDef)
+ if not found:
+ sObject.walk (self.__schemaConstraint, defs = cDef)
+
# ---------------------------------------------------------------------------
# Iterate over all fields of a primary key
# ---------------------------------------------------------------------------
@@ -392,11 +546,11 @@
@param defs: sequence of row definitions of the corresponding table
"""
if sObject._type == "GSRow":
- defs.append ({'key': [], 'fields': []})
- sObject.walk (self.__dataValues, defs [-1])
+ defs ['rows'].append ({'key': [], 'fields': {}})
+ sObject.walk (self.__dataValues, defs ['rows'][-1])
elif sObject._type == "GSColumn":
- defi = self.tabledata [-1]['defi']
+ defi = defs ['defi']
defi ['fields'].append (sObject.field)
if hasattr (sObject, 'key') and sObject.key:
@@ -415,7 +569,7 @@
@param defs: row definition dictionary to be extended.
"""
if sObject._type == "GSValue":
- defs ['fields'].append ({'name' : sObject.field, 'value': sObject.value})
+ defs ['fields'][sObject.field] = sObject.value
if hasattr (sObject, "key") and sObject.key:
defs ['key'].append (sObject.field)
@@ -435,7 +589,7 @@
@raise MissingKeyError: If no key information could be found for all rows
of a table this exception will be raised.
"""
- for item in self.tabledata:
+ for item in self.tabledata.values ():
tableName = item ['name']
rows = item ['rows']
tableKey = None
@@ -458,10 +612,11 @@
# is there a table definition with a valid primary key available ?
if tableKey is None:
- for item in self.tables:
- if item ['name'] == tableName and item.has_key ('primarykey'):
- tableKey = item['primarykey']['fields']
+ tableDef = self.tables.get (tableName.lower ())
+ if tableDef is not None and tableDef.has_key ('primarykey'):
+ tableKey = tableDef ['primarykey']['fields']
+
# if at least one key was available, fill up all missing keys with this
# one.
if tableKey is not None:
@@ -487,9 +642,16 @@
self.connections.loginToConnection (self.connection)
- print _("Updating schema ...")
- code = self.connection.updateSchema (self.tables,
- self.OPTIONS ['file-only'])
+ print o(u_("Updating schema ..."))
+
+ # create a list of table definitions ordered to avoid integrity errors
+ tables = []
+ for name in self.tableOrder:
+ if self.tables.has_key (name):
+ tables.append (self.tables [name])
+
+ code = self.connection.updateSchema (tables, self.OPTIONS ['file-only'])
+
if self.outfile is not None:
dest = open (self.outfile, 'w')
@@ -510,9 +672,15 @@
specified key-fields and updated if it exists otherwise inserted.
"""
- print _("Updating data ...")
+ print o(u_("Updating data ..."))
- for table in self.tabledata:
+ # make sure to use the proper table order for inserting data
+ tablelist = []
+ for name in self.tableOrder:
+ if self.tabledata.has_key (name):
+ tablelist.append (self.tabledata [name])
+
+ for table in tablelist:
tablename = table ['name']
rows = table ['rows']
@@ -524,20 +692,18 @@
print o (u_(" updating table '%s' ...") % tablename)
stat = [0, 0, 0]
- for row in table ['rows']:
- cList = ['and']
+ if self.fishes.has_key (tablename.lower ()):
+ rows = self.__fishSort (table)
+ else:
+ rows = table ['rows']
+ for row in rows:
+ condition = {}
+
for keyField in keyFields:
- for field in row ['fields']:
- if field ['name'] == keyField:
- kvalue = field ['value']
- break
+ condition [keyField] = row ['fields'][keyField]
- cList.append (['eq', ['field', keyField], ['const', kvalue]])
-
- gDebug (4, "Condition: %s" % cList)
-
- condition = GConditions.buildTreeFromList (cList)
+ gDebug (4, "Condition: %s" % condition)
resultSet = datasource.createResultSet (condition)
if resultSet.firstRecord () is None:
@@ -548,10 +714,7 @@
doPost = False
- for field in row ['fields']:
- fieldName = field ['name']
- fieldValue = field ['value']
-
+ for (fieldName, fieldValue) in row ['fields'].items ():
if resultSet.current.getField (fieldName) != fieldValue:
resultSet.current.setField (fieldName, fieldValue)
doPost = True
@@ -576,11 +739,21 @@
# ---------------------------------------------------------------------------
def __openSource (self, table):
+ """
+ Create a new datasource instance for the given table using all fields
+ requested either by a table definition, or a table description.
+
+ @param table: name of the table to create a datasource for
+ @return: tuple with datasource instance and a sequence of all key-fields
+ """
+
+ # First we use all fields and keys as described by a table definition
fieldList = table ['defi']['fields']
keyFields = table ['defi']['key']
+ # Now add all fields, which are only given in the rows of tabledata tags
for row in table ['rows']:
- for field in [f ['name'] for f in row ['fields']]:
+ for field in row ['fields'].keys ():
if not field in fieldList:
fieldList.append (field)
@@ -598,6 +771,118 @@
return (source, keyFields)
+ # ---------------------------------------------------------------------------
+ # Sort rows of table with a fish-hook
+ # ---------------------------------------------------------------------------
+
+ def __fishSort (self, tabledef):
+ """
+ If a table has fishhooks, make sure to re-order all data rows so they can
+ be inserted without conflicting constraints.
+
+ @param tabledef: tabledata definition to sort the rows of
+ @return: sequence of row-dictionaries
+ """
+
+ tablename = tabledef ['name']
+ fishes = self.fishes [tablename.lower ()]
+
+ # First create an empty map of all fields to keep track of
+ fishdict = {}
+ for (fields, reffields) in fishes:
+ for field in fields:
+ fishdict [field] = {}
+
+ # Now we create a dictionary of all data rows, where we use a tuple of all
+ # key-fields as dictionary key. Additionaly update the mapping with the
+ # backreferences (fishdict)
+ rowByKey = {}
+
+ for row in tabledef ['rows']:
+ key = tuple ([row ['fields'][item] for item in row ['key']])
+ rowByKey [key] = row ['fields']
+
+ for (fields, reffields) in fishes:
+ for ix in range (len (fields)):
+ fishdict [fields [ix]][row ['fields'][reffields [ix]]] = key
+
+ # In the next step we create a dictionary to track all dependencies between
+ # the rows.
+ sortdict = {}
+
+ for (key, data) in rowByKey.items ():
+ if not sortdict.has_key (key):
+ sortdict [key] = []
+
+ for (fields, reffields) in fishes:
+ for item in fields:
+ # If a reference field has a value, we get the key of that record,
+ # and add it as a dependency of the current row, since that record
+ # has to be inserted before the current one.
+ if data.get (item) is not None:
+ ref = fishdict [item].get (data [item])
+ if ref is not None:
+ sortdict [key].append (ref)
+
+ # Now, since we have a dictionary mapping all dependencies, let's create an
+ # ordered list of all rows. All items holding an empty sequence, do *not*
+ # depend on another row, so we can just add them. Aside from that we can
+ # remove all these rows from the remaining dependency-sequences, since this
+ # dependency is fullfilled.
+ order = []
+ add = self.__shrinkList (sortdict, CircularFishHookError, tablename)
+
+ while len (add):
+ order.extend (add)
+ add = self.__shrinkList (sortdict, CircularFishHookError, tablename)
+
+ # The sequence 'order' now contains all rows in the proper order to be
+ # inserted. To be done, we have to create a sequence of rows-dicts.
+ result = []
+ for key in order:
+ result.append ({'fields': rowByKey [key]})
+
+ return result
+
+
+
+ # ---------------------------------------------------------------------------
+ # Ask a question with a set of valid options and a default
+ # ---------------------------------------------------------------------------
+
+ def __ask (self, question, options, default):
+ """
+ This function asks for a question, allowing a set of answers, using a
+ default-value if the user just presses <Enter>.
+
+ @param question: string with the question to ask
+ @param options: sequence of allowed options, i.e. ['y', 'n']
+ @param default: string with the default option
+
+ @return: string with the option selected
+ """
+
+ if self.OPTIONS ['yes']:
+ return u_("y")
+
+ answer = None
+ default = default.lower ()
+ lopts = map (string.lower, options)
+
+ dopts = []
+ for item in lopts:
+ dopts.append (item == default and item.upper () or item)
+
+ while True:
+ print o(question), o("[%s]:" % string.join (dopts, ",")),
+ answer = raw_input ().lower () or default
+
+ if answer in lopts:
+ break
+
+ return answer
+
+
# =============================================================================
# If executed directly, start the scripter
# =============================================================================
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- r6801 - in trunk/gnue-common/src/schema: . scripter,
johannes <=