/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/table.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/table.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 628 by jonathan, Wed Apr 9 10:09:15 2003 UTC revision 1019 by jan, Fri May 23 12:56:34 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4    # Jan-Oliver Wagner <[email protected]>
5  #  #
6  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
7  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
# Line 11  Classes for handling tables of data. Line 12  Classes for handling tables of data.
12    
13  __version__ = "$Revision$"  __version__ = "$Revision$"
14    
15    import os
16    import inspect
17    import warnings
18    
19    from base import TitledObject
20    
21  import dbflib  import dbflib
22    
23  # the field types supported by a Table instance.  # the field types supported by a Table instance.
# Line 24  dbflib_fieldtypes = {dbflib.FTString: FI Line 31  dbflib_fieldtypes = {dbflib.FTString: FI
31                       dbflib.FTInteger: FIELDTYPE_INT,                       dbflib.FTInteger: FIELDTYPE_INT,
32                       dbflib.FTDouble: FIELDTYPE_DOUBLE}                       dbflib.FTDouble: FIELDTYPE_DOUBLE}
33    
 class Table:  
34    
35    class OldTableInterfaceMixin:
36    
37        """Mixin to implement the old table interface using the new one"""
38    
39        def __deprecation_warning(self):
40            """Issue a DeprecationWarning for code hat uses the old interface"""
41            callername = inspect.currentframe().f_back.f_code.co_name
42            warnings.warn("The %s method of the old table interface"
43                          " is deprecated" % callername,
44                          DeprecationWarning, stacklevel = 3)
45    
46        def record_count(self):
47            self.__deprecation_warning()
48            return self.NumRows()
49    
50        def field_count(self):
51            self.__deprecation_warning()
52            return self.NumColumns()
53    
54        def field_info(self, field):
55            """Return a tuple (type, name, width, prec) for the field no. field
56    
57            type is the data type of the field, name the name, width the
58            field width in characters and prec the decimal precision. width
59            and prec will be zero if the information returned by the Column
60            method doesn't provide values for them.
61            """
62            self.__deprecation_warning()
63            col = self.Column(field)
64            return (col.type, col.name,
65                   getattr(col, "width", 0), getattr(col, "prec", 0))
66    
67        def field_info_by_name(self, col):
68            self.__deprecation_warning()
69            try:
70                return self.field_info(col)
71            except KeyError:
72                # FIXME: It may be that field_info raises other exceptions
73                # when the name is not a valid column name.
74                return None
75    
76        def field_range(self, fieldName):
77            self.__deprecation_warning()
78            min, max = self.ValueRange(fieldName)
79            return ((min, None), (max, None))
80    
81        def GetUniqueValues(self, field):
82            self.__deprecation_warning()
83            return self.UniqueValues(field)
84    
85        def read_record(self, r):
86            self.__deprecation_warning()
87            return self.ReadRowAsDict(r)
88    
89    
90    
91    class DBFColumn:
92    
93        """Description of a column in a DBFTable
94    
95        Instances have the following public attributes:
96    
97        name -- Name of the column
98        type -- Type of the column (one of FIELDTYPE_STRING, FIELDTYPE_INT or\
99                FIELDTYPE_DOUBLE)
100        index -- The index of the column
101        width -- the width of the data in the column
102        prec -- The precision of the data (only valid for type == FIELDTYPE_DOUBLE)
103      """      """
     Represent a table of data.  
104    
105      Currently this is basically just a wrapper around dbflib.      def __init__(self, name, type, width, prec, index):
106            self.name = name
107            self.type = type
108            self.width = width
109            self.prec = prec
110            self.index = index
111    
112    
113    class DBFTable(TitledObject, OldTableInterfaceMixin):
114    
115        """
116        Table interface for the data in a DBF file
117      """      """
118    
119      # Implementation strategy regarding writing to a DBF file:      # Implementation strategy regarding writing to a DBF file:
# Line 48  class Table: Line 132  class Table:
132    
133      def __init__(self, filename):      def __init__(self, filename):
134          self.filename = filename          self.filename = filename
135            title = os.path.basename(self.filename)
136            TitledObject.__init__(self, title)
137          self.dbf = dbflib.DBFFile(filename)          self.dbf = dbflib.DBFFile(filename)
138    
139          # If true, self.dbf is open for writing.          # If true, self.dbf is open for writing.
140          self._writable = 0          self._writable = 0
141    
142      def Destroy(self):          # Create the column information objects
143          self.dbf.close()          self.columns = []
144          self.dbf = None          self.column_map = {}
145            for i in range(self.NumColumns()):
146                ftype, name, width, prec = self.dbf.field_info(i)
147                ftype = dbflib_fieldtypes[ftype]
148                index = len(self.columns)
149                col = DBFColumn(name, ftype, width, prec, index)
150                self.columns.append(col)
151                self.column_map[name] = col
152                self.column_map[index] = col
153    
154      def record_count(self):      def NumRows(self):
155          """Return the number of records"""          """Return the number of rows in the table"""
156          return self.dbf.record_count()          return self.dbf.record_count()
157    
158      def field_count(self):      def NumColumns(self):
159          """Return the number of fields in a record"""          """Return the number of columns in the table"""
160          return self.dbf.field_count()          return self.dbf.field_count()
161    
162      def field_info(self, field):      def Columns(self):
163          """Return a tuple (type, name, width, prec) for the field no. field          """Return the table's colum definitions
164    
165          type is the data type of the field, name the name, width the          The return value is a sequence of DBFColumn instances, one for
166          field width in characters and prec the decimal precision.          each column.
167          """          """
168          type, name, width, prec = self.dbf.field_info(field)          return self.columns
         type = dbflib_fieldtypes[type]  
         return type, name, width, prec  
   
     def field_info_by_name(self, fieldName):  
         count = self.field_count()  
