source: main/waeup.kofa/trunk/src/waeup/kofa/university/batching.py @ 8468

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

Use certificates_catalog when importing certificates in update mode.

The certificate updater is untested because batching tests in the university package are unit tests and catalogs are not available.
I suggest to rewrite w.k.university.tests.test_batching and remove w.k.browser.batchprocessing.

  • Property svn:keywords set to Id
File size: 10.6 KB
RevLine 
[7195]1## $Id: batching.py 8302 2012-04-28 07:58:39Z 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##
[5009]18"""Batch processing components for academics objects.
19
20Batch processors eat CSV files to add, update or remove large numbers
21of certain kinds of objects at once.
22
23Here we define the processors for academics specific objects like
24faculties, departments and the like.
25"""
26import grok
27from zope.interface import Interface
[8302]28from zope.component import queryUtility
29from zope.schema import getFields
30from zope.catalog.interfaces import ICatalog
31from waeup.kofa.interfaces import IBatchProcessor, IGNORE_MARKER, FatalCSVError
[7811]32from waeup.kofa.university.interfaces import (
[7333]33    IFacultiesContainer, IFaculty, ICourse, IDepartment, ICertificate,
[5009]34    ICertificateCourse)
[7811]35from waeup.kofa.utils.batching import BatchProcessor
[5009]36
37class FacultyProcessor(BatchProcessor):
38    """A batch processor for IFaculty objects.
39    """
[6628]40    grok.implements(IBatchProcessor)
[5009]41    grok.provides(IBatchProcessor)
42    grok.context(Interface)
[7933]43    util_name = 'facultyprocessor'
[5009]44    grok.name(util_name)
45
[7933]46    name = u'Faculty Processor'
[5009]47    iface = IFaculty
48
49    location_fields = ['code',]
50    factory_name = 'waeup.Faculty'
51
[6628]52    mode = None
53
[5009]54    def parentsExist(self, row, site):
55        return 'faculties' in site.keys()
56
57    def entryExists(self, row, site):
58        return row['code'] in site['faculties'].keys()
59
60    def getParent(self, row, site):
61        return site['faculties']
62
63    def getEntry(self, row, site):
64        if not self.entryExists(row, site):
65            return None
66        parent = self.getParent(row, site)
67        return parent.get(row['code'])
[6628]68
[5009]69    def addEntry(self, obj, row, site):
70        parent = self.getParent(row, site)
71        parent.addFaculty(obj)
72        return
73
74    def delEntry(self, row, site):
75        parent = self.getParent(row, site)
76        del parent[row['code']]
77        pass
78
79class DepartmentProcessor(BatchProcessor):
80    """A batch processor for IDepartment objects.
81    """
[6628]82    grok.implements(IBatchProcessor)
[5009]83    grok.provides(IBatchProcessor)
84    grok.context(Interface)
[7933]85    util_name = 'departmentprocessor'
[5009]86    grok.name(util_name)
87
[7933]88    name = u'Department Processor'
[5009]89    iface = IDepartment
90
91    location_fields = ['code', 'faculty_code']
92    factory_name = 'waeup.Department'
93
[6628]94    mode = None
95
[5009]96    def parentsExist(self, row, site):
97        if not 'faculties' in site.keys():
98            return False
99        return row['faculty_code'] in site['faculties']
100
101    def entryExists(self, row, site):
102        if not self.parentsExist(row, site):
103            return False
104        parent = self.getParent(row, site)
105        return row['code'] in parent.keys()
106
107    def getParent(self, row, site):
108        return site['faculties'][row['faculty_code']]
109
110    def getEntry(self, row, site):
111        if not self.entryExists(row, site):
112            return None
113        parent = self.getParent(row, site)
114        return parent.get(row['code'])
[6628]115
[5009]116    def addEntry(self, obj, row, site):
117        parent = self.getParent(row, site)
118        parent.addDepartment(obj)
119        return
120
121    def delEntry(self, row, site):
122        parent = self.getParent(row, site)
123        del parent[row['code']]
124        return
125
126class CourseProcessor(BatchProcessor):
127    """A batch processor for ICourse objects.
128    """
[6628]129    grok.implements(IBatchProcessor)
[5009]130    grok.provides(IBatchProcessor)
131    grok.context(Interface)
[7933]132    util_name = 'courseprocessor'
[5009]133    grok.name(util_name)
134
[7933]135    name = u'Course Processor'
[5009]136    iface = ICourse
137
138    location_fields = ['code', 'faculty_code', 'department_code']
139    factory_name = 'waeup.Course'
140
[6628]141    mode = None
142
[5009]143    def parentsExist(self, row, site):
144        if not 'faculties' in site.keys():
145            return False
146        if not row['faculty_code'] in site['faculties'].keys():
147            return False
148        faculty = site['faculties'][row['faculty_code']]
149        return row['department_code'] in faculty.keys()
150
151    def entryExists(self, row, site):
152        if not self.parentsExist(row, site):
153            return False
154        parent = self.getParent(row, site)
155        return row['code'] in parent.keys()
156
157    def getParent(self, row, site):
158        dept = site['faculties'][row['faculty_code']][row['department_code']]
159        return dept.courses
160
161    def getEntry(self, row, site):
162        if not self.entryExists(row, site):
163            return None
164        parent = self.getParent(row, site)
165        return parent.get(row['code'])
[6628]166
[5009]167    def addEntry(self, obj, row, site):
168        parent = self.getParent(row, site)
169        parent.addCourse(obj)
170        return
171
172    def delEntry(self, row, site):
173        parent = self.getParent(row, site)
174        del parent[row['code']]
175        return
176
177class CertificateProcessor(BatchProcessor):
178    """A batch processor for ICertificate objects.
179    """
[6628]180    grok.implements(IBatchProcessor)
[5009]181    grok.provides(IBatchProcessor)
182    grok.context(Interface)
[7933]183    util_name = 'certificateprocessor'
[5009]184    grok.name(util_name)
185
[7933]186    name = u'Certificate Processor'
[5009]187    iface = ICertificate
188
[8302]189    location_fields = ['code']
[5009]190    factory_name = 'waeup.Certificate'
191
[6628]192    mode = None
193
[8302]194    @property
195    def available_fields(self):
196        fields = getFields(self.iface)
197        return sorted(list(set(
198            ['faculty_code','department_code'] + fields.keys())))
199
200    def checkHeaders(self, headerfields, mode='create'):
201        req = self.req[mode]
202        # Check for required fields...
203        for field in req:
204            if not field in headerfields:
205                raise FatalCSVError(
206                    "Need at least columns %s for import!" %
207                    ', '.join(["'%s'" % x for x in req]))
208        # Check for double fields. Cannot happen because this error is
209        # already catched in views
210        not_ignored_fields = [x for x in headerfields
211                              if not x.startswith('--')]
212        if len(set(not_ignored_fields)) < len(not_ignored_fields):
213            raise FatalCSVError(
214                "Double headers: each column name may only appear once.")
215        if mode == 'create':
216            if not 'faculty_code' in headerfields \
217                and not 'department_code' in headerfields :
218                raise FatalCSVError(
219                    "Need at least columns faculty_code and department_code")
220        return True
221
[5009]222    def parentsExist(self, row, site):
[8302]223        return self.getParent(row,site) is not None
[5009]224
225    def entryExists(self, row, site):
226        parent = self.getParent(row, site)
[8302]227        if parent is not None:
228            return row['code'] in parent.keys()
229        return False
[5009]230
231    def getParent(self, row, site):
[8302]232        if not 'faculties' in site.keys():
233            return None
234        # If both faculty and department codes are provided, use
235        # these to get parent.
236        if row.get('faculty_code',None) not in (None, IGNORE_MARKER) and \
237            row.get('department_code',None) not in (None, IGNORE_MARKER):
238            if not row['faculty_code'] in site['faculties'].keys():
239                return None
240            faculty = site['faculties'][row['faculty_code']]
241            if not row['department_code'] in faculty.keys():
242                return None
243            dept = faculty[row['department_code']]
244            return dept.certificates
245        # If department code or faculty code is missing,
246        # use catalog to get parent. Makes only sense in update mode but
247        # does also work in create mode.
248        cat = queryUtility(ICatalog, name='certificates_catalog')
249        results = list(
250            cat.searchResults(code=(row['code'], row['code'])))
251        if results:
252            return results[0].__parent__
253        return None
[5009]254
255    def getEntry(self, row, site):
256        parent = self.getParent(row, site)
[8302]257        if parent is not None:
258            return parent.get(row['code'])
259        return None
[5009]260
261    def addEntry(self, obj, row, site):
262        parent = self.getParent(row, site)
[6243]263        parent.addCertificate(obj)
264        return
[5009]265
266    def delEntry(self, row, site):
267        parent = self.getParent(row, site)
268        del parent[row['code']]
269        return
270
271class CertificateCourseProcessor(BatchProcessor):
272    """A batch processor for ICertificateCourse objects.
273    """
[6628]274    grok.implements(IBatchProcessor)
[5009]275    grok.provides(IBatchProcessor)
276    grok.context(Interface)
[7933]277    util_name = 'certificatecourseprocessor'
[5009]278    grok.name(util_name)
279
[7933]280    name = u'CertificateCourse Processor'
[5009]281    iface = ICertificateCourse
282
283    location_fields = ['course', 'level', 'faculty_code', 'department_code',
284                       'certificate_code',]
285    factory_name = 'waeup.CertificateCourse'
286
[6628]287    mode = None
288
[5009]289    def parentsExist(self, row, site):
290        if not 'faculties' in site.keys():
291            return False
292        if not row['faculty_code'] in site['faculties'].keys():
293            return False
294        faculty = site['faculties'][row['faculty_code']]
295        if not row['department_code'] in faculty.keys():
296            return False
297        dept = faculty[row['department_code']]
298        return row['certificate_code'] in dept.certificates.keys()
299
300    def entryExists(self, row, site):
301        if not self.parentsExist(row, site):
302            return False
303        parent = self.getParent(row, site)
304        code = "%s_%s" % (row['course'].code, row['level'])
305        return code in parent.keys()
306
307    def getParent(self, row, site):
308        dept = site['faculties'][row['faculty_code']][row['department_code']]
309        return dept.certificates[row['certificate_code']]
310
311    def getEntry(self, row, site):
312        if not self.entryExists(row, site):
313            return None
314        parent = self.getParent(row, site)
[6628]315        return parent.get("%s_%s" % (row['course'].code, row['level']))
[5009]316
317    def addEntry(self, obj, row, site):
318        parent = self.getParent(row, site)
319        parent.addCourseRef(row['course'],
[7665]320                            row['level'], row['mandatory'])
[5009]321        return
322
323    def delEntry(self, row, site):
324        parent = self.getParent(row, site)
325        parent.delCourseRef(row['course'].code, row['level'])
326        return
Note: See TracBrowser for help on using the repository browser.