commit-gnue
[Top][All Lists]
Advanced

[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





reply via email to

[Prev in Thread] Current Thread [Next in Thread]