[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r7291 - in trunk/gnue-common/src/datasources/drivers: . Base file
From: |
reinhard |
Subject: |
[gnue] r7291 - in trunk/gnue-common/src/datasources/drivers: . Base file special |
Date: |
Mon, 4 Apr 2005 16:50:49 -0500 (CDT) |
Author: reinhard
Date: 2005-04-04 16:50:47 -0500 (Mon, 04 Apr 2005)
New Revision: 7291
Added:
trunk/gnue-common/src/datasources/drivers/file/
trunk/gnue-common/src/datasources/drivers/file/Base.py
trunk/gnue-common/src/datasources/drivers/file/__init__.py
trunk/gnue-common/src/datasources/drivers/file/csvfile.py
trunk/gnue-common/src/datasources/drivers/file/dbffile.py
trunk/gnue-common/src/datasources/drivers/file/inifile.py
Removed:
trunk/gnue-common/src/datasources/drivers/csv/
trunk/gnue-common/src/datasources/drivers/dbf/
trunk/gnue-common/src/datasources/drivers/special/configfile/
Modified:
trunk/gnue-common/src/datasources/drivers/Base/DataObject.py
Log:
Rewrote file based dbdrivers, using a common base class.
Modified: trunk/gnue-common/src/datasources/drivers/Base/DataObject.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/Base/DataObject.py
2005-04-01 14:32:06 UTC (rev 7290)
+++ trunk/gnue-common/src/datasources/drivers/Base/DataObject.py
2005-04-04 21:50:47 UTC (rev 7291)
@@ -49,6 +49,8 @@
def __init__(self, connection):
self._connection = connection
+ self.table = None
+
self.masterlink = ""
self.detaillink = ""
Property changes on: trunk/gnue-common/src/datasources/drivers/file
___________________________________________________________________
Name: svn:ignore
+ *.pyc
Added: trunk/gnue-common/src/datasources/drivers/file/Base.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/Base.py 2005-04-01
14:32:06 UTC (rev 7290)
+++ trunk/gnue-common/src/datasources/drivers/file/Base.py 2005-04-04
21:50:47 UTC (rev 7291)
@@ -0,0 +1,267 @@
+# GNU Enterprise Common Library - Generic File Based DB Driver
+#
+# Copyright 2000-2005 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
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+__noplugin__ = True
+
+import glob
+import os
+
+from gnue import paths
+from gnue.common.datasources import GIntrospection
+from gnue.common.datasources.drivers import Base
+
+
+# =============================================================================
+# Introspection class
+# =============================================================================
+
+class Introspection (GIntrospection.Introspection):
+
+ types = [('table', _('Tables'), 1)]
+
+ # ---------------------------------------------------------------------------
+ # Find a schema element by name and/or type
+ # ---------------------------------------------------------------------------
+
+ def find (self, name = None, type = None):
+
+ if name:
+ filename = self._connection.getFilename (name)
+ else:
+ filename = self._connection.getFilename ('*')
+
+ result = []
+
+ for file in glob.glob (filename):
+ for table in self._connection._listTables (file):
+ attrs = {'id': table, 'name': table, 'type': 'table', 'file': file}
+ result.append (GIntrospection.Schema (
+ attrs = attrs, getChildSchema = self.__getChildSchema))
+ return result
+
+
+ # ---------------------------------------------------------------------------
+ # Get all fields of a table
+ # ---------------------------------------------------------------------------
+
+ def __getChildSchema (self, parent):
+
+ result = []
+ for attrs in self._connection._listFields (parent.file, parent.name):
+ result.append (GIntrospection.Schema (attrs = attrs))
+ return result
+
+
+# =============================================================================
+# ResultSet class
+# =============================================================================
+
+class ResultSet (Base.ResultSet):
+ """
+ Generic ResultSet class for file based backends.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, dataObject, connection, table, conditions,
+ masterRecordSet):
+
+ Base.ResultSet.__init__ (self, dataObject,
+ masterRecordSet = masterRecordSet)
+
+ self._readOnly = True
+ self.__connection = connection
+ self.__table = table
+ self.__conditions = conditions
+ self.__dataLoaded = False
+
+
+ # ---------------------------------------------------------------------------
+ # Create RecordSet instances
+ # ---------------------------------------------------------------------------
+
+ def _loadNextRecord (self):
+
+ if self.__dataLoaded:
+ return False
+
+ for row in self.__connection.getFile (self.__table):
+ record = self._recordSetClass (parent = self, initialData = row)
+ self._cachedRecords.append (record)
+ self._recordCount += 1
+
+ self.__dataLoaded = True
+
+ return True
+
+
+# =============================================================================
+# DataObject class
+# =============================================================================
+
+class DataObject (Base.DataObject):
+ """
+ Generic DataObject class for file based backends.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Create RecordSet instances
+ # ---------------------------------------------------------------------------
+
+ def _createResultSet (self, conditions = None, readOnly = False,
+ masterRecordSet = None, sql = ""):
+ return ResultSet (self, self._connection, self.table, conditions,
+ masterRecordSet)
+
+
+# =============================================================================
+# Connection class
+# =============================================================================
+
+class Connection (Base.Connection):
+ """
+ Generic Connection class for file based backends.
+ """
+
+ defaultBehavior = Introspection
+
+ supportedDataObjects = {'object': DataObject}
+
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, connections, name, parameters):
+
+ Base.Connection.__init__ (self, connections, name, parameters)
+
+ if parameters.has_key ("filename"):
+ self.__filename = parameters ['filename']
+ elif parameters.has_key ("file"):
+ self.__filename = parameters ['file']
+ else:
+ self.__filename = parameters ['dbname']
+
+ # We can replace some of the parameters right away
+ self.__filename = self.__filename % {'home' : os.environ ['HOME'],
+ 'configdir': paths.config,
+ 'table' : '%(table)s'}
+
+
+ # ---------------------------------------------------------------------------
+ # Get the filename for a given table
+ # ---------------------------------------------------------------------------
+
+ def getFilename (self, table):
+
+ return self.__filename % {'table': table}
+
+
+ # ---------------------------------------------------------------------------
+ # Get the data of a file
+ # ---------------------------------------------------------------------------
+
+ def getFile (self, table):
+
+ return self._loadFile (self.getFilename (table), table)
+
+
+ # ---------------------------------------------------------------------------
+ # Virtual methods (to be implemented by descendants)
+ # ---------------------------------------------------------------------------
+
+ def _listTables (self, filename):
+ """
+ List all the table names contained in the given file.
+
+ This function must return a list (or any other iterable object) with table
+ names. Table names can be mixed case.
+
+ The default behaviour of this function is to extract the table name from
+ the file name (if the filename parameter of the connection contains a
+ '%(table)s') and return 'data' otherwise.
+
+ Descendants only have to overwrite this function if a file can contain more
+ than one table.
+
+ This method is used for introspection.
+
+ @param filename: Filename
+ @return: List of table names
+ """
+ f = self.getFilename ('\n')
+ if '\n' in f:
+ (prefix, postfix) = f.split ('\n', 1)
+ if filename [:len (prefix)] == prefix and \
+ filename [-len (postfix):] == postfix:
+ return [filename [len (prefix):-len (postfix)]]
+ else:
+ return [filename]
+ else:
+ return ['data']
+
+
+ # ---------------------------------------------------------------------------
+ # Virtual methods (to be implemented by descendants)
+ # ---------------------------------------------------------------------------
+
+ def _listFields (self, filename, table):
+ """
+ List all the field names.
+
+ This function must return a list (or any other iterable object) with a
+ dictionary per field. The dictionary must have the following keys:
+ id (unique field identifier), name (field name), type (field type, usually
+ 'field'), nativetype (native data type), datatype (GNUe data type), and
+ required (Boolean with True if the field is non-nullable).
+
+ Field names can be mixed case.
+
+ This method is used for introspection.
+
+ @param filename: Filename
+ @param table: Table name (only useful if a file can contain more than one
+ table)
+ @return: List of dictionaries as explained above
+ """
+ return []
+
+
+ # ---------------------------------------------------------------------------
+ # Virtual methods (to be implemented by descendants)
+ # ---------------------------------------------------------------------------
+
+ def _loadFile (self, filename, table):
+ """
+ Load data from a file.
+
+ @param filename: Filename
+ @param table: Table name (only useful if a file can contain more than one
+ table)
+ @return: List of fieldname/value dictionaries containing all data of the
+ complete file, where the keys in the dictionary must be lower case.
+ """
+ return []
Property changes on: trunk/gnue-common/src/datasources/drivers/file/Base.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: trunk/gnue-common/src/datasources/drivers/file/__init__.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/__init__.py 2005-04-01
14:32:06 UTC (rev 7290)
+++ trunk/gnue-common/src/datasources/drivers/file/__init__.py 2005-04-04
21:50:47 UTC (rev 7291)
@@ -0,0 +1,26 @@
+# GNU Enterprise Datasource Library - Driver for GNUe-AppServer
+#
+# Copyright 2000-2005 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
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+"""
+Implementations of dbdrivers for use with various file formats
+"""
Property changes on: trunk/gnue-common/src/datasources/drivers/file/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: trunk/gnue-common/src/datasources/drivers/file/csvfile.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/csvfile.py 2005-04-01
14:32:06 UTC (rev 7290)
+++ trunk/gnue-common/src/datasources/drivers/file/csvfile.py 2005-04-04
21:50:47 UTC (rev 7291)
@@ -0,0 +1,88 @@
+# GNU Enterprise Common Library - CSV File DB Driver
+#
+# Copyright 2000-2005 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
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+import csv
+
+from gnue.common.datasources.drivers.file import Base
+
+
+# =============================================================================
+# Connection class
+# =============================================================================
+
+class Connection (Base.Connection):
+ """
+ Connection class for CSV file backends.
+ """
+
+ # ---------------------------------------------------------------------------
+ # Prepare the file
+ # ---------------------------------------------------------------------------
+
+ def __prepareFile (self, filename):
+
+ f = file (filename, 'rb')
+
+ # Let the sniffer determine the file format
+ sniffer = csv.Sniffer ()
+ dialect = sniffer.sniff (f.read ())
+
+ # Rewind file
+ f.seek (0)
+
+ # Read the first row to get the field names
+ headers = (csv.reader (f, dialect = dialect)).next ()
+
+ return (f, dialect, headers)
+
+
+ # ---------------------------------------------------------------------------
+ # Iterate through the list of field names
+ # ---------------------------------------------------------------------------
+
+ def _listFields (self, filename, table):
+
+ (f, dialect, headers) = self.__prepareFile (filename)
+
+ for field in headers:
+ yield {'id': field,
+ 'name': field,
+ 'type': 'field',
+ 'nativetype': 'text',
+ 'datatype': 'text',
+ 'required': False}
+
+
+ # ---------------------------------------------------------------------------
+ # Load the file
+ # ---------------------------------------------------------------------------
+
+ def _loadFile (self, filename, table):
+
+ (f, dialect, headers) = self.__prepareFile (filename)
+
+ fieldnames = [field.lower () for field in headers]
+ reader = csv.DictReader (f, fieldnames, dialect = dialect)
+
+ # Reader behaves like a list of dictionaries, so we can just return it
+ return reader
Property changes on: trunk/gnue-common/src/datasources/drivers/file/csvfile.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: trunk/gnue-common/src/datasources/drivers/file/dbffile.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/dbffile.py 2005-04-01
14:32:06 UTC (rev 7290)
+++ trunk/gnue-common/src/datasources/drivers/file/dbffile.py 2005-04-04
21:50:47 UTC (rev 7291)
@@ -0,0 +1,74 @@
+# GNU Enterprise Common Library - DBF File DB Driver
+#
+# Copyright 2000-2005 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
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+def __initplugin__ ():
+ import dbf
+
+from gnue.common.datasources.drivers.file import Base
+
+
+# =============================================================================
+# Connection class
+# =============================================================================
+
+class Connection (Base.Connection):
+ """
+ Connection class for DBF file backends.
+ """
+
+ __datatypes = {'C': 'text',
+ 'N': 'number',
+ 'D': 'date',
+ 'L': 'boolean'}
+
+
+ # ---------------------------------------------------------------------------
+ # Iterate through the list of field names
+ # ---------------------------------------------------------------------------
+
+ def _listFields (self, filename, table):
+
+ f = dbf.dbf (filename)
+
+ for field in f.fields:
+ yield {'id': field [0],
+ 'name': field [0],
+ 'type': 'field',
+ 'nativetype': field [1],
+ 'datatype': self.__datatypes [field [1]],
+ 'length': field [2],
+ 'required': False}
+
+
+ # ---------------------------------------------------------------------------
+ # Load the file
+ # ---------------------------------------------------------------------------
+
+ def _loadFile (self, filename, table):
+
+ f = dbf.dbf (filename)
+
+ fieldnames = [(field [0]).lower () for field in f.fields]
+
+ for record in f:
+ yield dict (zip (fieldnames, record))
Property changes on: trunk/gnue-common/src/datasources/drivers/file/dbffile.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: trunk/gnue-common/src/datasources/drivers/file/inifile.py
===================================================================
--- trunk/gnue-common/src/datasources/drivers/file/inifile.py 2005-04-01
14:32:06 UTC (rev 7290)
+++ trunk/gnue-common/src/datasources/drivers/file/inifile.py 2005-04-04
21:50:47 UTC (rev 7291)
@@ -0,0 +1,223 @@
+# GNU Enterprise Common Library - INI File DB Driver
+#
+# Copyright 2000-2005 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
+# version 2, or (at your option) any later version.
+#
+# GNU Enterprise is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public
+# License along with program; see the file COPYING. If not,
+# write to the Free Software Foundation, Inc., 59 Temple Place
+# - Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+
+import ConfigParser
+
+from gnue.common.apps import errors
+from gnue.common.datasources.drivers.file import Base
+
+
+# =============================================================================
+# Exceptions
+# =============================================================================
+
+class DuplicateSectionError (errors.UserError):
+ def __init__ (self, section):
+ errors.UserError.__init__ (self, u_("Duplicate section name %s") % section)
+
+class MissingSectionError (errors.UserError):
+ def __init__ (self):
+ errors.UserError.__init__ (self, u_("Missing section name"))
+
+
+# =============================================================================
+# Connection class
+# =============================================================================
+
+class Connection (Base.Connection):
+ """
+ Connection class for INI file backends.
+ """
+
+ _primarykeyFields = ['_section_name']
+
+ # ---------------------------------------------------------------------------
+ # Constructor
+ # ---------------------------------------------------------------------------
+
+ def __init__ (self, *params, **kwargs):
+
+ Base.Connection.__init__ (self, *params, **kwargs)
+
+ self.__parsers = {} # ConfigParser objects per table
+ self.__dirty = {} # Dirty status per table
+
+
+ # ---------------------------------------------------------------------------
+ # Prepare the file
+ # ---------------------------------------------------------------------------
+
+ def __getParser (self, filename, table):
+
+ if not self.__parsers.has_key (table):
+ parser = ConfigParser.RawConfigParser ()
+ parser.read (filename)
+ self.__parsers [table] = parser
+ return self.__parsers [table]
+
+
+ # ---------------------------------------------------------------------------
+ # Iterate through the list of field names
+ # ---------------------------------------------------------------------------
+
+ def _listFields (self, filename, table):
+
+ parser = self.__getParser (filename, table)
+
+ fields = {}
+
+ for section in parser.sections ():
+ for fieldname in parser.options (section):
+ fields [fieldname] = {'id': fieldname,
+ 'name': fieldname,
+ 'type': 'field',
+ 'nativetype': 'text',
+ 'datatype': 'text',
+ 'required': False}
+ return fields.values ()
+
+
+ # ---------------------------------------------------------------------------
+ # Load the file
+ # ---------------------------------------------------------------------------
+
+ def _loadFile (self, filename, table):
+
+ parser = self.__getParser (filename, table)
+
+ for section in parser.sections ():
+ record = dict (parser.items (section))
+ record ['_section_name'] = section
+ yield record
+
+
+ # ---------------------------------------------------------------------------
+ # Set fields (used by _insert and _update)
+ # ---------------------------------------------------------------------------
+
+ def __setFields (self, parser, section, fields):
+
+ for (field, value) in fields.items ():
+ if field != '_section_name':
+ if value == '' or value is None:
+ parser.remove_option (section, field)
+ else:
+ parser.set (section, field, value)
+
+
+ # ---------------------------------------------------------------------------
+ # Insert new record
+ # ---------------------------------------------------------------------------
+
+ def _insert (self, table, newfields, recno):
+
+ parser = self.__getParser (self.getFilename (table), table)
+
+ section = newfields.get ('_section_name')
+
+ if section in parser.sections ():
+ raise DuplicateSectionError
+
+ if not section:
+ raise MissingSectionError
+
+ parser.add_section (section)
+
+ self.__setFields (parser, section, newfields)
+
+ self.__dirty [table] = True
+
+
+ # ---------------------------------------------------------------------------
+ # Update existing record
+ # ---------------------------------------------------------------------------
+
+ def _update (self, table, oldfields, newfields, recno):
+
+ parser = self.__getParser (self.getFilename (table), table)
+
+ section = oldfields.get ('_section_name')
+
+ # Handle section name change
+ if "_section_name" in newfields and newfields ["_section_name"] != section:
+
+ oldsection = section
+
+ section = newfields ['_section_name']
+
+ if section in parser.sections ():
+ raise DuplicateSectionError
+
+ if not section:
+ raise MissingSectionError
+
+ parser.add_section (section)
+
+ for option in parser.options (oldsection):
+ parser.set (section, option, parser.get (oldsection, option))
+
+ parser.remove_section (oldsection)
+
+ self.__setFields (parser, section, newfields)
+
+ self.__dirty [table] = True
+
+
+ # ---------------------------------------------------------------------------
+ # Delete record
+ # ---------------------------------------------------------------------------
+
+ def _delete (self, table, oldfields, recno):
+
+ parser = self.__getParser (self.getFilename (table), table)
+
+ section = oldfields.get ('_section_name')
+
+ dataCon.remove_section (section)
+
+ self.__dirty [table] = True
+
+
+ # ---------------------------------------------------------------------------
+ # Write changes back to the file
+ # ---------------------------------------------------------------------------
+
+ def _commit (self):
+
+ for table in self.__dirty.keys ():
+ f = open (self.getFilename (table), "w+")
+ (self.__parsers [table]).write (f)
+ f.close ()
+
+ self.__dirty = {}
+
+
+ # ---------------------------------------------------------------------------
+ # Undo changes
+ # ---------------------------------------------------------------------------
+
+ def _rollback (self):
+
+ # Just clean the cache; will cause each file to be re-read on next access
+ self.__parsers = {}
+ self.__dirty = {}
Property changes on: trunk/gnue-common/src/datasources/drivers/file/inifile.py
___________________________________________________________________
Name: svn:keywords
+ Id
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r7291 - in trunk/gnue-common/src/datasources/drivers: . Base file special,
reinhard <=