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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 998 - (show annotations)
Thu May 22 19:29:39 2003 UTC (21 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/table.py
File MIME type: text/x-python
File size: 11656 byte(s)
Give the tables titles so that the GUI can display more meaningful
names. For now the titles are fixed but depend on e.g. filenames
or the titles of the joined tables.

* Thuban/Model/transientdb.py (TransientTable.Title)
(TransientJoinedTable.Title, AutoTransientTable.Title): New.

* Thuban/Model/table.py (DBFTable.Title, MemoryTable.Title): New.

* test/test_transientdb.py
(TestTransientTable.test_auto_transient_table_title): New. Test
for the Title method
(TestTransientTable.test_transient_joined_table)
(TestTransientTable.test_transient_table): Add test for the Title
methods

* test/test_memory_table.py (TestMemoryTable.test_title): New.
Test for the Title method

* test/test_dbf_table.py (TestDBFTable.test_title): New. Test for
the Title method

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