Changeset 4221 for waeup


Ignore:
Timestamp:
8 Jun 2009, 16:56:05 (15 years ago)
Author:
uli
Message:

Update tests.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • waeup/branches/ulif-rewrite/src/waeup/utils/importexport.txt

    r4159 r4221  
    149149data types like faculties, students, and the like.
    150150
    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 
     151For our purposes we define CSV imports as tuples, containing a CSV
     152source and a CSV receiver:
     153
     154  ``CSV Import := <CSVSource, CSVReceiver>``
     155
     156CSV sources
     157-----------
     158
     159CSV sources build a plugin framework inside the WAeUP portal (that
     160might be factored out one day). See `waeup.csvfile` to learn more
     161about that.
     162
     163A `waeup.csvfile.CSVFile` plugin cares for a special kind of CSV file.
     164
     165To make clear what that means, we start by creating a simple CSV
     166file.
     167
     168CSVFile objects are abstractions of simple .csv-files. But as not
     169every CSV file contains the same kind of data, we can create different
     170flavours of it, for instance to import departments or faculties. As
     171each kind of import needs different kind of data, we have different
     172kinds of CSV sources.
     173
     174CSV sources can check their associated files for consistency,
     175importability, etc.
     176
     177We create a simple CSV file with cave data:
     178
     179    >>> open('mycavedata.csv', 'wb').write(
     180    ... """size,owner
     181    ... 42,Manfred
     182    ... """)
     183
     184We have a very simple CSVFile available, which is registered as an
     185adapter for strings. To make this work, the string should be a path to
     186a file:
     187
     188    >>> from waeup.csvfile import getCSVFile
     189    >>> mysource = getCSVFile('mycavedata.csv')
     190    >>> mysource
     191    <waeup.csvfile.csvfile.CSVFile object at 0x...>
     192
     193We 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
     203Let'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
     217Now, if we want to import caves from CSV files, we also need an
     218importer, that imports data to the container:
     219
     220    >>> from waeup.csvfile.interfaces import ICSVFile
     221    >>> from waeup.utils.importexport import CSVImporter
    169222    >>> 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
     240An CSV importer must be grokked before we can use it. In real life,
     241this is normally done on startup automatically:
     242
     243    >>> grok.testing.grok_component('CaveImporter', CaveImporter)
    222244    True
    223245
    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.
     246Importers are multi-adapters, so we need to ask for one:
     247
     248    >>> from zope.component import getMultiAdapter
     249    >>> myimporter = getMultiAdapter((mysource, mycaves), IWAeUPCSVImporter)
     250
     251Now we can finally do the import:
     252
     253    >>> myimporter.doImport()
     254
     255The 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.
    268285
    269286Clean up:
    270287
    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.