/[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 6 by bh, Tue Aug 28 15:41:52 2001 UTC revision 839 by bh, Tue May 6 15:54:18 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001 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 inspect
16    import warnings
17    
18  import dbflib  import dbflib
19    
20  # the field types supported by a Table instance.  # the field types supported by a Table instance.
# Line 24  dbflib_fieldtypes = {dbflib.FTString: FI Line 28  dbflib_fieldtypes = {dbflib.FTString: FI
28                       dbflib.FTInteger: FIELDTYPE_INT,                       dbflib.FTInteger: FIELDTYPE_INT,
29                       dbflib.FTDouble: FIELDTYPE_DOUBLE}                       dbflib.FTDouble: FIELDTYPE_DOUBLE}
30    
 class Table:  
31    
32    class OldTableInterfaceMixin:
33    
34        """Mixin to implement the old table interface using the new one"""
35    
36        def __deprecation_warning(self):
37            """Issue a DeprecationWarning for code hat uses the old interface"""
38            callername = inspect.currentframe().f_back.f_code.co_name
39            warnings.warn("The %s method of the old table interface"
40                          " is deprecated" % callername,
41                          DeprecationWarning, stacklevel = 3)
42    
43        def record_count(self):
44            self.__deprecation_warning()
45            return self.NumRows()
46    
47        def field_count(self):
48            self.__deprecation_warning()
49            return self.NumColumns()
50    
51        def field_info(self, field):
52            """Return a tuple (type, name, width, prec) for the field no. field
53    
54            type is the data type of the field, name the name, width the
55            field width in characters and prec the decimal precision. width
56            and prec will be zero if the information returned by the Column
57            method doesn't provide values for them.
58            """
59            self.__deprecation_warning()
60            col = self.Column(field)
61            return (col.type, col.name,
62                   getattr(col, "width", 0), getattr(col, "prec", 0))
63    
64        def field_info_by_name(self, col):
65            self.__deprecation_warning()
66            try:
67                return self.field_info(col)
68            except KeyError:
69                # FIXME: It may be that field_info raises other exceptions
70                # when the name is not a valid column name.
71                return None
72    
73        def field_range(self, fieldName):
74            self.__deprecation_warning()
75            min, max = self.ValueRange(fieldName)
76            return ((min, None), (max, None))
77    
78        def GetUniqueValues(self, field):
79            self.__deprecation_warning()
80            return self.UniqueValues(field)
81    
82        def read_record(self, r):
83            self.__deprecation_warning()
84            return self.ReadRowAsDict(r)
85    
86    
87    
88    class DBFColumn:
89    
90        """Description of a column in a DBFTable
91    
92        Instances have the following public attributes:
93    
94        name -- Name of the column
95        type -- Type of the column (one of FIELDTYPE_STRING, FIELDTYPE_INT or\
96                FIELDTYPE_DOUBLE)
97        index -- The index of the column
98        width -- the width of the data in the column
99        prec -- The precision of the data (only valid for type == FIELDTYPE_DOUBLE)
100      """      """
     Represent a table of data.  
101    
102      Currently this is basically just a wrapper around dbflib.      def __init__(self, name, type, width, prec, index):
103            self.name = name
104            self.type = type
105            self.width = width
106            self.prec = prec
107            self.index = index
108    
109    
110    class DBFTable(OldTableInterfaceMixin):
111    
112      """      """
113        Table interface for the data in a DBF file
114        """
115    
116        # Implementation strategy regarding writing to a DBF file:
117        #
118        # Most of the time Thuban only needs to read from a table and it is
119        # important that Thuban can work with read-only files. Therefore the
120        # DBF file is opened only for reading initially. Only when
121        # write_record is called we try to open the DBF file for writing as
122        # well. If that succeeds the read/write DBF file will be used for
123        # all IO afterwards.
124        #
125        # It's important to use the same DBF file object for both reading
126        # and writing to make sure that reading a records after writing
127        # returns the new values. With two separate objects this wouldn't
128        # work because a DBF file object buffers some data
129    
130      def __init__(self, filename):      def __init__(self, filename):
131          self.filename = filename          self.filename = filename
132          self.dbf = dbflib.DBFFile(filename)          self.dbf = dbflib.DBFFile(filename)
133    
134      def record_count(self):          # If true, self.dbf is open for writing.
135          """Return the number of records"""          self._writable = 0
136    
137            # Create the column information objects
138            self.columns = []
139            self.column_map = {}
140            for i in range(self.NumColumns()):
141                ftype, name, width, prec = self.dbf.field_info(i)
142                ftype = dbflib_fieldtypes[ftype]
143                index = len(self.columns)
144                col = DBFColumn(name, ftype, width, prec, index)
145                self.columns.append(col)
146                self.column_map[name] = col
147                self.column_map[index] = col
148    
149        def NumRows(self):
150            """Return the number of rows in the table"""
151          return self.dbf.record_count()          return self.dbf.record_count()
152    
153      def field_count(self):      def NumColumns(self):
154          """Return the number of fields in a record"""          """Return the number of columns in the table"""
155          return self.dbf.field_count()          return self.dbf.field_count()
156    
157      def field_info(self, field):      def Columns(self):
158          """Return a tuple (type, name, width, prec) for the field no. field          """Return the table's colum definitions
159    
160          type is the data type of the field, name the name, width the          The return value is a sequence of DBFColumn instances, one for
161          field width in characters and prec the decimal precision.          each column.
162            """
163            return self.columns
164    
165        def Column(self, col):
166            """Return information about the column given by its name or index
167    
168            The returned object is an instance of DBFColumn
169          """          """
170          type, name, width, prec = self.dbf.field_info(field)          return self.column_map[col]
171          type = dbflib_fieldtypes[type]  
172          return type, name, width, prec      def HasColumn(self, col):
173            """Return whether the table has a column with the given name or index
174            """
175            return self.column_map.has_key(col)
176    
177        def ReadRowAsDict(self, row):
178            """Return the entire row as a dictionary with column names as keys"""
179            return self.dbf.read_record(row)
180    
181        def ReadValue(self, row, col):
182            """Return the value of the specified row and column
183    
184            The col parameter may be the index of the column or its name.
185            """
186            return self.dbf.read_record(row)[self.column_map[col].name]
187    
188        def ValueRange(self, col):
189            """Return the minimum and maximum values of the values in the column
190    
191            The return value is a tuple (min, max) unless the table is empty
192            in which case the return value is None.
193            """
194            count = self.NumRows()
195    
196            if count == 0:
197                return None
198    
199            min = max = self.ReadValue(0, col)
200            for i in range(1, count):
201                value = self.ReadValue(i, col)
202                if value < min:
203                    min = value
204                elif value > max:
205                    max = value
206    
207            return (min, max)
208    
209        def UniqueValues(self, col):
210            """Return a sorted list of all unique values in the column col"""
211            dict = {}
212    
213            for i in range(self.NumRows()):
214                value = self.ReadValue(i, col)
215                dict[value] = 0
216    
217            values = dict.keys()
218            values.sort()
219            return values
220    
221    
222        # DBF specific interface parts.
223    
224        def Destroy(self):
225            self.dbf.close()
226            self.dbf = None
227    
228        def write_record(self, record, values):
229            """Write the values into the record
230    
231            The values parameter may either be a dictionary or a sequence.
232    
233            If it's a dictionary the keys must be the names of the fields
234            and their value must have a suitable type. Only the fields
235            actually contained in the dictionary are written. Fields for
236            which there's no item in the dict are not modified.
237    
238            If it's a sequence, all fields must be present in the right
239            order.
240            """
241            if not self._writable:
242                new_dbf = dbflib.DBFFile(self.filename, "r+b")
243                self.dbf.close()
244                self.dbf = new_dbf
245                self._writable = 1
246            self.dbf.write_record(record, values)
247            self.dbf.commit()
248    
249    
250    
251    # Temporary backwards compatibility
252    Table = DBFTable
253    
254    
255    
256    class MemoryColumn:
257    
258        def __init__(self, name, type, index):
259            self.name = name
260            self.type = type
261            self.index = index
262    
263    class MemoryTable(OldTableInterfaceMixin):
264    
265        """Very simple table implementation that operates on a list of tuples"""
266    
267        def __init__(self, fields, data):
268            """Initialize the MemoryTable
269    
270            Parameters:
271            fields -- List of (name, field_type) pairs
272            data -- List of tuples, one for each row of data
273            """
274            self.data = data
275    
276            # Create the column information objects
277            self.columns = []
278            self.column_map = {}
279            for name, ftype in fields:
280                index = len(self.columns)
281                col = MemoryColumn(name, ftype, index)
282                self.columns.append(col)
283                self.column_map[name] = col
284                self.column_map[index] = col
285    
286        def NumColumns(self):
287            """Return the number of columns in the table"""
288            return len(self.columns)
289    
290        def Column(self, col):
291            """Return information about the column given by its name or index
292    
293            The returned object is an instance of MemoryColumn.
294            """
295            return self.column_map[col]
296    
297        def Columns(self):
298            """Return the table's colum definitions
299    
300            The return value is a sequence of MemoryColumn instances, one
301            for each column.
302            """
303            return self.columns
304    
305        def HasColumn(self, col):
306            """Return whether the table has a column with the given name or index
307            """
308            return self.column_map.has_key(col)
309    
310        def NumRows(self):
311            """Return the number of rows in the table"""
312            return len(self.data)
313    
314        def ReadValue(self, row, col):
315            """Return the value of the specified row and column
316    
317            The col parameter may be the index of the column or its name.
318            """
319            return self.data[row][self.column_map[col].index]
320    
321        def ReadRowAsDict(self, index):
322            """Return the entire row as a dictionary with column names as keys"""
323            return dict([(col.name, self.data[index][col.index])
324                          for col in self.columns])
325    
326        def ValueRange(self, col):
327            """Return the minimum and maximum values of the values in the column
328    
329      def read_record(self, record):          The return value is a tuple (min, max) unless the table is empty
330          """Return the record no. record as a dict mapping field names to values          in which case the return value is None.
331          """          """
         return self.dbf.read_record(record)  
332    
333                index = self.column_map[col].index
334            values = [row[index] for row in self.data]
335            if not values:
336                return None
337    
338            return min(values), max(values)
339    
340        def UniqueValues(self, col):
341            """Return a sorted list of all unique values in the column col"""
342            dict = {}
343    
344            for i in range(self.NumRows()):
345                value = self.ReadValue(i, col)
346                dict[value] = 0
347    
348            values = dict.keys()
349            values.sort()
350            return values
351    
352    
353        def write_record(self, record, values):
354            # TODO: Check for correct lenght and perhaps also
355            # for correct types in case values is a tuple. How to report problems?
356            # TODO: Allow values to be a dictionary and write the single
357            # fields that are specified.
358            self.data[record] = values

Legend:
Removed from v.6  
changed lines
  Added in v.839

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26