Changeset 4221 for waeup/branches/ulif-rewrite/src
- Timestamp:
- 8 Jun 2009, 16:56:05 (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
waeup/branches/ulif-rewrite/src/waeup/utils/importexport.txt
r4159 r4221 149 149 data types like faculties, students, and the like. 150 150 151 Obtaining importers 152 ------------------- 153 154 Before we actually do real imports, we have to provide so-called 155 importers, that will do the job for us. Let's see how that works. 156 157 We first create an object into that we can import something: 158 159 >>> import grok 160 >>> from waeup.interfaces import IWAeUPObject 161 >>> class MyContainer(grok.Container): 162 ... #grok.implements(IWAeUPObject) 163 ... def foo(self): 164 ... print "Boo!" 165 166 We get a suitable importer for certain objects by asking for an 167 adapter to `IWAeUPCSVImporter`: 168 151 For our purposes we define CSV imports as tuples, containing a CSV 152 source and a CSV receiver: 153 154 ``CSV Import := <CSVSource, CSVReceiver>`` 155 156 CSV sources 157 ----------- 158 159 CSV sources build a plugin framework inside the WAeUP portal (that 160 might be factored out one day). See `waeup.csvfile` to learn more 161 about that. 162 163 A `waeup.csvfile.CSVFile` plugin cares for a special kind of CSV file. 164 165 To make clear what that means, we start by creating a simple CSV 166 file. 167 168 CSVFile objects are abstractions of simple .csv-files. But as not 169 every CSV file contains the same kind of data, we can create different 170 flavours of it, for instance to import departments or faculties. As 171 each kind of import needs different kind of data, we have different 172 kinds of CSV sources. 173 174 CSV sources can check their associated files for consistency, 175 importability, etc. 176 177 We create a simple CSV file with cave data: 178 179 >>> open('mycavedata.csv', 'wb').write( 180 ... """size,owner 181 ... 42,Manfred 182 ... """) 183 184 We have a very simple CSVFile available, which is registered as an 185 adapter for strings. To make this work, the string should be a path to 186 a file: 187 188 >>> from waeup.csvfile import getCSVFile 189 >>> mysource = getCSVFile('mycavedata.csv') 190 >>> mysource 191 <waeup.csvfile.csvfile.CSVFile object at 0x...> 192 193 We define caves like this: 194 195 >>> class Cave(object): 196 ... def __init__(self, size, owner): 197 ... self.size = size 198 ... self.owner = owner 199 ... def __repr__(self): 200 ... return '<Cave object [size: %s, owner: %s]>' % ( 201 ... self.size, self.owner) 202 203 Let's assume, we have a container for caves: 204 205 >>> from zope.interface import Interface 206 >>> class ICaveContainer(Interface): 207 ... """A container for caves.""" 208 209 >>> class CaveContainer(object): 210 ... grok.implements(ICaveContainer) 211 ... caves = [] 212 ... def addCave(self, cave): 213 ... self.caves.append(cave) 214 215 >>> mycaves = CaveContainer() 216 217 Now, if we want to import caves from CSV files, we also need an 218 importer, that imports data to the container: 219 220 >>> from waeup.csvfile.interfaces import ICSVFile 221 >>> from waeup.utils.importexport import CSVImporter 169 222 >>> from waeup.interfaces import IWAeUPCSVImporter 170 171 If, however, we try to create an importer for our container, we won't 172 find one right now: 173 174 >>> mycontainer = MyContainer() 175 >>> importer = IWAeUPCSVImporter(mycontainer) 176 Traceback (most recent call last): 177 ... 178 TypeError: ('Could not adapt', <MyContainer ...>, 179 <InterfaceClass ...IWAeUPCSVImporter>) 180 181 The reason is, that there is simply no importer defined for 182 `MyContainer` objects. 183 184 As an importer must take care for the specific structure of objects 185 that receive the data, we must first define an importer for our 186 special container class: 187 188 >>> import grok 189 >>> from waeup.utils.importexport import CSVImporter 190 >>> class MyImporter(CSVImporter): 191 ... grok.context(MyContainer) 192 ... datatype = u'My Stuff' 193 ... column_terms = ['col1', 'col2'] 194 ... def doImport(self, filepath, clear_old_data=True, 195 ... overwrite=True): 196 ... print "Data imported!" 197 198 This importer derives from `CSVImporter`. The `CSVImporter` base class 199 makes our importer automatically an adapter. Therefore we have to give 200 the type of object we provide this importer for by using 201 `grok.context()`. 202 203 Instances of our importer will have an instance variable 204 `self.context` available, which will hold the specific object into 205 which data should be imported. 206 207 The `datatype` string gives a description of the kind of importer we 208 define. It might be used by UI components to offer services of this 209 importer. 210 211 The `column_terms` class variable holds the column headings we expect 212 in a received CSV file. 213 214 Our importer should also comply with the IWAeUPCSVImporter interface, 215 therefore we defined a `doImport` method. 216 217 To register our importer with the framework, we have to ``grok`` it 218 first. Note, that in normal use this is done automatically on startup 219 by the Grok framework: 220 221 >>> grok.testing.grok_component('MyImporter', MyImporter) 223 >>> class CaveImporter(CSVImporter): 224 ... # Tell, what kinds of objects we connect... 225 ... grok.adapts(ICSVFile, ICaveContainer) 226 ... # Tell the world, that we are an importer... 227 ... grok.provides(IWAeUPCSVImporter) 228 ... # The datatype can be used by UI components and is required 229 ... # by the interface... 230 ... datatype = u'Cave Importer' 231 ... def doImport(self): 232 ... # CSVImporter instances have a `csvfile` and a `receiver` 233 ... # object defined which refer to the CSV file and the container. 234 ... for row in self.csvfile.getData(): 235 ... cave = Cave(size = row['size'], 236 ... owner = row['owner']) 237 ... self.receiver.addCave(cave) 238 239 240 An CSV importer must be grokked before we can use it. In real life, 241 this is normally done on startup automatically: 242 243 >>> grok.testing.grok_component('CaveImporter', CaveImporter) 222 244 True 223 245 224 Now we can get an importer: 225 226 >>> importer = IWAeUPCSVImporter(mycontainer) 227 >>> importer 228 <MyImporter object at 0x...> 229 230 This importer is rather useless as it does not do much: 231 232 >>> importer.doImport('nonsense') 233 Data imported! 234 235 This is a shameless lie. You might, however, get an idea of how to 236 build your own, real importer. 237 238 From the baseclass, however, we get some functionality out-of-the-box: 239 We can validate files (without importing them): 240 241 >>> open('mydata.csv', 'wb').write( 242 ... """col1,col2 243 ... item1,item2 244 ... """) 245 246 >>> importer.validate('mydata.csv') 247 True 248 249 If we deliver a misformatted file, the validator will notice that: 250 251 >>> open('crapdata.csv', 'wb').write(""" 252 ... col1,blah 253 ... item1,item2 254 ... """) 255 256 >>> importer.validate('crapdata.csv') 257 Traceback (most recent call last): 258 ... 259 KeyError: 'Validation Error: ... could not be found: col2' 260 261 Apparently our input file lacks a ``col2`` column. 262 263 The WAeUP portal already comes with a set of better suited 264 importers. As they rely pretty much on the data structures they are 265 built for, they are normally defined in the approriate code pieces 266 where also the data structure itself is handled. So you can find an 267 importer for faculty CSV data in the `facultycontainer` module. 246 Importers are multi-adapters, so we need to ask for one: 247 248 >>> from zope.component import getMultiAdapter 249 >>> myimporter = getMultiAdapter((mysource, mycaves), IWAeUPCSVImporter) 250 251 Now we can finally do the import: 252 253 >>> myimporter.doImport() 254 255 The import was really done: 256 257 >>> mycaves.caves 258 [<Cave object [size: 42, owner: Manfred]>] 259 260 **Important note**: 261 262 This importer adapts ICSVFile and ICaveContainer, which means that 263 the type of data receiver is specified correctly. For the CSV 264 source, however, the importer cannot be sure to find a column 265 ``size`` or ``owner`` in the file. 266 267 This can be prevented, by defining a more special CSVFile type and 268 adapting this instead of ICSVData. 269 270 In the `waeup.csvfile` subpackage's README it is shown, how this can 271 be accomplished. 272 273 Summing up we would define an ICaveCSVFile type, provide an 274 appropriate CSV file wrapper and then let our importer adapt:: 275 276 (ICaveCSVFile, ICaveContainer) 277 278 **Another note**: 279 280 The importer we defined above does not support the ``clear_old_data`` 281 and ``overwrite`` keywords as would be required by the 282 IWAeUPImporter interface. 283 284 In real life this keywords should be supported. 268 285 269 286 Clean up: 270 287 271 >>> import os 272 >>> os.unlink('crapdata.csv') 273 >>> os.unlink('mydata.csv') 274 288 >>> import os 289 >>> os.unlink('mycavedata.csv')
Note: See TracChangeset for help on using the changeset viewer.