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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 818 - (hide annotations)
Mon May 5 17:18:31 2003 UTC (21 years, 10 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/table.py
File MIME type: text/x-python
File size: 9992 byte(s)
Convert the table implementations to a new table interface. All
tables use a common mixin class to provide backwards compatibility
until all table users have been updated.

* Thuban/Model/table.py (OldTableInterfaceMixin): Mixin class to
provide backwards compatibility for table classes implementing the
new interface
(DBFTable, MemoryTable): Implement the new table interface. Use
OldTableInterfaceMixin as base for compatibility
(DBFColumn, MemoryColumn): New. Column description for DBFTable
and MemoryTable resp.

* test/test_dbf_table.py: New. Test cases for the DBFTable with
the new table interface.

* test/test_memory_table.py: New. Test cases for the MemoryTable
with the new table interface.

* test/test_table.py: Document the all tests in this file as only
for backwards compatibility. The equivalent tests for the new
interface are in test_memory_table.py and test_dbf_table.py
(MemoryTableTest.test_read): field_info should be returning tuples
with four items
(MemoryTableTest.test_write): Make doc-string a more precise.

* Thuban/Model/transientdb.py (TransientTableBase): Convert to new
table interface. Derive from from OldTableInterfaceMixin for
compatibility.
(TransientTableBase.create): New intance variable column_map to
map from names and indices to column objects
(TransientTable.create): Use the new table interface of the input
table
(AutoTransientTable): Convert to new table interface. Derive from
from OldTableInterfaceMixin for compatibility.
(AutoTransientTable.write_record): Removed. It's not implemented
yet and we still have to decide how to handle writing with the new
table and data framework.

* test/test_transientdb.py
(TestTransientTable.run_iceland_political_tests)
(TestTransientTable.test_transient_joined_table): Use the new
table interface

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

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26