source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/batching.py @ 10222

Last change on this file since 10222 was 7271, checked in by Henrik Bettermann, 13 years ago

we definitely need also some browser tests to catch these kind of mistakes.

  • Property svn:keywords set to Id
File size: 6.7 KB
Line 
1## $Id: batching.py 7271 2011-12-04 18:00:38Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""Batch processing for applicants.
19"""
20import csv
21import grok
22from zope.schema import getFields
23from zope.interface import Interface
24from zope.component import queryUtility
25from zope.catalog.interfaces import ICatalog
26from waeup.sirp.interfaces import (
27    IBatchProcessor, IObjectConverter, FatalCSVError)
28from waeup.sirp.utils.batching import BatchProcessor
29from waeup.sirp.applicants.interfaces import (
30    IApplicantsContainer, IApplicant, IApplicantUpdateByRegNo)
31
32class ApplicantsContainerImporter(BatchProcessor):
33    """An importer for applicants containers.
34    """
35    grok.implements(IBatchProcessor)
36    grok.provides(IBatchProcessor)
37    grok.context(Interface)
38    util_name = 'applicants container importer'
39    grok.name(util_name)
40
41    name = u'Applicants Container Importer'
42    mode = u'create'
43    iface = IApplicantsContainer
44
45    location_fields = ['code',]
46    factory_name = 'waeup.ApplicantsContainer'
47
48    def parentsExist(self, row, site):
49        return 'applicants' in site.keys()
50
51    def entryExists(self, row, site):
52        return row['code'] in site['applicants'].keys()
53
54    def getParent(self, row, site):
55        return site['applicants']
56
57    def getEntry(self, row, site):
58        if not self.entryExists(row, site):
59            return None
60        parent = self.getParent(row, site)
61        return parent.get(row['code'])
62
63    def addEntry(self, obj, row, site):
64        parent = self.getParent(row, site)
65        parent[row['code']] = obj
66        return
67
68    def delEntry(self, row, site):
69        parent = self.getParent(row, site)
70        del parent[row['code']]
71        return
72
73class ApplicantImporter(BatchProcessor):
74    """A batch processor for IApplicant objects.
75    """
76    grok.implements(IBatchProcessor)
77    grok.provides(IBatchProcessor)
78    grok.context(Interface)
79    util_name = 'applicantimporter'
80    grok.name(util_name)
81    name = u'Applicant Importer'
82    iface = IApplicant
83    location_fields = []
84    factory_name = 'waeup.Applicant'
85    location_fields = ['container_code']
86
87    mode = None
88
89    @property
90    def available_fields(self):
91        return sorted(list(set(
92            ['application_number','reg_number','container_code'] + getFields(
93                self.iface).keys())))
94
95    def checkHeaders(self, headerfields, mode='create'):
96        if not 'reg_number' in headerfields and not 'application_number' \
97            in headerfields:
98            raise FatalCSVError(
99                "Need at least columns application_number or reg_number ")
100        if mode == 'create':
101            for field in self.required_fields:
102                if not field in headerfields:
103                    raise FatalCSVError(
104                        "Need at least columns %s for import!" %
105                        ', '.join(["'%s'" % x for x in self.required_fields]))
106        # Check for fields to be ignored...
107        not_ignored_fields = [x for x in headerfields
108                              if not x.startswith('--')]
109        if len(set(not_ignored_fields)) < len(not_ignored_fields):
110            raise FatalCSVError(
111                "Double headers: each column name may only appear once.")
112        return True
113
114    def getLocator(self, row):
115        #if 'application_number' in row.keys() and row['application_number']:
116        if row.get('application_number',None):
117            return 'application_number'
118        elif 'reg_number' in row.keys() and row['reg_number']:
119            return 'reg_number'
120        else:
121            return None
122
123    def getParent(self, row, site):
124        if not 'applicants' in site.keys():
125            return False
126        return site['applicants'][row['container_code']]
127
128    def parentsExist(self, row, site):
129        return self.getParent(row, site) is not None
130
131    def getEntry(self, row, site):
132        if not self.parentsExist(row, site):
133            return None
134        parent = self.getParent(row, site)
135        if self.getLocator(row) == 'application_number':
136            if row['application_number'] in parent:
137                applicant = parent[row['application_number']]
138                return applicant
139        elif self.getLocator(row) == 'reg_number':
140            reg_number = row['reg_number']
141            cat = queryUtility(ICatalog, name='applicants_catalog')
142            results = list(
143                cat.searchResults(reg_number=(reg_number, reg_number)))
144            if results:
145                return results[0]
146        return None
147
148    def entryExists(self, row, site):
149        return self.getEntry(row, site) is not None
150
151    def addEntry(self, obj, row, site):
152        parent = self.getParent(row, site)
153        parent.addApplicant(obj)
154        return
155
156    def delEntry(self, row, site):
157        applicant = self.getEntry(row, site)
158        if applicant is not None:
159            parent = self.getParent(row, site)
160            del parent[applicant.application_number]
161        pass
162
163    def getMapping(self, path, headerfields, mode):
164        """Get a mapping from CSV file headerfields to actually used fieldnames.
165        """
166        result = dict()
167        reader = csv.reader(open(path, 'rb'))
168        raw_header = reader.next()
169        for num, field in enumerate(headerfields):
170            if field not in ['container_code',
171                'application_number', 'reg_number'] and mode == 'remove':
172                continue
173            if field == u'--IGNORE--':
174                # Skip ignored columns in failed and finished data files.
175                continue
176            result[raw_header[num]] = field
177        return result
178
179    def checkConversion(self, row, mode='create'):
180        """Validates all values in row.
181        """
182        if mode in ['update', 'remove']:
183            if self.getLocator(row) == 'reg_number':
184                iface = IApplicantUpdateByRegNo
185        else:
186            iface = self.iface
187        converter = IObjectConverter(iface)
188        errs, inv_errs, conv_dict =  converter.fromStringDict(
189            row, self.factory_name)
190        return errs, inv_errs, conv_dict
Note: See TracBrowser for help on using the repository browser.