169    
170          for i in range(count):      def Column(self, col):
171              info = self.field_info(i)          """Return information about the column given by its name or index
             if info[1] == fieldName:  
                 return info  
172    
173          return None          The returned object is an instance of DBFColumn
174            """
175      def field_range(self, fieldName):          return self.column_map[col]
         """Finds the first occurences of the minimum and maximum values  
         in the table for the given field.  
176    
177          This assumes that the standard comparison operators (<, >, etc.)      def HasColumn(self, col):
178          will work for the given data.          """Return whether the table has a column with the given name or index
179            """
180            return self.column_map.has_key(col)
181    
182          Returns a tuple ((min, rec), (max, rec)) where:      def ReadRowAsDict(self, row):
183              min is the minimum value          """Return the entire row as a dictionary with column names as keys"""
184              max is the maximum value          return self.dbf.read_record(row)
             rec is the record number where the value was found. One  
                 should check that the record number of min is not  
                 the same as the record number of max.  
185    
186          Returns None if there are no records      def ReadValue(self, row, col):
187            """Return the value of the specified row and column
188    
189            The col parameter may be the index of the column or its name.
190          """          """
191            return self.dbf.read_record(row)[self.column_map[col].name]
192    
193        def ValueRange(self, col):
194            """Return the minimum and maximum values of the values in the column
195    
196          count = self.record_count()          The return value is a tuple (min, max) unless the table is empty
197            in which case the return value is None.
198            """
199            count = self.NumRows()
200    
201          if count == 0:          if count == 0:
202              return None              return None
203    
204          rec = self.read_record(0)          min = max = self.ReadValue(0, col)
   
         min = rec[fieldName]  
         min_rec = 0  
   
         max = rec[fieldName]  
         max_rec = 0  
   
205          for i in range(1, count):          for i in range(1, count):
206              rec = self.read_record(i)              value = self.ReadValue(i, col)
207              data = rec[fieldName]              if value < min:
208                    min = value
209                elif value > max:
210                    max = value
211    
212              if data < min:          return (min, max)
                 min = data  
                 min_rec = rec  
             elif data > max:  
                 max = data  
                 max_rec = rec  
   
         return ((min, min_rec), (max, max_rec))  
   
     def GetUniqueValues(self, fieldName):  
         """Return a list of all unique entries in the table for the given  
         field name.  
         """  
