/[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 467 by jonathan, Wed Mar 5 18:18:38 2003 UTC revision 994 by bh, Thu May 22 18:05:16 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002 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
 from Thuban.common import *  
19    
20  # the field types supported by a Table instance.  # the field types supported by a Table instance.
21  #FIELDTYPE_INT = "int"  FIELDTYPE_INT = "int"
22  #FIELDTYPE_STRING = "string"  FIELDTYPE_STRING = "string"
23  #FIELDTYPE_DOUBLE = "double"  FIELDTYPE_DOUBLE = "double"
24    
25    
26  # map the dbflib constants for the field types to our constants  # map the dbflib constants for the field types to our constants
# Line 25  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:      # Implementation strategy regarding writing to a DBF file:
# Line 39  class Table: Line 119  class Table:
119      # important that Thuban can work with read-only files. Therefore the      # important that Thuban can work with read-only files. Therefore the
120      # DBF file is opened only for reading initially. Only when      # 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      # write_record is called we try to open the DBF file for writing as
122      # well. If that succeeds the dbf read/write DBF file will be used      # well. If that succeeds the read/write DBF file will be used for
123      # for all IO afterwards.      # all IO afterwards.
124      #      #
125      # It's important to use the same DBF file object for both reading      # 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      # and writing to make sure that reading a records after writing
# Line 54  class Table: Line 134  class Table:
134          # If true, self.dbf is open for writing.          # If true, self.dbf is open for writing.
135          self._writable = 0          self._writable = 0
136    
137      def Destroy(self):          # Create the column information objects
138          self.dbf.close()          self.columns = []
139          self.dbf = None          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 record_count(self):      def NumRows(self):
150          """Return the number of records"""          """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          type, name, width, prec = self.dbf.field_info(field)          return self.columns
164          type = dbflib_fieldtypes[type]  
165          return type, name, width, prec      def Column(self, col):
166            """Return information about the column given by its name or index
167    
168      def field_info_by_name(self, fieldName):          The returned object is an instance of DBFColumn
169          count = self.field_count()          """
170            return self.column_map[col]
171    
172        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          for i in range(count):      def ReadRowAsDict(self, row):
178              info = self.field_info(i)          """Return the entire row as a dictionary with column names as keys"""
179              if info[1] == fieldName:          return self.dbf.read_record(row)
                 return info  
180    
181          return None      def ReadValue(self, row, col):
182            """Return the value of the specified row and column
183    
184      def read_record(self, record):          The col parameter may be the index of the column or its name.
         """Return the record no. record as a dict mapping field names to values  
185          """          """
186          return self.dbf.read_record(record)          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        def Dependencies(self):
222            """Return an empty sequence. The DBFTable doesn't depend on anything"""
223            return ()
224    
225        # DBF specific interface parts.
226    
227        def Destroy(self):
228            self.dbf.close()
229            self.dbf = None
230    
231      def write_record(self, record, values):      def write_record(self, record, values):
232          """Write the values into the record          """Write the values into the record
# Line 112  class Table: Line 249  class Table:
249          self.dbf.write_record(record, values)          self.dbf.write_record(record, values)
250          self.dbf.commit()          self.dbf.commit()
251    
252        def FileName(self):
253            """Return the filename the DBFTable was instantiated with"""
254            return self.filename
255    
256    
257    class MemoryColumn:
258    
259        def __init__(self, name, type, index):
260            self.name = name
261            self.type = type
262            self.index = index
263    
264    class MemoryTable(OldTableInterfaceMixin):
265    
266        """Very simple table implementation that operates on a list of tuples"""
267    
268        def __init__(self, fields, data):
269            """Initialize the MemoryTable
270    
271            Parameters:
272            fields -- List of (name, field_type) pairs
273            data -- List of tuples, one for each row of data
274            """
275            self.data = data
276    
277            # Create the column information objects
278            self.columns = []
279            self.column_map = {}
280            for name, ftype in fields:
281                index = len(self.columns)
282                col = MemoryColumn(name, ftype, index)
283                self.columns.append(col)
284                self.column_map[name] = col
285                self.column_map[index] = col
286    
287        def NumColumns(self):
288            """Return the number of columns in the table"""
289            return len(self.columns)
290    
291        def Column(self, col):
292            """Return information about the column given by its name or index
293    
294            The returned object is an instance of MemoryColumn.
295            """
296            return self.column_map[col]
297    
298        def Columns(self):
299            """Return the table's colum definitions
300    
301            The return value is a sequence of MemoryColumn instances, one
302            for each column.
303            """
304            return self.columns
305    
306        def HasColumn(self, col):
307            """Return whether the table has a column with the given name or index
308            """
309            return self.column_map.has_key(col)
310    
311        def NumRows(self):
312            """Return the number of rows in the table"""
313            return len(self.data)
314    
315        def ReadValue(self, row, col):
316            """Return the value of the specified row and column
317    
318            The col parameter may be the index of the column or its name.
319            """
320            return self.data[row][self.column_map[col].index]
321    
322        def ReadRowAsDict(self, index):
323            """Return the entire row as a dictionary with column names as keys"""
324            return dict([(col.name, self.data[index][col.index])
325                          for col in self.columns])
326    
327        def ValueRange(self, col):
328            """Return the minimum and maximum values of the values in the column
329    
330            The return value is a tuple (min, max) unless the table is empty
331            in which case the return value is None.
332            """
333    
334            index = self.column_map[col].index
335            values = [row[index] for row in self.data]
336            if not values:
337                return None
338    
339            return min(values), max(values)
340    
341        def UniqueValues(self, col):
342            """Return a sorted list of all unique values in the column col"""
343            dict = {}
344    
345            for i in range(self.NumRows()):
346                value = self.ReadValue(i, col)
347                dict[value] = 0
348    
349            values = dict.keys()
350            values.sort()
351            return values
352    
353        def Dependencies(self):
354            """Return an empty sequence. The MemoryTable doesn't depend on anything
355            """
356            return ()
357    
358        def write_record(self, record, values):
359            # TODO: Check for correct lenght and perhaps also
360            # for correct types in case values is a tuple. How to report problems?
361            # TODO: Allow values to be a dictionary and write the single
362            # fields that are specified.
363            self.data[record] = values

Legend:
Removed from v.467  
changed lines
  Added in v.994

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26