/[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 998 - (hide 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 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 bh 998 import os
16 bh 839 import inspect
17     import warnings
18    
19 bh 6 import dbflib
20    
21     # the field types supported by a Table instance.
22 jonathan 474 FIELDTYPE_INT = "int"
23     FIELDTYPE_STRING = "string"
24     FIELDTYPE_DOUBLE = "double"
25 bh 6
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 jan 806
33 bh 818 class OldTableInterfaceMixin:
34 jan 806
35 bh 818 """Mixin to implement the old table interface using the new one"""
36 jan 806
37 bh 839 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 bh 818 def record_count(self):
45 bh 839 self.__deprecation_warning()
46 bh 818 return self.NumRows()
47 jan 806
48     def field_count(self):
49 bh 839 self.__deprecation_warning()
50 bh 818 return self.NumColumns()
51 jan 806
52 bh 818 def field_info(self, field):
53     """Return a tuple (type, name, width, prec) for the field no. field
54 jan 806
55 bh 818 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 bh 839 self.__deprecation_warning()
61 bh 818 col = self.Column(field)
62     return (col.type, col.name,
63     getattr(col, "width", 0), getattr(col, "prec", 0))
64 jan 806
65 bh 818 def field_info_by_name(self, col):
66 bh 839 self.__deprecation_warning()
67 bh 818 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 jan 806
74 bh 818 def field_range(self, fieldName):
75 bh 839 self.__deprecation_warning()
76 bh 818 min, max = self.ValueRange(fieldName)
77     return ((min, None), (max, None))
78 jan 806
79 bh 818 def GetUniqueValues(self, field):
80 bh 839 self.__deprecation_warning()
81 bh 818 return self.UniqueValues(field)
82 jan 806
83 bh 818 def read_record(self, r):
84 bh 839 self.__deprecation_warning()
85 bh 818 return self.ReadRowAsDict(r)
86 bh 6
87 bh 818
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 bh 6 """
102 bh 818
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 bh 765 Table interface for the data in a DBF file
115 bh 6 """
116    
117 bh 286 # 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 bh 590 # well. If that succeeds the read/write DBF file will be used for
124     # all IO afterwards.
125 bh 286 #
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 bh 6 def __init__(self, filename):
132     self.filename = filename
133 bh 284 self.dbf = dbflib.DBFFile(filename)
134 bh 6
135 bh 286 # If true, self.dbf is open for writing.
136     self._writable = 0
137    
138 bh 818 # 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 bh 257
150 bh 998 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 bh 818 def NumRows(self):
158     """Return the number of rows in the table"""
159 bh 6 return self.dbf.record_count()
160    
161 bh 818 def NumColumns(self):
162     """Return the number of columns in the table"""
163 bh 6 return self.dbf.field_count()
164    
165 bh 818 def Columns(self):
166     """Return the table's colum definitions
167 bh 6
168 bh 818 The return value is a sequence of DBFColumn instances, one for
169     each column.
170 bh 6 """
171 bh 818 return self.columns
172 bh 6
173 bh 818 def Column(self, col):
174     """Return information about the column given by its name or index
175 jonathan 467
176 bh 818 The returned object is an instance of DBFColumn
177     """
178     return self.column_map[col]
179 jonathan 467
180 bh 839 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 bh 818 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 jonathan 467
189 bh 818 def ReadValue(self, row, col):
190     """Return the value of the specified row and column
191 jonathan 628
192 bh 818 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 jonathan 628
196 bh 818 def ValueRange(self, col):
197     """Return the minimum and maximum values of the values in the column
198 jonathan 628
199 bh 818 The return value is a tuple (min, max) unless the table is empty
200     in which case the return value is None.
201 jonathan 628 """
202 bh 818 count = self.NumRows()
203 jonathan 628
204     if count == 0:
205     return None
206    
207 bh 818 min = max = self.ReadValue(0, col)
208 jonathan 628 for i in range(1, count):
209 bh 818 value = self.ReadValue(i, col)
210     if value < min:
211     min = value
212     elif value > max:
213     max = value
214 jonathan 628
215 bh 818 return (min, max)
216 jonathan 628
217 bh 818 def UniqueValues(self, col):
218     """Return a sorted list of all unique values in the column col"""
219     dict = {}
220 jonathan 628
221 bh 818 for i in range(self.NumRows()):
222     value = self.ReadValue(i, col)
223     dict[value] = 0
224 jonathan 628
225 bh 818 values = dict.keys()
226     values.sort()
227     return values
228 jonathan 628
229 bh 984 def Dependencies(self):
230     """Return an empty sequence. The DBFTable doesn't depend on anything"""
231     return ()
232 jonathan 628
233 bh 818 # DBF specific interface parts.
234 jonathan 628
235 bh 818 def Destroy(self):
236     self.dbf.close()
237     self.dbf = None
238 jonathan 628
239 bh 274 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 bh 286 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 jonathan 467
260 bh 994 def FileName(self):
261     """Return the filename the DBFTable was instantiated with"""
262     return self.filename
263 bh 765
264    
265 bh 818 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 bh 998 def Title(self):
296     """Return 'MemoryTable'
297    
298     Override in derived classes to have a more meaningful title.
299     """
300     return "MemoryTable"
301    
302 bh 818 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 bh 839 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 bh 818 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 bh 984 def Dependencies(self):
369     """Return an empty sequence. The MemoryTable doesn't depend on anything
370     """
371     return ()
372 bh 818
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