/[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 1019 - (show annotations)
Fri May 23 12:56:34 2003 UTC (21 years, 9 months ago) by jan
Original Path: trunk/thuban/Thuban/Model/table.py
File MIME type: text/x-python
File size: 11533 byte(s)
(DBFTable, MemoryTable): mix-in TitledObject and call its init-method with
a default title. Remove 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 from base import TitledObject
20
21 import dbflib
22
23 # the field types supported by a Table instance.
24 FIELDTYPE_INT = "int"
25 FIELDTYPE_STRING = "string"
26 FIELDTYPE_DOUBLE = "double"
27
28
29 # map the dbflib constants for the field types to our constants
30 dbflib_fieldtypes = {dbflib.FTString: FIELDTYPE_STRING,
31 dbflib.FTInteger: FIELDTYPE_INT,
32 dbflib.FTDouble: FIELDTYPE_DOUBLE}
33
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 """
104
105 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:
120 #
121 # Most of the time Thuban only needs to read from a table and it is
122 # important that Thuban can work with read-only files. Therefore the
123 # DBF file is opened only for reading initially. Only when
124 # write_record is called we try to open the DBF file for writing as
125 # well. If that succeeds the read/write DBF file will be used for
126 # all IO afterwards.
127 #
128 # It's important to use the same DBF file object for both reading
129 # and writing to make sure that reading a records after writing
130 # returns the new values. With two separate objects this wouldn't
131 # work because a DBF file object buffers some data
132
133 def __init__(self, filename):
134 self.filename = filename
135 title = os.path.basename(self.filename)
136 TitledObject.__init__(self, title)
137 self.dbf = dbflib.DBFFile(filename)
138
139 # If true, self.dbf is open for writing.
140 self._writable = 0
141
142 # Create the column information objects
143 self.columns = []
144 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 NumRows(self):
155 """Return the number of rows in the table"""
156 return self.dbf.record_count()
157
158 def NumColumns(self):
159 """Return the number of columns in the table"""
160 return self.dbf.field_count()
161
162 def Columns(self):
163 """Return the table's colum definitions
164
165 The return value is a sequence of DBFColumn instances, one for
166 each column.
167 """
168 return self.columns
169
170 def Column(self, col):
171 """Return information about the column given by its name or index
172
173 The returned object is an instance of DBFColumn
174 """
175 return self.column_map[col]
176
177 def HasColumn(self, col):
178 """Return whether the table has a column with the given name or index
179 """
180 return self.column_map.has_key(col)
181
182 def ReadRowAsDict(self, row):
183 """Return the entire row as a dictionary with column names as keys"""
184 return self.dbf.read_record(row)
185
186 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 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:
202 return None
203
204 min = max = self.ReadValue(0, col)
205 for i in range(1, count):
206 value = self.ReadValue(i, col)
207 if value < min:
208 min = value
209 elif value > max:
210 max = value
211
212 return (min, max)
213
214 def UniqueValues(self, col):
215 """Return a sorted list of all unique values in the column col"""
216 dict = {}
217
218 for i in range(self.NumRows()):
219 value = self.ReadValue(i, col)
220 dict[value] = 0
221
222 values = dict.keys()
223 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 # DBF specific interface parts.
231
232 def Destroy(self):
233 self.dbf.close()
234 self.dbf = None
235
236 def write_record(self, record, values):
237 """Write the values into the record
238
239 The values parameter may either be a dictionary or a sequence.
240
241 If it's a dictionary the keys must be the names of the fields
242 and their value must have a suitable type. Only the fields
243 actually contained in the dictionary are written. Fields for
244 which there's no item in the dict are not modified.
245
246 If it's a sequence, all fields must be present in the right
247 order.
248 """
249 if not self._writable:
250 new_dbf = dbflib.DBFFile(self.filename, "r+b")
251 self.dbf.close()
252 self.dbf = new_dbf
253 self._writable = 1
254 self.dbf.write_record(record, values)
255 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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26