source: WAeUP_SRP/base/WAeUPImport.py @ 3350

Last change on this file since 3350 was 3347, checked in by joachim, 17 years ago

more informative message if trying to upload a pending-file as csv.

  • Property svn:keywords set to Id
File size: 48.5 KB
Line 
1#-*- mode: python; mode: fold -*-
2# (C) Copyright 2005 The WAeUP group  <http://www.waeup.org>
3# Author: Joachim Schmitz (js@aixtraware.de)
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 2 as published
7# by the Free Software Foundation.
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
17# 02111-1307, USA.
18#
19# $Id: WAeUPImport.py 3347 2008-03-18 16:29:37Z joachim $
20"""The WAeUP Tool Box.
21"""
22
23#from AccessControl import ClassSecurityInfo
24#from Acquisition import aq_inner
25#from Acquisition import aq_parent
26#from Globals import DTMLFile
27#from Globals import InitializeClass
28from OFS.SimpleItem import SimpleItem
29from zExceptions import BadRequest
30
31#from Products.CMFCore.utils import getToolByName
32#from Products.CPSSchemas.DataStructure import DataStructure
33#from Products.CPSSchemas.DataModel import DataModel
34#from Products.CPSSchemas.StorageAdapter import MappingStorageAdapter
35from Products.CMFCore.ActionProviderBase import ActionProviderBase
36#from Products.CMFCore.permissions import View
37#from Products.ZCatalog.ZCatalog import ZCatalog
38#from Products.CMFCore.permissions import ModifyPortalContent
39#from Products.CMFCore.permissions import ManagePortal
40from Products.CMFCore.utils import UniqueObject
41#from Products.CMFCore.URLTool import URLTool
42from Products.CMFCore.utils import getToolByName
43from Globals import package_home,INSTANCE_HOME
44from Products.AdvancedQuery import Eq, Between, Le,In
45import csv,re,os,sys
46from shutil import copy2
47import DateTime,time
48import logging
49p_home = package_home(globals())
50i_home = INSTANCE_HOME
51from utils import makeDigest
52
53NO_KEY = '----'
54IGNORE = 'ignore'
55class WAeUPImport(UniqueObject, SimpleItem, ActionProviderBase): ###(
56    """ WAeUPImport """
57    required_modes = ('create',)
58
59    def __init__(self,waeup_tool): ###(
60        self.students_folder = waeup_tool.portal_url.getPortalObject().campus.students
61        self.member = member = waeup_tool.portal_membership.getAuthenticatedMember()
62        self.import_date = DateTime.DateTime()
63        self.imported_by = str(member)
64        #self.current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
65        self.waeup_tool = waeup_tool
66        self.academics_folder = waeup_tool.portal_url.getPortalObject().campus.academics
67        self.schema_tool = getToolByName(waeup_tool, 'portal_schemas')
68        self.layout_tool = getToolByName(waeup_tool, 'portal_layouts')
69        self.portal_workflow = getToolByName(waeup_tool, 'portal_workflow')
70        self.portal_url = getToolByName(waeup_tool, 'portal_url')
71        self.portal_catalog = waeup_tool.portal_catalog
72        self.students_catalog = waeup_tool.students_catalog
73        self.courses_catalog = waeup_tool.courses_catalog
74        self.course_results = waeup_tool.course_results
75        self.applicants_catalog = waeup_tool.applicants_catalog
76        #self.mode = mode
77        # self.import_method = getattr(self, '%s' % mode,None)
78        errors = []
79        # if self.import_method is None:
80        #     errors.append('No importer method %s' % mode)
81        self.pending_path = "%s/import/%s.pending" % (i_home,self.plural_name)
82        self.pending_tmp = "%s/import/%s.pending.tmp" % (i_home,self.plural_name)
83        self.pending_backup = "%s/import/%s.pending.old" % (i_home,self.plural_name)
84        self.pending_fn = os.path.split(self.pending_path)[1]
85        self.imported_path = "%s/import/%s.imported" % (i_home,self.plural_name)
86        self.imported_fn = os.path.split(self.imported_path)[1]
87        iname = "import_%s" % self.name
88        self.logger = logging.getLogger('WAeUPImport.%sImport' % self.plural_name.capitalize())
89        self.schema = self.schema_tool._getOb(iname,None)
90        #self.pending_schema = self.schema_tool._getOb("%s_pending" % iname,None)
91        self.layout = self.layout_tool._getOb(iname,None)
92        while True:
93            if self.schema is None:
94                errors.append('no schema %s' % iname)
95            # if self.pending_schema is None:
96            #     self.pending_schema = self.schema
97            if self.layout is None:
98                errors.append('no such layout %s' % iname)
99            if errors:
100                break
101            self.data_keys = self.schema.keys()
102            self.csv_keys = self.schema.keys()
103            info = {}
104            info['imported_from'] = ''
105            info['import_record_no'] = 0
106            info['imported_by'] = self.imported_by
107            info['import_date'] = self.import_date.strftime("%d/%m/%y %H:%M:%S")
108            info['error'] = ''
109            self.info = info
110            self.csv_keys.extend(self.info)
111            self.validators = {}
112            for widget in self.layout.keys():
113                self.validators[widget] = self.layout[widget].validate
114            self.required_keys = {}
115            for mode in self.required_modes:
116                self.required_keys[mode] = [self.layout.getIdUnprefixed(id)
117                                    for id,widget in self.layout.objectItems()
118                                    if widget.is_required]
119            break
120        self.init_errors = ','.join(errors)
121    ###)
122
123    def makeIdLists(self): ###(
124        pending_digests = []
125        pending = []
126        # pending_student_ids = []
127        # pending_matric_nos = []
128        # pending_reg_ns = []
129        if os.path.exists(self.pending_path):
130            datafile = open(self.pending_path,"r")
131            reader = csv.reader(datafile)
132            old_headline = reader.next()
133            #datafile.seek(0)
134            pending_csv_reader = csv.DictReader(datafile,old_headline,)
135            #pending_csv_reader.next() # skip headline
136            for item in pending_csv_reader:
137                digest = makeDigest(item,self.data_keys)
138                if digest not in pending_digests:
139                    pending_digests += digest,
140                    pending += item,
141            datafile.close()
142            #copy2(self.pending_path,self.pending_backup)
143        return pending, pending_digests
144    ###)
145
146    def checkHeadline(self,headline): ###(
147        """ check the headline of a csv file """
148        import_keys = [k.strip() for k in headline if not (k.strip().startswith('ignore')
149                                                        or k.strip() in self.info.keys())]
150        # import_keys = [k.strip() for k in headline if not (k.strip() in self.info.keys())]
151        diff2schema = set(import_keys).difference(set(self.schema.keys()))
152        diff2layout = set(import_keys).difference(set(self.layout.keys()))
153        #if diff2schema and diff2schema != set(['id',]):
154        if diff2schema:
155            return list(diff2schema)
156        return []
157    ###)
158
159    def getHeadlineFields(self,headline,values): ###(
160        """ check the headline of a csv file """
161        # import_keys = [k.strip() for k in headline if not (k.strip().startswith('ignore')
162        #                                                 or k.strip() in self.info.keys())]
163        import_keys = [k.strip() for k in headline if not (k.strip() in self.info.keys())]
164        info_keys = [k.strip() for k in headline if k.strip() in self.info.keys()]
165        si = set(import_keys)
166        ss = set(self.schema.keys())
167
168        invalid_keys = si - ss
169        keys = []
170        i = 0
171        duplicates = False
172        singels = []
173        msg = ''
174        while True:
175            if len(values) != len(import_keys):
176                if len(values) == len(import_keys) + len(info_keys):
177                    msg += "fields from pending file in headline"
178                else:
179                    msg += "%d fields in headline but %d values" % (len(import_keys),len(values))
180                break
181            for k in import_keys:
182                if k in singels:
183                    keys += (k,'%s' % k,values[i],'(duplicate)'),
184                    msg += ("duplicate %s," % k)
185                    keys[singels.index(k)] = (k,'%s' % k,values[singels.index(k)],'(duplicate)')
186                elif k in invalid_keys and not k.startswith(IGNORE):
187                    keys += (k,NO_KEY,values[i],'(invalid)'),
188                else:
189                    keys += (k,k,values[i],''),
190                i += 1
191                singels += k,
192            break
193        return msg,keys
194    ###)
195###)
196
197class ApplicationImport(WAeUPImport):###(
198    name = "application"
199    plural_name = "%ss" % name
200    commit_after = 1000
201
202    def create(self,mapping):###(
203        reg_no = mapping.get('reg_no')
204        msg = ''
205        while True:
206            try:
207                self.applicants_catalog.addRecord(**mapping)
208            except ValueError:
209                msg =  "applicant record with reg_no %s already exists" % reg_no
210            break
211        return reg_no,msg,mapping
212    ###)
213
214    def edit(self,mapping):###(
215        reg_no = mapping.get('reg_no')
216        status = mapping.get('status')
217        msg = ''
218        while True:
219            res = self.applicants_catalog(reg_no = reg_no)
220            if len(res):
221                if res[0].status == 'created' and status != 'created':
222                    msg =  "student object with id %s for %s already created, status cannot be changed" % (res[0].student_id, reg_no)
223                elif status == 'created' and res[0].status != 'created':
224                    msg =  "student object for %s has not yet been created, status cannot be set to 'created'" % (reg_no)
225                else:
226                    self.applicants_catalog.modifyRecord(**mapping)
227            else:
228                msg =  "applicant record with reg_no %s does not exist" % reg_no
229            break
230        return reg_no,msg,mapping
231    ###)
232
233###)
234
235class CertificateImport(WAeUPImport):###(
236    name = "certificate"
237    plural_name = "%ss" % name
238    commit_after = 100000
239
240    def create(self,mapping):###(
241        if getattr(self,'_v_certificate_list',None) is None:
242            self._v_certificate_list = []
243        if getattr(self,'_v_department_certificates',None) is None:
244            departments = self.portal_catalog(portal_type = "Department")
245            self._v_department_certificates = {}
246            for d in departments:
247                certificates_folder = getattr(d.getObject(),"certificates",None)
248                self._v_department_certificates[d.getId] = certificates_folder.objectIds()
249        department_id = mapping['department_code']
250        msg = ''
251        certificate_id = mapping.get('code')
252        while True:
253            department_certificates = self._v_department_certificates.get(department_id,None)
254            if department_certificates is None:
255                msg =  "No Department with ID: %s" % department_id
256                break
257            if certificate_id in self._v_certificate_list:
258                msg =  "Duplicate Certificate ID: %s" % department_id
259                break
260            if certificate_id in department_certificates:
261                msg =  "Duplicate Certificate ID: %s" % department_id
262                break
263            try:
264                d.invokeFactory('Certificate', certificate_id)
265            except BadRequest,E:
266                msg =  "%s" % E
267                break
268            self._v_certificate_list.append(certificate_id)
269            c = getattr(d,certificate_id)
270            c.getContent().edit(mapping=mapping)
271            break
272        return certificate_id,msg,mapping
273    ###)
274
275    def edit(self,mapping):###(
276        certificate_id = mapping.get('code')
277        res = self.portal_catalog(id=certificate_id)
278        msg = ''
279        while True:
280            if not res:
281                msg =  "no certificate with id: %s" % certificate_id
282                break
283            c = res[0].getObject()
284            c.getContent().edit(mapping=mapping)
285            break
286        return certificate_id,msg,mapping
287    ###)
288###)
289
290class CourseImport(WAeUPImport):###(
291    name = "course"
292    plural_name = "%ss" % name
293    commit_after = 1000
294
295    def create(self,mapping):###(
296        if getattr(self,'_v_course_list',None) is None:
297            self._v_course_list = []
298        if getattr(self,'_v_department_courses',None) is None:
299            departments = self.portal_catalog(portal_type = "Department")
300            self._v_department_courses = {}
301            for department in departments:
302                courses_folder = getattr(department.getObject(),"courses",None)
303                if courses_folder is not None:
304                    self._v_department_courses[department.getId] = courses_folder.objectIds()
305        department_id = mapping['department_code']
306        course_id = mapping.get('code','')
307        msg = ''
308        while True:
309            department_courses = self._v_department_courses.get(department_id,None)
310            if department_courses is None:
311                msg =  "no department with id: %(department_id)s" % vars()
312                break
313            if course_id in self._v_course_list:
314                msg =  "duplicate course id: %(course_id)s" % vars()
315                break
316            if course_id in department_courses:
317                msg =  "course %(course_id)s already exists in department %(department_id)s" % vars()
318                break
319            try:
320                department.invokeFactory('Course', course_id)
321            except BadRequest,E:
322                msg =  "%s" % E
323                break
324            self._v_course_list.append(course_id)
325            course = getattr(department,course_id)
326            course.getContent().edit(mapping=mapping)
327            break
328        return course_id,msg,mapping
329    ###)
330
331    def edit(self,mapping): ###(
332        course_id = mapping.get('code','')
333        course = self.courses_catalog.getRecordByKey(course_id)
334        while True:
335            if course is None:
336                msg =  "no course with id: %s" % course_id
337                break
338            course_object = getattr(getattr(self.academics_folder,course.department),course_id)
339            course_object.getContent().edit(mapping=mapping)
340            break
341        return course_id,msg,mapping
342    ###)
343###)
344
345class CourseResultImport(WAeUPImport):###(
346    """ CourseresultImport """
347    name = "course_result"
348    plural_name = "%ss" % name
349    commit_after = 1000000
350    required_modes = ('create','edit','remove')
351
352    def getStudentRecord(self,mapping): ###(
353        id_field_found = False
354        msg = ''
355        student_record = None
356        id_count = 0
357        for id_key in ('matric_no','id'):
358            id_field = mapping.get(id_key,'')
359            if id_field:
360                id_count += 1
361                search_key = id_key
362                search_field = id_field
363        while True:
364            #if id_count > 1:
365            #    msg = "both id and matric_no are provided"
366            #    break
367            if id_count == 0:
368                msg = "neither id nor matric_no provided"
369                break
370            query = Eq(search_key,search_field)
371            res = self.students_catalog.evalAdvancedQuery(query)
372            if res:
373                student_record = res[0]
374                if search_key == "matric_no":
375                    mapping['id'] = student_record.id
376                elif search_key == "id":
377                    mapping['matric_no'] = student_record.matric_no
378            else:
379                msg = "no student with %(search_key)s %(search_field)s" % vars()
380            break
381        return student_record,msg
382    ###)
383
384    def create(self,mapping):###(
385        students_folder = self.portal_url.getPortalObject().campus.students
386        if getattr(self,'_v_courses',None) is None:
387            res = self.courses_catalog()
388            self._v_courses = {}
389            for brain in res:
390                self._v_courses[brain.code] = brain
391        if getattr(self,'_v_level_created',None) is None:
392            self._v_level_created = []
393        msg = ''
394        key = ''
395        while True:
396            course_id = mapping.get('code')
397            if course_id not in self._v_courses.keys():
398                msg = "no course with id: %s" % course_id
399                break
400            student_record,msg = self.getStudentRecord(mapping)
401            if msg:
402                break
403            student_id = student_record.id
404            level_id = mapping.get('level_id','')
405            code = mapping.get('code','')
406            if student_id not in self._v_level_created:
407                try:
408                    context = getattr(getattr(students_folder,
409                                            "%(student_id)s" % vars()),
410                                    'study_course')
411                except:
412                    msg = "could not create level %(level_id)s for %(student_id)s" % vars()
413                    break
414                if level_id not in context.objectIds():
415                    context.invokeFactory('StudentStudyLevel',"%(level_id)s" % vars())
416                    level = getattr(context,"%(level_id)s" % vars())
417                    self.portal_workflow.doActionFor(level,'open')
418                    # the session string must not be copied into the level object
419                    current_verdict = getattr(student_record,'current_verdict','')
420                    current_session = getattr(student_record,'current_session','')
421                    if current_verdict and student_record.current_level == level_id:
422                        level.getContent().edit(mapping={'verdict': "%s" %
423                                                        current_verdict,
424                                                        'session': "%s" %
425                                                        current_session,
426                                                        })
427                        self.portal_workflow.doActionFor(level,'close')
428                self._v_level_created += student_id,
429            mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % vars()
430            for k in ('semester','credits',):
431                mapping[k] = getattr(self._v_courses[course_id],k)
432            try:
433                self.course_results.addRecord(**mapping)
434            except ValueError:
435                msg = "course result already exists: %s" % key
436            break
437        return key,msg,mapping
438    ###)
439
440    def edit(self,mapping): ###(
441        msg = ''
442        key = ''
443        while True:
444            student_record,msg = self.getStudentRecord(mapping)
445            if msg:
446                break
447            student_id = student_record.id
448            level_id = mapping.get('level_id','')
449            code = mapping.get('code','')
450            code = mapping['code']
451            mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % vars()
452            if self.course_results.getRecordByKey(key) is None:
453                msg =  "no course result with key %(key)s" % vars()
454                break
455            self.course_results.modifyRecord(**mapping)
456            break
457        return key,msg,mapping
458    ###)
459
460    def remove(self,mapping):###(
461        key = ''
462        msg = ''
463        while True:
464            student_record,msg = self.getStudentRecord(mapping)
465            if msg:
466                break
467            student_id = student_record.id
468            level_id = mapping.get('level_id','')
469            code = mapping.get('code','')
470            key = "%(student_id)s|%(level_id)s|%(code)s" % vars()
471            if self.course_results.getRecordByKey(key) is None:
472                msg =  "no course result with key %(key)s" % vars()
473                break
474            self.course_results.deleteRecord(key)
475            break
476        return key,msg,mapping
477    ###)
478
479###)
480
481class CertificateCourseImport(WAeUPImport):###(
482    name = "certificate_course"
483    plural_name = "%ss" % name
484    commit_after = 100000
485    required_modes = ('create','edit')
486
487    def create(self,mapping):
488        if getattr(self,'_v_courses',None) is None:
489            res = self.courses_catalog()
490            self._v_courses= [course.code for course in res]
491        if getattr(self,'_v_ceritficates',None) is None:
492            res = self.portal_catalog(portal_type = "Certificate")
493            self._v_certificates = {}
494            for cert in res:
495                self._v_certificates[cert.getId] = cert.getObject()
496        msg = ''
497        while True:
498            certificate_course_id = mapping.get('code')
499            if certificate_course_id not in self._v_courses:
500                msg =  "no course with id: %s" % certificate_course_id
501                break
502            cert_id = mapping['certificate_code']
503            cert = self._v_certificates.get(cert_id,None)
504            if cert is None:
505                msg =  "no certificate with id: %s" % cert_id
506                break
507            level_id = mapping.get('level')
508            level = getattr(cert,level_id,None)
509            if level is None:
510                cert.invokeFactory('StudyLevel', level_id)
511                level = getattr(cert,level_id,None)
512            elif hasattr(level,certificate_course_id):
513                msg =  "duplicate certificate course id: %(certificate_course_id)s " % vars()
514                msg += "in %(cert_id)s/ %(level_id)s" % vars()
515                break
516            level.invokeFactory('CertificateCourse', certificate_course_id)
517            c = getattr(level,certificate_course_id)
518            c.getContent().edit(mapping=mapping)
519            break
520        return certificate_course_id,msg,mapping
521    ###)
522
523class DepartmentImport(WAeUPImport):###(
524    name = "department"
525    plural_name = "%ss" % name
526    commit_after = 1000
527
528    def create(self,mapping):###(
529        "create a department in the correct faculty"
530        faculty_id = mapping['faculty_code']
531        msg = ''
532        if getattr(self,'_v_faculties',None) is None:
533            res = self.portal_catalog(portal_type = "Department")
534            self._v_faculties = {}
535            for f in res:
536                self._v_faculties[f.getId] = f.getObject()
537        department_id = mapping.get('code','')
538        while True:
539            faculty = self._v_faculties.get(faculty_id,None)
540            if faculty is None:
541                msg =  "no faculty with id: %s" % faculty_id
542                break
543            else:
544                d = getattr(faculty,department_id,None)
545                if d is None or d.portal_type == "Faculty":
546                    try:
547                        faculty.invokeFactory('Department', department_id)
548                    except BadRequest,E:
549                        msg =  "%s" % E
550                        break
551                    d = getattr(faculty,department_id)
552                    d.invokeFactory('CoursesFolder','courses')
553                    courses = getattr(d,'courses')
554                    dict = {'Title': 'Courses'}
555                    courses.getContent().edit(mapping=dict)
556                    d.invokeFactory('CertificatesFolder','certificates')
557                    certificates = getattr(d,'certificates')
558                    dict = {'Title': 'Certificates'}
559                    certificates.getContent().edit(mapping=dict)
560                    d.getContent().edit(mapping=mapping)
561            break
562        return department_id,msg,mapping
563    ###)
564
565    def edit(self,mapping): ###(
566        "edit a department in the correct faculty"
567        academics_folder = self.portal_url.getPortalObject().campus.academics
568        faculty_id = mapping['faculty_code']
569        department_id = mapping.get('code','')
570        msg = ''
571        while True:
572            try:
573                d = getattr(getattr(academics_folder,faculty_id),department_id,None)
574            except KeyError:
575                msg =  "department %s or faculty %s wrong" % (department_id,faculty_id)
576                break
577            if d is None or d.portal_type == "Faculty":
578                msg =  "department %s not found" % (department_id)
579                break
580            d.getContent().edit(mapping=mapping)
581            break
582        return department_id,msg,mapping
583    ###)
584###)
585
586class FacultyImport(WAeUPImport):###(
587    name = "faculty"
588    plural_name = "faculties"
589    commit_after = 100
590
591    def create(self,mapping): ###(
592        "create a faculty"
593        academics_folder = self.portal_url.getPortalObject().campus.academics
594        faculty_id = mapping.get('code','')
595        msg = ''
596        while True:
597            if faculty_id in academics_folder.objectIds():
598                msg =  "faculty with id: %s exists" % faculty_id
599                break
600            logger.info('Creating Faculty %(code)s, %(title)s' % mapping)
601            try:
602                academics_folder.invokeFactory('Faculty', faculty_id)
603            except BadRequest,E:
604                msg =  "%s" % E
605                break
606            f = getattr(academics_folder,faculty_id,None)
607            f.getContent().edit(mapping=mapping)
608            break
609        return faculty_id,msg,mapping
610        ###)
611
612    def edit(self,mapping): ###(
613        "edit a faculty"
614        academics_folder = self.portal_url.getPortalObject().campus.academics
615        faculty_id = mapping['code']
616        msg = ''
617        while True:
618            f = getattr(academics_folder,faculty_id,None)
619            if f is None:
620                msg =  "faculty with id: %s does not exist" % faculty_id
621            f.getContent().edit(mapping=mapping)
622            break
623        return faculty_id,msg,mapping
624    ###)
625###)
626
627class StudentImport(WAeUPImport):###(
628    name = "student"
629    plural_name = "%ss" % name
630    commit_after = 100
631
632    field2types_student = {   ###(
633                      'StudentApplication':
634                          {'id': 'application',
635                           'title': 'Application Data',
636                           'wf_transition_return': 'close',
637                           'wf_transition_admit': 'remain',
638                           'fields':
639                             ('jamb_reg_no',
640                              'entry_mode',
641                              'entry_session',
642                              'jamb_score',
643                              'app_email',
644                              'jamb_age',
645                              'jamb_state',
646                              'jamb_lga',
647                              'jamb_sex',
648                              )
649                              },
650                      #'StudentPume':
651                      #    {'id': 'pume',
652                      #     'title': 'Pume Data',
653                      #     'wf_transition_return': 'close',
654                      #     'wf_transition_admit': 'close',
655                      #     'fields':
656                      #       ('pume_score',
657                      #        )
658                      #        },
659                      'StudentClearance':
660                          {'id': 'clearance',
661                           'title': 'Clearance/Eligibility Record',
662                           'wf_transition_return': 'close',
663                           'wf_transition_admit': 'remain',
664                           'fields':
665                             ('matric_no',
666                              'nationality',
667                              'lga',
668                              'birthday',
669                              )
670                              },
671                         'StudentPersonal':
672                          {'id': 'personal',
673                           'title': 'Personal Data',
674                           'wf_transition_return': 'open',
675                           'wf_transition_admit': 'remain',
676                           'fields':
677                             ('firstname',
678                              'middlename',
679                              'lastname',
680                              'sex',
681                              'email',
682                              'phone',
683                              'perm_address',
684                              )
685                              },
686                         'StudentStudyCourse':
687                          {'id': 'study_course',
688                           'title': 'Study Course',
689                           'wf_transition_return': 'open',
690                           'wf_transition_admit': 'remain',
691                           'fields':
692                             ('study_course',
693                              'current_level',
694                              'current_session',
695                              'current_mode',
696                              'current_verdict',
697                              'previous_verdict',
698                              )
699                              },
700                         # 'StudentStudyLevel':
701                         #  {'id': 'current_level',
702                         #   'title': '',
703                         #   'wf_transition_return': 'open',
704                         #   'wf_transition_admit': 'remain',
705                         #   'fields':
706                         #     ('verdict',
707                         #      'session',
708                         #      )
709                         #      },
710                         'PaymentsFolder':
711                          {'id': 'payments',
712                           'title': 'Payments',
713                           'wf_transition_return': 'open',
714                           'wf_transition_admit': 'open',
715                           'fields':
716                             ()
717                              },
718                         }
719    ###)
720
721    def create(self,mapping): ###(
722        "create student records due import"
723        logger = logging.getLogger('WAeUPTool.mass_create_student')
724        students_folder = self.portal_url.getPortalObject().campus.students
725        jamb_reg_no = mapping.get('jamb_reg_no',None)
726        matric_no = mapping.get('matric_no',None)
727        msg = ''
728        student_id = mapping.get('id',None)
729        while True:
730            if student_id:
731                msg = "student_id must not be specified in create mode"
732                break
733            if jamb_reg_no:
734                res = self.students_catalog(jamb_reg_no = jamb_reg_no)
735                if res:
736                    msg = "jamb_reg_no exists"
737                    break
738            if matric_no:
739                res = self.students_catalog(matric_no = matric_no)
740                if res:
741                    msg = "matric_no exists"
742                    break
743            if not matric_no and not jamb_reg_no:
744                msg = "jamb_reg_no or matric_no must be specified"
745                break
746            student_id = self.waeup_tool.generateStudentId('?')
747            students_folder.invokeFactory('Student', student_id)
748            student_obj = getattr(students_folder,student_id)
749            f2t = self.field2types_student
750            d = {}
751            transition = mapping.get('reg_transition','admit')
752            if transition not in ('admit','return'):
753                msg = "no valid transition provided"
754                break
755            for pt in f2t.keys():
756                student_obj.invokeFactory(pt,f2t[pt]['id'])
757                sub_obj = getattr(student_obj,f2t[pt]['id'])
758                sub_doc = sub_obj.getContent()
759                d['Title'] = f2t[pt]['title']
760                for field in f2t[pt]['fields']:
761                    d[field] = mapping.get(field,'')
762
763                if pt == "StudentApplication":
764                    #d['jamb_sex']  = 'M'
765                    #if mapping.get('sex'):
766                    #    d['jamb_sex']  = 'F'
767                    d['jamb_firstname'] = mapping.get('firstname',None)
768                    d['jamb_middlename'] = mapping.get('middlename',None)
769                    d['jamb_lastname'] = mapping.get('lastname',None)
770
771                # if pt == "StudyCourse":
772                #     for von,zu in (('entry_mode','current_mode'),
773                #                    ('entry_session','current_session')):
774                #         if mapping.get(zu,None) is None and mapping.get(von,None) is not None:
775                #             d[zu] = mapping[von]
776                sub_doc.edit(mapping = d)
777
778                #import pdb;pdb.set_trace()
779                new_state = f2t[pt]['wf_transition_%(transition)s' % vars()]
780                if new_state != "remain":
781                    self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
782            self.portal_workflow.doActionFor(student_obj,transition)
783            student_obj.manage_setLocalRoles(student_id, ['Owner',])
784            mapping['id'] = student_id
785            break
786        return student_id,msg,mapping
787    ###)
788
789    def edit(self,mapping): ###(
790        wftool = self.portal_workflow
791        "edit student records due import"
792        logger = logging.getLogger('WAeUPTool.mass_edit_student')
793        students_folder = self.portal_url.getPortalObject().campus.students
794        student_id = mapping.get('id',None)
795        jamb_reg_no = mapping.get('jamb_reg_no',None)
796        matric_no = mapping.get('matric_no',None)
797        editable_keys = mapping.keys()
798        msg = ''
799        while True:
800            if student_id:
801                res = self.students_catalog(id = student_id)
802                if not res:
803                    msg = "no student with id %s" % student_id
804                    break
805                student_record = res[0]
806                if matric_no and student_record.matric_no:
807                    if  matric_no != student_record.matric_no:
808                        msg = "old matric_no %s overwritten with %s" % (student_record.matric_no,matric_no)
809                        #logger.info("%s, old matric_no %s overwritten with %s" % (student_record.id,student_record.matric_no,matric_no))
810                if jamb_reg_no and student_record.jamb_reg_no:
811                    if jamb_reg_no != student_record.jamb_reg_no:
812                        msg = "old reg_no %s overwritten with %s" % (student_record.jamb_reg_no,jamb_reg_no)
813                        #logger.info("%s, old reg_no %s overwritten with %s" % (student_record.id,student_record.jamb_reg_no,jamb_reg_no))
814            elif jamb_reg_no:
815                res = self.students_catalog(jamb_reg_no = jamb_reg_no)
816                if not res:
817                    msg = "no student with jamb_reg_no %s" % jamb_reg_no
818                    break
819                student_record = res[0]
820                editable_keys.remove('jamb_reg_no')
821            elif matric_no:
822                res = self.students_catalog(matric_no = matric_no)
823                if not res:
824                    msg = "no student with matric_no %s" % matric_no
825                    break
826                student_record = res[0]
827                editable_keys.remove('matric_no')
828            ## included only to change wf state from admitted to returning
829            #if student_record.review_state not in ('admitted','objection_raised'):
830            #    return '%s' % student_record.id ,"student is not in state admitted or objection_raised"
831            ## end inclusion
832            student_id = student_record.id
833            student_obj = getattr(students_folder,student_id)
834            f2t = self.field2types_student
835            d = {}
836            any_change = False
837            #special treatment for StudentStudyLevel
838            d['verdict']  = mapping.get('current_verdict','')
839            d['session']  = mapping.get('current_session','')
840            current_level = mapping.get('current_level','')
841            while d['session'] and d['verdict'] and current_level:
842                sub_obj = getattr(student_obj,'study_course',None)
843                if sub_obj is None:
844                    break
845                level_obj = getattr(sub_obj,current_level,None)
846                if  level_obj is None:
847                    break
848                any_change = True
849                level_obj.getContent().edit(mapping = d)
850                try:
851                    wftool.doActionFor(level_obj,'close')
852                except:
853                    pass
854                break
855            for pt in f2t.keys():
856                #if pt == "StudentApplication":
857                #    d['jamb_sex']  = 'M'
858                #    if mapping.get('sex'):
859                #        d['jamb_sex']  = 'F'
860                intersect = set(f2t[pt]['fields']).intersection(set(editable_keys))
861                if intersect and pt not in ('StudentStudyLevel',):
862                    object_id = f2t[pt]['id']
863                    sub_obj = getattr(student_obj,object_id,None)
864                    if sub_obj is None:
865                        try:
866                            student_obj.invokeFactory(pt,object_id)
867                        except:
868                            continue
869                        sub_obj = getattr(student_obj,object_id)
870                        if f2t[pt]['title'] != '':
871                            d['Title'] = f2t[pt]['title']
872                    sub_doc = sub_obj.getContent()
873                    for field in intersect:
874                        changed = False
875                        if getattr(sub_doc,field,None) != mapping.get(field,''):
876                            any_change = True
877                            changed = True
878                            d[field] = mapping.get(field,'')
879                        if changed:
880                            sub_doc.edit(mapping = d)
881            ## included only to change wf state from admitted to returning
882            #    if student_record.review_state in ('admitted','objection_raised'):
883            #        new_state = f2t[pt]['wf_transition_return']
884            #        sub_obj = getattr(student_obj,f2t[pt]['id'],None)
885            #        if sub_obj and new_state != "remain":
886            #            try:
887            #                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
888            #            except:
889            #                #logger.info('%s, wf transition %s of %s failed' % (student_id,new_state,sub_obj.id))
890            #                pass
891            #if student_record.review_state in ('admitted','objection_raised'):
892            #    wfaction = 'return'
893            #    try:
894            #        self.portal_workflow.doActionFor(student_obj,wfaction)
895            #        logger.info('%s, wf state changed' % student_id)
896            #        any_change = True
897            #    except:
898            #        logger.info('%s, wf transition failed, old state = %s' % (student_id,student_record.review_state))
899            #        pass
900            ## end inclusion
901            break
902        # if not any_change:
903        #     msg = 'not modified'
904        return student_id,msg,mapping
905    ###)
906###)
907
908class VerdictImport(WAeUPImport):###(
909    """ VerdictImport """
910    name = "verdict"
911    plural_name = "%ss" % name
912    commit_after = 100000
913    required_modes = ('create','edit')
914
915    def create(self,mapping):
916        "edit student verdicts and create StudentStudyLevel object if not existent"
917        wftool = self.portal_workflow
918        logger = logging.getLogger('WAeUPTool.mass_edit_verdict')
919        students_folder = self.portal_url.getPortalObject().campus.students
920        student_id = mapping.get('id',None)
921        matric_no = mapping.get('matric_no',None)
922        editable_keys = mapping.keys()
923        while True:
924            key = ''
925            msg = ''
926            if student_id:
927                student_record = self.students_catalog.getRecordByKey(student_id)
928                if student_record is None:
929                    #return '',"no student with id %s" % student_id
930                    msg = "no student with id %s" % student_id
931                    break
932                if matric_no and student_record.matric_no and matric_no != student_record.matric_no:
933                    msg = 'student %s: matric_no %s does not match %s' % (student_record.id,
934                                                                          student_record.matric_no,
935                                                                          matric_no)
936                    break
937                mapping['matric_no'] = student_record.matric_no
938            elif matric_no:
939                res = self.students_catalog(matric_no = matric_no)
940                if not res:
941                    msg = "no student with matric_no %s" % matric_no
942                    break
943                student_record = res[0]
944                editable_keys.remove('matric_no')
945            else:
946                msg = "no id or matric_no specified"
947                break
948            student_id = student_record.id
949            mapping['id'] = student_id
950            d = {}
951            #import pdb;pdb.set_trace()
952            any_change = False
953            #special treatment for StudentStudyLevel
954            current_session = d['session'] = mapping.get('current_session','')
955            if current_session and student_record.session != current_session:
956                msg = 'student %s: imported session %s does not match current_session %s' % (student_id,
957                                                                                            current_session,
958                                                                                            student_record.session)
959                break
960            current_level = mapping.get('current_level','')
961            if not current_level.isdigit():
962                msg = 'student %s: imported level is empty' % (student_id,)
963                break
964            if current_level and student_record.level != current_level:
965                msg = 'student %s: imported level %s does not match current_level %s' % (student_id,
966                                                                                        current_level,
967                                                                                        student_record.level)
968                break
969            student_review_state =  student_record.review_state
970            if student_review_state == 'deactivated':
971                msg = "student %s in review_state %s" % (student_id, student_review_state)
972                break
973            if student_review_state not in ('courses_validated','returning'):
974                msg = "student %s in wrong review_state %s" % (student_id, student_review_state)
975                break
976            student_obj = getattr(students_folder,student_id)
977            # f2t = self.field2types_student
978            study_course_obj = getattr(student_obj,'study_course',None)
979            if study_course_obj is None:
980                msg = 'student %s: no study_course object' % student_id
981                break
982            level_obj = getattr(study_course_obj,current_level,None)
983
984            if  level_obj is None:
985                # The only difference to the edit method is that we create a StudentStudyLevel object
986                try:
987                    study_course_obj.invokeFactory('StudentStudyLevel',"%s" % current_level)
988                    level_obj = getattr(context,"%s" % current_level)
989                    level_obj.portal_workflow.doActionFor(level,'open')
990                except:
991                    continue
992                #msg = 'student %s: no study_level object for level %s' % (student_id,
993                #                                                                current_level)
994                #break
995
996            verdict = d['verdict'] = d['current_verdict']  = mapping.get('current_verdict','')
997
998            #if verdict == student_record.verdict:
999            #    msg = 'student %s: verdict already set to %s' % (student_id,
1000            #                                                            verdict)
1001
1002            level_review_state = wftool.getInfoFor(level_obj,'review_state',None)
1003            if level_review_state != "closed":
1004                wftool.doActionFor(level_obj,'close')
1005                # msg = 'student %s: level %s is not closed' % (student_id,
1006                #                                                      current_level)
1007
1008            study_course_obj.getContent().edit(mapping = d)
1009            level_obj.getContent().edit(mapping = d)
1010            if student_review_state != "returning":
1011                wftool.doActionFor(student_obj,'return')
1012            # try:
1013            #     wftool.doActionFor(level_obj,'close')
1014            # except:
1015            #     pass
1016            break
1017        return student_id,msg,mapping
1018
1019
1020    def edit(self,mapping):
1021        "edit student verdicts"
1022        wftool = self.portal_workflow
1023        logger = logging.getLogger('WAeUPTool.mass_edit_verdict')
1024        students_folder = self.portal_url.getPortalObject().campus.students
1025        student_id = mapping.get('id',None)
1026        matric_no = mapping.get('matric_no',None)
1027        editable_keys = mapping.keys()
1028        while True:
1029            key = ''
1030            msg = ''
1031            if student_id:
1032                student_record = self.students_catalog.getRecordByKey(student_id)
1033                if student_record is None:
1034                    #return '',"no student with id %s" % student_id
1035                    msg = "no student with id %s" % student_id
1036                    break
1037                if matric_no and student_record.matric_no and matric_no != student_record.matric_no:
1038                    msg = 'student %s: matric_no %s does not match %s' % (student_record.id,
1039                                                                          student_record.matric_no,
1040                                                                          matric_no)
1041                    break
1042                mapping['matric_no'] = student_record.matric_no
1043            elif matric_no:
1044                res = self.students_catalog(matric_no = matric_no)
1045                if not res:
1046                    msg = "no student with matric_no %s" % matric_no
1047                    break
1048                student_record = res[0]
1049                editable_keys.remove('matric_no')
1050            else:
1051                msg = "no id or matric_no specified"
1052                break
1053            student_id = student_record.id
1054            mapping['id'] = student_id
1055            d = {}
1056            #import pdb;pdb.set_trace()
1057            any_change = False
1058            #special treatment for StudentStudyLevel
1059            current_session = d['session'] = mapping.get('current_session','')
1060            if current_session and student_record.session != current_session:
1061                msg = 'student %s: imported session %s does not match current_session %s' % (student_id,
1062                                                                                            current_session,
1063                                                                                            student_record.session)
1064                break
1065            current_level = mapping.get('current_level','')
1066            if not current_level.isdigit():
1067                msg = 'student %s: imported level is empty' % (student_id,)
1068                break
1069            if current_level and student_record.level != current_level:
1070                msg = 'student %s: imported level %s does not match current_level %s' % (student_id,
1071                                                                                        current_level,
1072                                                                                        student_record.level)
1073                break
1074            student_review_state =  student_record.review_state
1075            if student_review_state == 'deactivated':
1076                msg = "student %s in review_state %s" % (student_id, student_review_state)
1077                break
1078            if student_review_state not in ('courses_validated','returning'):
1079                msg = "student %s in wrong review_state %s" % (student_id, student_review_state)
1080                break
1081            student_obj = getattr(students_folder,student_id)
1082            # f2t = self.field2types_student
1083            study_course_obj = getattr(student_obj,'study_course',None)
1084            if study_course_obj is None:
1085                msg = 'student %s: no study_course object' % student_id
1086                break
1087            level_obj = getattr(study_course_obj,current_level,None)
1088            if  level_obj is None:
1089                msg = 'student %s: no study_level object for level %s' % (student_id,
1090                                                                                current_level)
1091                break
1092            verdict = d['verdict'] = d['current_verdict']  = mapping.get('current_verdict','')
1093
1094            #if verdict == student_record.verdict:
1095            #    msg = 'student %s: verdict already set to %s' % (student_id,
1096            #                                                            verdict)
1097
1098            level_review_state = wftool.getInfoFor(level_obj,'review_state',None)
1099            if level_review_state != "closed":
1100                wftool.doActionFor(level_obj,'close')
1101                # msg = 'student %s: level %s is not closed' % (student_id,
1102                #                                                      current_level)
1103
1104            study_course_obj.getContent().edit(mapping = d)
1105            level_obj.getContent().edit(mapping = d)
1106            if student_review_state != "returning":
1107                wftool.doActionFor(student_obj,'return')
1108            # try:
1109            #     wftool.doActionFor(level_obj,'close')
1110            # except:
1111            #     pass
1112            break
1113        return student_id,msg,mapping
1114    ###)
1115
Note: See TracBrowser for help on using the repository browser.