## $Id: batching.py 8008 2012-03-30 10:23:32Z henrik $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Batch processing for applicants. """ import csv import grok from zope.schema import getFields from zope.interface import Interface from zope.component import queryUtility from zope.catalog.interfaces import ICatalog from waeup.kofa.interfaces import ( IBatchProcessor, IObjectConverter, FatalCSVError) from waeup.kofa.utils.batching import BatchProcessor from waeup.kofa.applicants.interfaces import ( IApplicantsContainer, IApplicant, IApplicantUpdateByRegNo) class ApplicantsContainerProcessor(BatchProcessor): """A processor for applicants containers. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'applicants container processor' grok.name(util_name) name = u'Applicants Container Processor' mode = u'create' iface = IApplicantsContainer location_fields = ['code',] factory_name = 'waeup.ApplicantsContainer' def parentsExist(self, row, site): return 'applicants' in site.keys() def entryExists(self, row, site): return row['code'] in site['applicants'].keys() def getParent(self, row, site): return site['applicants'] def getEntry(self, row, site): if not self.entryExists(row, site): return None parent = self.getParent(row, site) return parent.get(row['code']) def addEntry(self, obj, row, site): parent = self.getParent(row, site) parent[row['code']] = obj return def delEntry(self, row, site): parent = self.getParent(row, site) del parent[row['code']] return class ApplicantProcessor(BatchProcessor): """A batch processor for IApplicant objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'applicantprocessor' grok.name(util_name) name = u'Applicant Processor' iface = IApplicant location_fields = ['container_code'] factory_name = 'waeup.Applicant' mode = None @property def available_fields(self): return sorted(list(set( ['application_number','reg_number','container_code'] + getFields( self.iface).keys()))) def checkHeaders(self, headerfields, mode='create'): if not 'reg_number' in headerfields and not 'application_number' \ in headerfields: raise FatalCSVError( "Need at least columns application_number or reg_number ") if mode == 'create': for field in self.required_fields: if not field in headerfields: raise FatalCSVError( "Need at least columns %s for import!" % ', '.join(["'%s'" % x for x in self.required_fields])) # Check for fields to be ignored... not_ignored_fields = [x for x in headerfields if not x.startswith('--')] if len(set(not_ignored_fields)) < len(not_ignored_fields): raise FatalCSVError( "Double headers: each column name may only appear once.") return True def getLocator(self, row): #if 'application_number' in row.keys() and row['application_number']: if row.get('application_number',None): return 'application_number' elif 'reg_number' in row.keys() and row['reg_number']: return 'reg_number' else: return None def getParent(self, row, site): if not 'applicants' in site.keys(): return False return site['applicants'][row['container_code']] def parentsExist(self, row, site): return self.getParent(row, site) is not None def getEntry(self, row, site): if not self.parentsExist(row, site): return None parent = self.getParent(row, site) if self.getLocator(row) == 'application_number': if row['application_number'] in parent: applicant = parent[row['application_number']] return applicant elif self.getLocator(row) == 'reg_number': reg_number = row['reg_number'] cat = queryUtility(ICatalog, name='applicants_catalog') results = list( cat.searchResults(reg_number=(reg_number, reg_number))) if results: return results[0] return None def entryExists(self, row, site): return self.getEntry(row, site) is not None def addEntry(self, obj, row, site): parent = self.getParent(row, site) parent.addApplicant(obj) return def delEntry(self, row, site): applicant = self.getEntry(row, site) if applicant is not None: parent = self.getParent(row, site) del parent[applicant.application_number] pass def getMapping(self, path, headerfields, mode): """Get a mapping from CSV file headerfields to actually used fieldnames. """ result = dict() reader = csv.reader(open(path, 'rb')) raw_header = reader.next() for num, field in enumerate(headerfields): if field not in ['container_code', 'application_number', 'reg_number'] and mode == 'remove': continue if field == u'--IGNORE--': # Skip ignored columns in failed and finished data files. continue result[raw_header[num]] = field return result def checkConversion(self, row, mode='create'): """Validates all values in row. """ if mode in ['update', 'remove']: if self.getLocator(row) == 'reg_number': iface = IApplicantUpdateByRegNo else: iface = self.iface converter = IObjectConverter(iface) errs, inv_errs, conv_dict = converter.fromStringDict( row, self.factory_name) return errs, inv_errs, conv_dict