/[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 984 - (hide annotations)
Thu May 22 16:37:48 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: 11178 byte(s)
Implement a way to discover dependencies between tables and
shapestores.

* Thuban/Model/transientdb.py (TransientTableBase.Dependencies)
(TransientJoinedTable.Dependencies)
(AutoTransientTable.SimpleQuery): New. Implement the dependencies
interface
(TransientJoinedTable.__init__): Keep tack of the original table
objects in addition to the corresponding transient tables.

* Thuban/Model/table.py (DBFTable.Dependencies)
(MemoryTable.Dependencies): New. Implement the dependencies
interface

* Thuban/Model/data.py (ShapeTable): New. Helper class for
ShapefileStore
(ShapefileStore.__init__): Use ShapeTable instead of
AutoTransientTable
(ShapefileStore.Table, ShapefileStore.Shapefile): Add doc-strings
(ShapefileStore.FileName, ShapefileStore.FileType): New. Accessor
methods for filename and type
(ShapefileStore.Dependencies): New. Implement the dependencies
interface
(DerivedShapeStore): New class to replace SimpleStore. The main
difference to SimpleStore is that it depends not on a shapefile
but another shapestore which expresses the dependencies a bit
better
(SimpleStore.__init__): Add deprecation warning.

* test/test_dbf_table.py (TestDBFTable.test_dependencies): New.
Test for the Dependencies method.

* test/test_memory_table.py (TestMemoryTable.test_dependencies):
New. Test for the Dependencies method.

* test/test_transientdb.py
(TestTransientTable.test_auto_transient_table_dependencies): New.
Test for the Dependencies method.
(TestTransientTable.test_transient_joined_table): Add test for the
Dependencies method.

* test/test_session.py (TestSessionSimple.setUp)
(TestSessionSimple.tearDown): New. Implement a better way to
destroy the sessions.
(TestSessionSimple.test_initial_state)
(TestSessionSimple.test_add_table): Bind session to self.session
so that it's destroyed by tearDown
(TestSessionSimple.test_open_shapefile): New. Test for
OpenShapefile and the object it returns

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 839 import inspect
16     import warnings
17    
18 bh 6 import dbflib
19    
20     # the field types supported by a Table instance.
21 jonathan 474 FIELDTYPE_INT = "int"
22     FIELDTYPE_STRING = "string"
23     FIELDTYPE_DOUBLE = "double"
24 bh 6
25    
26     # map the dbflib constants for the field types to our constants
27     dbflib_fieldtypes = {dbflib.FTString: FIELDTYPE_STRING,
28     dbflib.FTInteger: FIELDTYPE_INT,
29     dbflib.FTDouble: FIELDTYPE_DOUBLE}
30    
31 jan 806
32 bh 818 class OldTableInterfaceMixin:
33 jan 806
34 bh 818 """Mixin to implement the old table interface using the new one"""
35 jan 806
36 bh 839 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 bh 818 def record_count(self):
44 bh 839 self.__deprecation_warning()
45 bh 818 return self.NumRows()
46 jan 806
47     def field_count(self):
48 bh 839 self.__deprecation_warning()
49 bh 818 return self.NumColumns()
50 jan 806
51 bh 818 def field_info(self, field):
52     """Return a tuple (type, name, width, prec) for the field no. field
53 jan 806
54 bh 818 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 bh 839 self.__deprecation_warning()
60 bh 818 col = self.Column(field)
61     return (col.type, col.name,
62     getattr(col, "width", 0), getattr(col, "prec", 0))
63 jan 806
64 bh 818 def field_info_by_name(self, col):
65 bh 839 self.__deprecation_warning()
66 bh 818 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 jan 806
73 bh 818 def field_range(self, fieldName):
74 bh 839 self.__deprecation_warning()
75 bh 818 min, max = self.ValueRange(fieldName)
76     return ((min, None), (max, None))
77 jan 806
78 bh 818 def GetUniqueValues(self, field):
79 bh 839 self.__deprecation_warning()
80 bh 818 return self.UniqueValues(field)
81 jan 806
82 bh 818 def read_record(self, r):
83 bh 839 self.__deprecation_warning()
84 bh 818 return self.ReadRowAsDict(r)
85 bh 6
86 bh 818
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 bh 6 """
101 bh 818
102     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 bh 765 Table interface for the data in a DBF file
114 bh 6 """
115    
116 bh 286 # Implementation strategy regarding writing to a DBF file:
117     #
118     # Most of the time Thuban only needs to read from a table and it is
119     # important that Thuban can work with read-only files. Therefore the
120     # 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
122 bh 590 # well. If that succeeds the read/write DBF file will be used for
123     # all IO afterwards.
124 bh 286 #
125     # 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
127     # returns the new values. With two separate objects this wouldn't
128     # work because a DBF file object buffers some data
129    
130 bh 6 def __init__(self, filename):
131     self.filename = filename
132 bh 284 self.dbf = dbflib.DBFFile(filename)
133 bh 6
134 bh 286 # If true, self.dbf is open for writing.
135     self._writable = 0
136    
137 bh 818 # Create the column information objects
138     self.columns = []
139     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 bh 257
149 bh 818 def NumRows(self):
150     """Return the number of rows in the table"""
151 bh 6 return self.dbf.record_count()
152    
153 bh 818 def NumColumns(self):
154     """Return the number of columns in the table"""
155 bh 6 return self.dbf.field_count()
156    
157 bh 818 def Columns(self):
158     """Return the table's colum definitions
159 bh 6
160 bh 818 The return value is a sequence of DBFColumn instances, one for
161     each column.
162 bh 6 """
163 bh 818 return self.columns
164 bh 6
165 bh 818 def Column(self, col):
166     """Return information about the column given by its name or index
167 jonathan 467
168 bh 818 The returned object is an instance of DBFColumn
169     """
170     return self.column_map[col]
171 jonathan 467
172 bh 839 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 bh 818 def ReadRowAsDict(self, row):
178     """Return the entire row as a dictionary with column names as keys"""
179     return self.dbf.read_record(row)
180 jonathan 467
181 bh 818 def ReadValue(self, row, col):
182     """Return the value of the specified row and column
183 jonathan 628
184 bh 818 The col parameter may be the index of the column or its name.
185     """
186     return self.dbf.read_record(row)[self.column_map[col].name]
187 jonathan 628
188 bh 818 def ValueRange(self, col):
189     """Return the minimum and maximum values of the values in the column
190 jonathan 628
191 bh 818 The return value is a tuple (min, max) unless the table is empty
192     in which case the return value is None.
193 jonathan 628 """
194 bh 818 count = self.NumRows()
195 jonathan 628
196     if count == 0:
197     return None
198    
199 bh 818 min = max = self.ReadValue(0, col)
200 jonathan 628 for i in range(1, count):
201 bh 818 value = self.ReadValue(i, col)
202     if value < min:
203     min = value
204     elif value > max:
205     max = value
206 jonathan 628
207 bh 818 return (min, max)
208 jonathan 628
209 bh 818 def UniqueValues(self, col):
210     """Return a sorted list of all unique values in the column col"""
211     dict = {}
212 jonathan 628
213 bh 818 for i in range(self.NumRows()):
214     value = self.ReadValue(i, col)
215     dict[value] = 0
216 jonathan 628
217 bh 818 values = dict.keys()
218     values.sort()
219     return values
220 jonathan 628
221 bh 984 def Dependencies(self):
222     """Return an empty sequence. The DBFTable doesn't depend on anything"""
223     return ()
224 jonathan 628
225 bh 818 # DBF specific interface parts.
226 jonathan 628
227 bh 818 def Destroy(self):
228     self.dbf.close()
229     self.dbf = None
230 jonathan 628
231 bh 274 def write_record(self, record, values):
232     """Write the values into the record
233    
234     The values parameter may either be a dictionary or a sequence.
235    
236     If it's a dictionary the keys must be the names of the fields
237     and their value must have a suitable type. Only the fields
238     actually contained in the dictionary are written. Fields for
239     which there's no item in the dict are not modified.
240    
241     If it's a sequence, all fields must be present in the right
242     order.
243     """
244 bh 286 if not self._writable:
245     new_dbf = dbflib.DBFFile(self.filename, "r+b")
246     self.dbf.close()
247     self.dbf = new_dbf
248     self._writable = 1
249     self.dbf.write_record(record, values)
250     self.dbf.commit()
251 jonathan 467
252 bh 765
253    
254 bh 818 class MemoryColumn:
255    
256     def __init__(self, name, type, index):
257     self.name = name
258     self.type = type
259     self.index = index
260    
261     class MemoryTable(OldTableInterfaceMixin):
262    
263     """Very simple table implementation that operates on a list of tuples"""
264    
265     def __init__(self, fields, data):
266     """Initialize the MemoryTable
267    
268     Parameters:
269     fields -- List of (name, field_type) pairs
270     data -- List of tuples, one for each row of data
271     """
272     self.data = data
273    
274     # Create the column information objects
275     self.columns = []
276     self.column_map = {}
277     for name, ftype in fields:
278     index = len(self.columns)
279     col = MemoryColumn(name, ftype, index)
280     self.columns.append(col)
281     self.column_map[name] = col
282     self.column_map[index] = col
283    
284     def NumColumns(self):
285     """Return the number of columns in the table"""
286     return len(self.columns)
287    
288     def Column(self, col):
289     """Return information about the column given by its name or index
290    
291     The returned object is an instance of MemoryColumn.
292     """
293     return self.column_map[col]
294    
295     def Columns(self):
296     """Return the table's colum definitions
297    
298     The return value is a sequence of MemoryColumn instances, one
299     for each column.
300     """
301     return self.columns
302    
303 bh 839 def HasColumn(self, col):
304     """Return whether the table has a column with the given name or index
305     """
306     return self.column_map.has_key(col)
307    
308 bh 818 def NumRows(self):
309     """Return the number of rows in the table"""
310     return len(self.data)
311    
312     def ReadValue(self, row, col):
313     """Return the value of the specified row and column
314    
315     The col parameter may be the index of the column or its name.
316     """
317     return self.data[row][self.column_map[col].index]
318    
319     def ReadRowAsDict(self, index):
320     """Return the entire row as a dictionary with column names as keys"""
321     return dict([(col.name, self.data[index][col.index])
322     for col in self.columns])
323    
324     def ValueRange(self, col):
325     """Return the minimum and maximum values of the values in the column
326    
327     The return value is a tuple (min, max) unless the table is empty
328     in which case the return value is None.
329     """
330    
331     index = self.column_map[col].index
332     values = [row[index] for row in self.data]
333     if not values:
334     return None
335    
336     return min(values), max(values)
337    
338     def UniqueValues(self, col):
339     """Return a sorted list of all unique values in the column col"""
340     dict = {}
341    
342     for i in range(self.NumRows()):
343     value = self.ReadValue(i, col)
344     dict[value] = 0
345    
346     values = dict.keys()
347     values.sort()
348     return values
349    
350 bh 984 def Dependencies(self):
351     """Return an empty sequence. The MemoryTable doesn't depend on anything
352     """
353     return ()
354 bh 818
355     def write_record(self, record, values):
356     # TODO: Check for correct lenght and perhaps also
357     # for correct types in case values is a tuple. How to report problems?
358     # TODO: Allow values to be a dictionary and write the single
359     # fields that are specified.
360     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