213    
214        def UniqueValues(self, col):
215            """Return a sorted list of all unique values in the column col"""
216          dict = {}          dict = {}
217    
218          for i in range(0, self.record_count()):          for i in range(self.NumRows()):
219              rec = self.read_record(i)              value = self.ReadValue(i, col)
220              data = rec[fieldName]              dict[value] = 0
221    
222              if not dict.has_key(data):          values = dict.keys()
223                  dict[data] = 0          values.sort()
224            return values
225    
226        def Dependencies(self):
227            """Return an empty sequence. The DBFTable doesn't depend on anything"""
228            return ()
229    
230          return dict.keys()      # DBF specific interface parts.
231    
232      def read_record(self, record):      def Destroy(self):
233          """Return the record no. record as a dict mapping field names to values          self.dbf.close()
234          """          self.dbf = None
         return self.dbf.read_record(record)  
235    
236      def write_record(self, record, values):      def write_record(self, record, values):
237          """Write the values into the record          """Write the values into the record
# Line 172  class Table: Line 254  class Table:
254          self.dbf.write_record(record, values)          self.dbf.write_record(record, values)
255          self.dbf.commit()          self.dbf.commit()
256    
257        def FileName(self):
258            """Return the filename the DBFTable was instantiated with"""
259            return self.filename
260    
261    
262    class MemoryColumn:
263    
264        def __init__(self, name, type, index):
265            self.name = name
266            self.type = type
267            self.index = index
268    
269    class MemoryTable(TitledObject, OldTableInterfaceMixin):
270    
271        """Very simple table implementation that operates on a list of tuples"""
272    
273        def __init__(self, fields, data):
274            """Initialize the MemoryTable
275    
276            Parameters:
277            fields -- List of (name, field_type) pairs
278            data -- List of tuples, one for each row of data
279            """
280            self.data = data
281            title = 'MemoryTable'
282            TitledObject.__init__(self, title)
283    
284            # Create the column information objects
285            self.columns = []
286            self.column_map = {}
287            for name, ftype in fields:
288                index = len(self.columns)
289                col = MemoryColumn(name, ftype, index)
290                self.columns.append(col)
291                self.column_map[name] = col
292                self.column_map[index] = col
293    
294        def NumColumns(self):
295            """Return the number of columns in the table"""
296            return len(self.columns)
297    
298        def Column(self, col):
299            """Return information about the column given by its name or index
300    
301            The returned object is an instance of MemoryColumn.
302            """
303            return self.column_map[col]
304    
305        def Columns(self):
306            """Return the table's colum definitions
307    
308            The return value is a sequence of MemoryColumn instances, one
309            for each column.
310            """
311            return self.columns
312    
313        def HasColumn(self, col):
314            """Return whether the table has a column with the given name or index
315            """
316            return self.column_map.has_key(col)
317    
318        def NumRows(self):
319            """Return the number of rows in the table"""
320            return len(self.data)
321    
322        def ReadValue(self, row, col):
323            """Return the value of the specified row and column
324    
325            The col parameter may be the index of the column or its name.
326            """
327            return self.data[row][self.column_map[col].index]
328    
329        def ReadRowAsDict(self, index):
330            """Return the entire row as a dictionary with column names as keys"""
331            return dict([(col.name, self.data[index][col.index])
332                          for col in self.columns])
333    
334        def ValueRange(self, col):
335            """Return the minimum and maximum values of the values in the column
336    
337            The return value is a tuple (min, max) unless the table is empty
338            in which case the return value is None.
339            """
340    
341            index = self.column_map[col].index
342            values = [row[index] for row in self.data]
343            if not values:
344                return None
345    
346            return min(values), max(values)
347    
348        def UniqueValues(self, col):
349            """Return a sorted list of all unique values in the column col"""
350            dict = {}
351    
352            for i in range(self.NumRows()):
353                value = self.ReadValue(i, col)
354                dict[value] = 0
355    
356            values = dict.keys()
357            values.sort()
358            return values
359    
360        def Dependencies(self):
361            """Return an empty sequence. The MemoryTable doesn't depend on anything
362            """
363            return ()
364    
365        def write_record(self, record, values):
366            # TODO: Check for correct lenght and perhaps also
367            # for correct types in case values is a tuple. How to report problems?
368            # TODO: Allow values to be a dictionary and write the single
369            # fields that are specified.
370            self.data[record] = values

Legend:
Removed from v.628  
changed lines
  Added in v.1019

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26