"""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.schema import getFields from zope.component import queryUtility from zope.catalog.interfaces import ICatalog from waeup.sirp.interfaces import IBatchProcessor, FatalCSVError from waeup.sirp.students.interfaces import ( IStudent, IStudentStudyCourse, IStudentStudyCourseImport) from waeup.sirp.utils.batching import BatchProcessor class StudentProcessor(BatchProcessor): """A batch processor for IStudent objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'studentimporter' grok.name(util_name) name = u'Student Importer' iface = IStudent location_fields = ['student_id',] factory_name = 'waeup.Student' @property def req(self): result = dict( create = self.required_fields, update = self.location_fields, remove = self.location_fields, ) return result def parentsExist(self, row, site): return 'students' in site.keys() # The entry never exists in create mode. def entryExists(self, row, site): if row.has_key('student_id'): return row['student_id'] in site['students'].keys() return False def getParent(self, row, site): return site['students'] def getEntry(self, row, site): if not self.entryExists(row, site): return None parent = self.getParent(row, site) return parent.get(row['student_id']) def addEntry(self, obj, row, site): parent = self.getParent(row, site) parent.addStudent(obj) return def delEntry(self, row, site): parent = self.getParent(row, site) del parent[row['student_id']] pass class StudentStudyCourseProcessor(BatchProcessor): """A batch processor for IStudentStudyCourse objects. """ grok.implements(IBatchProcessor) grok.provides(IBatchProcessor) grok.context(Interface) util_name = 'studycourseupdater' grok.name(util_name) name = u'StudentStudyCourse Importer (update only)' iface = IStudentStudyCourseImport factory_name = 'waeup.StudentStudyCourse' @property def available_fields(self): result = [] return sorted(list(set( ['student_id','reg_number'] + getFields(self.iface).keys()))) def checkHeaders(self, headerfields, mode='ignore'): if not 'reg_number' in headerfields and not 'student_id' in headerfields: raise FatalCSVError( "Need at least columns student_id or reg_number for import!") # 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 parentsExist(self, row, site): if not 'students' in site.keys(): return False if 'student_id' in row.keys(): if row['student_id'] in site['students']: student = site['students'][row['student_id']] return student else: # Here we know that the reg_number is in row reg_number = row['reg_number'] cat = queryUtility(ICatalog, name='students_catalog') results = list( cat.searchResults(reg_number=(reg_number, reg_number))) if results: return results[0] return False def entryExists(self, row, site): student = self.parentsExist(row, site) if not student: return False if 'studycourse' in student: return student return False def getEntry(self, row, site): student = self.entryExists(row, site) if not student: return None return student.get('studycourse')