## $Id: batching.py 8920 2012-07-05 14:48:51Z 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 components for academics objects. Batch processors eat CSV files to add, update or remove large numbers of certain kinds of objects at once. Here we define the processors for academics specific objects like faculties, departments and the like. """ import grok from zope.interface import Interface from zope.component import queryUtility from zope.schema import getFields from zope.catalog.interfaces import ICatalog from waeup.kofa.interfaces import IBatchProcessor, IGNORE_MARKER, FatalCSVError from waeup.kofa.university.interfaces import ( IFacultiesContainer, IFaculty, ICourse, IDepartment, ICertificate, ICertificateCourse) from waeup.kofa.utils.batching import BatchProcessor class FacultyProcessor(BatchProcessor): """A batch processor for IFaculty objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'facultyprocessor' grok.name(util_name) name = u'Faculty Processor' iface = IFaculty location_fields = ['code',] factory_name = 'waeup.Faculty' mode = None def parentsExist(self, row, site): return 'faculties' in site.keys() def entryExists(self, row, site): return row['code'] in site['faculties'].keys() def getParent(self, row, site): return site['faculties'] 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.addFaculty(obj) return def delEntry(self, row, site): parent = self.getParent(row, site) del parent[row['code']] pass class DepartmentProcessor(BatchProcessor): """A batch processor for IDepartment objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'departmentprocessor' grok.name(util_name) name = u'Department Processor' iface = IDepartment location_fields = ['code', 'faculty_code'] factory_name = 'waeup.Department' mode = None def parentsExist(self, row, site): if not 'faculties' in site.keys(): return False return row['faculty_code'] in site['faculties'] def entryExists(self, row, site): if not self.parentsExist(row, site): return False parent = self.getParent(row, site) return row['code'] in parent.keys() def getParent(self, row, site): return site['faculties'][row['faculty_code']] 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.addDepartment(obj) return def delEntry(self, row, site): parent = self.getParent(row, site) del parent[row['code']] return class CourseProcessor(BatchProcessor): """A batch processor for ICourse objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'courseprocessor' grok.name(util_name) name = u'Course Processor' iface = ICourse location_fields = ['code', 'faculty_code', 'department_code'] factory_name = 'waeup.Course' mode = None def parentsExist(self, row, site): if not 'faculties' in site.keys(): return False if not row['faculty_code'] in site['faculties'].keys(): return False faculty = site['faculties'][row['faculty_code']] return row['department_code'] in faculty.keys() def entryExists(self, row, site): if not self.parentsExist(row, site): return False parent = self.getParent(row, site) return row['code'] in parent.keys() def getParent(self, row, site): dept = site['faculties'][row['faculty_code']][row['department_code']] return dept.courses 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.addCourse(obj) return def delEntry(self, row, site): parent = self.getParent(row, site) del parent[row['code']] return class CertificateProcessor(BatchProcessor): """A batch processor for ICertificate objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'certificateprocessor' grok.name(util_name) name = u'Certificate Processor' iface = ICertificate location_fields = ['code'] factory_name = 'waeup.Certificate' mode = None @property def available_fields(self): fields = getFields(self.iface) return sorted(list(set( ['faculty_code','department_code'] + fields.keys()))) def checkHeaders(self, headerfields, mode='create'): req = self.req[mode] # Check for required fields... for field in req: if not field in headerfields: raise FatalCSVError( "Need at least columns %s for import!" % ', '.join(["'%s'" % x for x in req])) # Check for double fields. Cannot happen because this error is # already catched in views 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.") if mode == 'create': if not 'faculty_code' in headerfields \ and not 'department_code' in headerfields : raise FatalCSVError( "Need at least columns faculty_code and department_code") return True def parentsExist(self, row, site): return self.getParent(row,site) is not None def entryExists(self, row, site): parent = self.getParent(row, site) if parent is not None: return row['code'] in parent.keys() return False def getParent(self, row, site): if not 'faculties' in site.keys(): return None # If both faculty and department codes are provided, use # these to get parent. if row.get('faculty_code',None) not in (None, IGNORE_MARKER) and \ row.get('department_code',None) not in (None, IGNORE_MARKER): if not row['faculty_code'] in site['faculties'].keys(): return None faculty = site['faculties'][row['faculty_code']] if not row['department_code'] in faculty.keys(): return None dept = faculty[row['department_code']] return dept.certificates # If department code or faculty code is missing, # use catalog to get parent. Makes only sense in update mode but # does also work in create mode. cat = queryUtility(ICatalog, name='certificates_catalog') results = list( cat.searchResults(code=(row['code'], row['code']))) if results: return results[0].__parent__ return None def getEntry(self, row, site): parent = self.getParent(row, site) if parent is not None: return parent.get(row['code']) return None def addEntry(self, obj, row, site): parent = self.getParent(row, site) parent.addCertificate(obj) return def delEntry(self, row, site): parent = self.getParent(row, site) del parent[row['code']] return class CertificateCourseProcessor(BatchProcessor): """A batch processor for ICertificateCourse objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'certificatecourseprocessor' grok.name(util_name) name = u'CertificateCourse Processor' iface = ICertificateCourse location_fields = ['course', 'level', 'faculty_code', 'department_code', 'certificate_code',] factory_name = 'waeup.CertificateCourse' mode = None def parentsExist(self, row, site): if not 'faculties' in site.keys(): return False if not row['faculty_code'] in site['faculties'].keys(): return False faculty = site['faculties'][row['faculty_code']] if not row['department_code'] in faculty.keys(): return False dept = faculty[row['department_code']] return row['certificate_code'] in dept.certificates.keys() def entryExists(self, row, site): if not self.parentsExist(row, site): return False parent = self.getParent(row, site) code = "%s_%s" % (row['course'].code, row['level']) return code in parent.keys() def getParent(self, row, site): dept = site['faculties'][row['faculty_code']][row['department_code']] return dept.certificates[row['certificate_code']] def getEntry(self, row, site): if not self.entryExists(row, site): return None parent = self.getParent(row, site) return parent.get("%s_%s" % (row['course'].code, row['level'])) def addEntry(self, obj, row, site): parent = self.getParent(row, site) parent.addCertCourse(row['course'], row['level'], row['mandatory']) return def delEntry(self, row, site): parent = self.getParent(row, site) parent.delCertCourse(row['course'].code, row['level']) return