source: WAeUP_SRP/base/WAeUPTool.py @ 2537

Last change on this file since 2537 was 2528, checked in by Henrik Bettermann, 17 years ago

show proper application statistics
commit after 2000 if importing application data

  • Property svn:keywords set to Id
File size: 72.3 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: WAeUPTool.py 2528 2007-11-04 20:15:03Z henrik $
20"""The WAeUP Tool Box.
21"""
22
23from AccessControl import ClassSecurityInfo
24from Acquisition import aq_inner
25from Acquisition import aq_parent
26from Globals import DTMLFile
27from Globals import InitializeClass
28from OFS.SimpleItem import SimpleItem
29from zExceptions import BadRequest
30
31from Products.CMFCore.utils import getToolByName
32from Products.CPSSchemas.DataStructure import DataStructure
33from Products.CPSSchemas.DataModel import DataModel
34from Products.CPSSchemas.StorageAdapter import MappingStorageAdapter
35from Products.CMFCore.ActionProviderBase import ActionProviderBase
36from Products.CMFCore.permissions import View
37from Products.ZCatalog.ZCatalog import ZCatalog
38from Products.CMFCore.permissions import ModifyPortalContent
39from Products.CMFCore.permissions import ManagePortal
40from Products.CMFCore.utils import UniqueObject
41from Products.CMFCore.URLTool import URLTool
42from Products.CMFCore.utils import getToolByName
43from Students import makeCertificateCode
44from Globals import package_home,INSTANCE_HOME
45import DateTime,time
46import logging
47import transaction
48import csv,re,os,sys
49from shutil import copy2
50from Products.AdvancedQuery import Eq, Between, Le,In
51
52p_home = package_home(globals())
53i_home = INSTANCE_HOME
54images_base = os.path.join(i_home,"images")
55
56
57def getObject(object,name):
58    if object.hasObject(name):
59        return getattr(object,name)
60    return None
61
62class WAeUPTool(UniqueObject, SimpleItem, ActionProviderBase):
63    """WAeUP tool"""
64
65    id = 'waeup_tool'
66    meta_type = 'WAeUP Tool'
67    _actions = ()
68    security = ClassSecurityInfo()
69    security.declareObjectProtected(View)
70    manage_options = ( ActionProviderBase.manage_options
71                     + SimpleItem.manage_options
72                     )
73
74    def rwrite(self,s): ###(
75        response = self.REQUEST.RESPONSE
76        response.setHeader('Content-type','text/html; charset=ISO-8859-15')
77        response.write("%s<br />\r\n" % s)
78    ###)
79
80    def sleep(self,secs): ###(
81        "sleep"
82        import time
83        time.sleep(secs)
84        return
85
86###)
87
88    security.declareProtected(ModifyPortalContent,'openLog') ###(
89    def openLog(self,name):
90        """open a log file"""
91        version = 1
92        path = "%s/log/%s_%d.log" % (i_home,name,version)
93        while os.path.exists(path):
94            version += 1
95            path = "%s/log/%s_%d.log" % (i_home,name,version)
96        log = open(path,"w")
97        return log
98    ###)
99
100    security.declareProtected(ModifyPortalContent,'bypassQueueCatalog') ###(
101    def bypassQueueCatalog(self,enable=True):
102        """bypass the QueueCatalog by setting all indexes to process imediate,
103        if enable is True (default) the old settings are restored
104        """
105
106    ###)
107
108    security.declareProtected(ModifyPortalContent,'measureOaT') ###(
109    def measureOaT(self,method="a",probe="1000",nr_pts="1"):
110        """measure Object access Time"""
111        import random
112        if hasattr(self,'portal_catalog_real'):
113            aq_portal = self.portal_catalog_real.evalAdvancedQuery
114        else:
115            aq_portal = self.portal_catalog.evalAdvancedQuery
116        nr_pts = int(nr_pts)
117        probe = int(probe)
118        intervall = probe/10
119        objects = ("application","clearance","personal")
120        portal_types = ("StudentApplication","StudentClearance","StudentPersonal")
121        #i = random.randrange(num_objects)
122        count = 0
123        found = 0
124        not_found = 0
125        t_found = 0
126        t_not_found = 0
127        time_found = time_not_found = 0.0
128        t_time_found = t_time_not_found = 0.0
129        accessed = []
130        t_min = 1000
131        t_max = 0
132        #import pdb;pdb.set_trace()
133        students = self.portal_catalog(portal_type="Student")
134        num_students = len(students)
135        if method == "d":
136            query = Eq('path','/uniben/campus/students') & In('portal_type',portal_types[:nr_pts])
137            res = aq_portal(query)
138            brains = {}
139            for r in res:
140                sid = r.relative_path.split('/')[-2]
141                if brains.has_key(sid):
142                    brains[sid][r.portal_type] = r
143                else:
144                    brains[sid] = {r.portal_type : r}
145            brains_list = brains.keys()
146            num_objects = len(brains_list)
147        else:
148            num_objects = num_students
149        print "="*40
150        print "method: %s probes: %d nr_pts: %d num_objects: %d" % (method,
151                                                                        probe,
152                                                                        nr_pts,
153                                                                        num_objects)
154        print "nr found/not time found/not min/max"
155        elapse = time.time()
156        i_elapse = time.time()
157        c_elapse = time.clock()
158        for c in range(1,probe + 1):
159            i = random.randrange(num_objects)
160            if method in ('a','b','c'):
161                student_brain = students[i]
162            elif method == "d":
163                #import pdb;pdb.set_trace()
164                student_brain = brains[brains_list[i]]
165            if method == "c":
166                query = Eq('path',student_brain.getPath()) & In('portal_type',portal_types[:nr_pts])
167                res = aq_portal(query)
168                this_portal_types = [r.portal_type for r in res]
169            for i in range(nr_pts):
170                oid = objects[i]
171                if method == "a":
172                    try:
173                        student_path = student_brain.getPath()
174                        path = "%s/%s" % (student_path,oid)
175                        doc = self.unrestrictedTraverse(path).getContent()
176                        found += 1
177                        i_time = time.time() - i_elapse
178                        time_found += i_time
179                    except:
180                        not_found += 1
181                        i_time = time.time() - i_elapse
182                        time_not_found += i_time
183                        pass
184                elif method == "b":
185                    try:
186                        student_object = student_brain.getObject()
187                        doc = getattr(student_object,oid).getContent()
188                        found += 1
189                        i_time = time.time() - i_elapse
190                        time_found += i_time
191                    except:
192                        i_time = time.time() - i_elapse
193                        time_not_found += i_time
194                        not_found += 1
195                        pass
196                elif method == "c":
197                    if portal_types[i] in this_portal_types:
198                        found += 1
199                        doc = res[this_portal_types.index(portal_types[i])].getObject().getContent()
200                        i_time = time.time() - i_elapse
201                        time_found += i_time
202                    else:
203                        not_found += 1
204                        i_time = time.time() - i_elapse
205                        time_not_found += i_time
206                elif method == "d":
207                    if student_brain.has_key(portal_types[i]):
208                        found += 1
209                        doc = student_brain[portal_types[i]].getObject().getContent()
210                        i_time = time.time() - i_elapse
211                        time_found += i_time
212                    else:
213                        not_found += 1
214                        i_time = time.time() - i_elapse
215                        time_not_found += i_time
216                i_elapse = time.time()
217            if c and (c % intervall == 0):
218                #i_time = time.time() - i_elapse
219                t_per = 0.0
220                if found:
221                    t_per = time_found/found
222                if t_per > t_max:
223                    t_max = t_per
224                if t_per > 0.0 and t_per < t_min:
225                    t_min = t_per
226                itf = 0.0
227                if found:
228                    itf = time_found/found
229                itnf = 0.0
230                if not_found :
231                    itnf = time_not_found / not_found
232                interval_time = time_found + time_not_found
233                s = "%(c)d: %(found)d/%(not_found)d " % vars()
234                s += "%(interval_time)6.2f %(itf)6.4f/%(itnf)6.4f " % vars()
235                s += "%(t_min)6.4f/%(t_max)6.4f" %  vars()
236                print s
237                t_found += found
238                t_not_found += not_found
239                t_time_found += time_found
240                t_time_not_found += time_not_found
241                time_found = time_not_found = 0.0
242                found = not_found = 0
243        # t_found += found
244        # t_not_found += not_found
245        elapse = time.time() - elapse
246        itf = 0.0
247        if t_found:
248            itf = t_time_found/t_found
249        itnf = 0.0
250        if t_not_found:
251            itnf = t_time_not_found / t_not_found
252        #c_elapse = time.clock() - c_elapse
253        s = "%(probe)d: %(t_found)d/%(t_not_found)d " % vars()
254        s += "%(elapse)6.2f %(itf)6.4f/%(itnf)6.4f " % vars()
255        s += "%(t_min)6.4f/%(t_max)6.4f" %  vars()
256        print "-"*40
257        print s
258        rel_found = float(t_found)/probe
259        rel_not_found = float(t_not_found)/probe
260        estimated_total_time = num_objects*(rel_found*itf + rel_not_found*itnf)
261        print estimated_total_time
262    ###)
263
264    security.declareProtected(ModifyPortalContent,'writeLog') ###(
265    def writeLog(self,logfile,s):
266        """write to the log file"""
267        logfile.write(s)
268
269###)
270
271    def generateStudentId(self,letter): ###(
272        import random
273        r = random
274        ##if letter not in ('ABCDEFGIHKLMNOPQRSTUVWXY'):
275        if letter == '?':
276            letter= r.choice('ABCDEFGHKLMNPQRSTUVWXY')
277        sid = "%c%d" % (letter,r.randint(99999,1000000))
278        students = self.portal_url.getPortalObject().campus.students
279##        while hasattr(students, sid):
280##            sid = "%c%d" % (letter,r.randint(99999,1000000))
281        while self.students_catalog(id = sid):
282            sid = "%c%d" % (letter,r.randint(99999,1000000))
283        return sid
284    ###)
285
286    def generatePassword(self,s=None): ###(
287        import random
288        r = random
289        ##if letter not in ('ABCDEFGIHKLMNOPQRSTUVWXY'):
290        if s is None:
291            s = 'abcdefghklmnpqrstuvwxy23456789'
292        pw = ''
293        while len(pw) < 6:
294            pw += r.choice(s)
295        return pw
296    ###)
297
298    security.declareProtected(ManagePortal, 'removeDeletedDocIds') ###(
299    def removeDeletedDocIds(self, max=1000):
300        """
301        remove deleted docids from repository commit after max
302        """
303        logger = logging.getLogger('WAeUPTool.removeDeletedDocIds')
304        repository = getToolByName(self, 'portal_repository')
305        pxtool = getToolByName(self, 'portal_proxies')
306        pxtool_infos = pxtool.getRevisionsUsed()
307
308        nb_revs = 0
309        docids_d = {} # all docids
310        unused_docids_d = {} # all docids that are unused
311        ids_unused_revs_docids = [] # ids for revs of unused docids
312        ids_unused_revs = [] # ids for unused revs
313        total = 0
314        idlist = repository.objectIds()
315        for id in idlist:
316            docid, rev = repository._splitId(id)
317            if docid is None:
318                logger.info("invalid doc_id %s" % docid)
319                continue
320            nb_revs += 1
321            docids_d[docid] = None
322            if not pxtool_infos.has_key(docid):
323                unused_docids_d[docid] = None
324                ids_unused_revs_docids.append(id)
325                ids_unused_revs.append(id)
326            elif not pxtool_infos[docid].has_key(rev):
327                ids_unused_revs.append(id)
328            if len(ids_unused_revs) >= max:
329                repository.manage_delObjects(ids_unused_revs)
330                #import pdb;pdb.set_trace()
331                transaction.commit()
332                total += max
333                logger.info('removed %d total %d unused docids ' % (max,total))
334        anz = len(ids_unused_revs)
335        if anz > 0:
336            repository.manage_delObjects(ids_unused_revs)
337            transaction.commit()
338            total += anz
339            logger.info('finished removing %d unused docids ' % (total))
340
341
342###)
343
344    security.declareProtected(View,'getCredential') ###(
345    def getCredential(self,student_id):
346        "return a student password"
347        student_entry = getattr(self.portal_directories.students,student_id,None)
348        if student_entry is None:
349            return None
350        return getattr(student_entry,"password","not set")
351    ###)
352
353    security.declarePublic('checkPassword') ###(
354    def checkPassword(self,student_id,password):
355        "return a student password"
356        student_entry = getattr(self.portal_directories.students,student_id,None)
357        if student_entry is None:
358            return False
359        return getattr(student_entry,"password","not set") == password
360    ###)
361
362    security.declarePublic('editPassword') ###(
363    def editPassword(self,student_id,password):
364        "edit a student password"
365        student_entry = getattr(self.portal_directories.students,student_id,None)
366        if student_entry is None:
367            return
368        setattr(student_entry,'password',password)
369    ###)
370
371    security.declareProtected(View,'doCommit') ###(
372    def doCommit(self,logger=None):
373        "commit some transactions"
374        transaction.commit()
375    ###)
376
377    security.declarePublic('loadStudentFoto') ###(
378    def loadStudentFoto(self,student,filename,folder):
379        "return a student passport picture"
380        #import pdb;pdb.set_trace()
381        picture ="%s/import/%s/%s" % (i_home,folder,filename)
382        student_id = student.getId()
383        images_dir = os.path.join("%s" % images_base,student_id)
384        if not os.path.exists(images_dir):
385            os.mkdir(images_dir)
386        image_name = os.path.join(images_dir,"passport_%(student_id)s.jpg" % vars())
387        for extension in ('.jpg','.JPG'):
388            fullname = "%(picture)s%(extension)s" % vars()
389            if os.path.exists(fullname):
390                copy2(fullname,image_name)
391                return "successfully copied passport picture"
392        return "passport picture not found: %s.jpg or .JPG" % picture
393    ###)
394
395    def old____loadStudentFoto(self,student,filename,folder): ###(
396        "return a student passport picture"
397        app = student.application
398        app_doc = app.getContent()
399        #clear = student.clearance
400        #clear_doc = clear.getContent()
401        #matric_no = clear_doc.matric_no.upper()
402        picture1 ="%s/import/%s/%s.jpg" % (i_home,folder,filename)
403        picture2 ="%s/import/%s/%s.JPG" % (i_home,folder,filename)
404        #import pdb;pdb.set_trace()
405        if os.path.exists(picture1):
406            file = open(picture1)
407        elif os.path.exists(picture2):
408            file = open(picture2)
409        else:
410            return "passport picture not found %s" % picture1
411        reopened = False
412        if self.portal_workflow.getInfoFor(app,'review_state',None) !='opened':
413            self.portal_workflow.doActionFor(app,'open')
414            reopened = True
415        outfile = file.read()
416        app_doc.manage_addFile('passport',
417                               file=outfile,
418                               title="%s.jpg" % filename)
419        if reopened:
420            self.portal_workflow.doActionFor(app,'close')
421        return "successfully loaded passport picture"
422    ###)
423
424    security.declareProtected(ModifyPortalContent,'createOne') ###(
425    def createOne(self,students_folder,student_brain,letter,commit=False):
426        sid = self.waeup_tool.generateStudentId(letter)
427        students_folder.invokeFactory('Student', sid)
428        student = getattr(students_folder,sid)
429        self.portal_workflow.doActionFor(student,'return')
430        student.manage_setLocalRoles(sid, ['Owner',])
431        matric_no = student_brain.matric_no
432        jamb_reg_no = student_brain.Entryregno
433        self.students_catalog.addRecord(id = sid,
434                                           matric_no = matric_no,
435                                           jamb_reg_no = jamb_reg_no,
436                                           sex = student_brain.Sex == "F",
437                                           name = "%s %s %s" % (student_brain.Firstname,
438                                                                student_brain.Middlename,
439                                                                student_brain.Lastname)
440                                        )
441        if commit:
442            transaction.commit()
443        return sid,jamb_reg_no
444    ###)
445
446    security.declareProtected(ModifyPortalContent,'addStudent') ###(
447    def addStudent(self,dict):
448        students_folder = self.portal_url.getPortalObject().campus.students
449        sid = self.waeup_tool.generateStudentId('?')
450        students_folder.invokeFactory('Student', sid)
451        student_obj = getattr(students_folder,sid)
452        f2t = self.student_field2types
453        #from pdb import set_trace; set_trace()
454        d = {}
455        #d['jamb_sex']  = 'M'
456        #if dict.get('sex'):
457        #    d['jamb_sex']  = 'F'
458
459        entry_session = dict.get('entry_session')
460        if entry_session == self.getSessionId()[0]:
461            wfaction = 'admit'
462            wft = 'wf_transition_admit'
463            password = None
464        else:
465            wfaction = 'return'
466            wft = 'wf_transition_return'
467            password = self.generatePassword()
468            self.makeStudentMember(sid,password)
469
470        for pt in f2t.keys():
471            student_obj.invokeFactory(pt,f2t[pt]['id'])
472            sub_obj = getattr(student_obj,f2t[pt]['id'])
473            sub_doc = sub_obj.getContent()
474            #self.portal_workflow.doActionFor(sub_obj,'open',dest_container=sub_obj)
475            d['Title'] = f2t[pt]['title']
476            for field in f2t[pt]['fields']:
477                d[field] = dict.get(field,'')
478            sub_doc.edit(mapping = d)
479            new_state = f2t[pt][wft]
480            if new_state != "remain":
481                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
482        self.portal_workflow.doActionFor(student_obj,wfaction)
483        student_obj.manage_setLocalRoles(sid, ['Owner',])
484        return sid,password
485    ###)
486
487    security.declarePublic('getCertificateBrain') ###(
488    def getCertificateBrain(self,cert_id):
489        "do it"
490        res = ZCatalog.searchResults(self.portal_catalog_real,
491                                {'portal_type':"Certificate",
492                                      'id': cert_id})
493        if res:
494            return res[0]
495        return None
496    ###)
497
498    security.declareProtected(ModifyPortalContent,'get_csv_filenames') ###(
499    def get_csv_filenames(self):
500        "do it"
501        files = [file for file in os.listdir("%s/import/" % (i_home))
502                 if file.endswith('.csv') and file.find('imported') == -1]
503        return files
504    ###)
505
506    security.declarePublic('findStudentByMatricelNo') ###(
507    def findStudentByMatricelNo(self,matric_no):
508        "do it"
509        res = ZCatalog.searchResults(self.portal_catalog_real,
510                                {'portal_type':"StudentClearance",
511                                 'SearchableText': matric_no})
512        if res:
513            return res[0]
514        return None
515    ###)
516
517    security.declarePublic('makeStudentMember') ###(
518    def makeStudentMember(self,sid,password='uNsEt'):
519        """make the student a member"""
520        membership = self.portal_membership
521        membership.addMember(sid,
522                             password ,
523                             roles=('Member',
524                                     'Student',
525                                     ),
526                             domains='',
527                             properties = {'memberareaCreationFlag': False,
528                                           'homeless': True},)
529        member = membership.getMemberById(sid)
530        self.portal_registration.afterAdd(member, sid, password, None)
531        #self.manage_setLocalRoles(sid, ['Owner',])
532    ###)
533
534    security.declarePublic('makeStudentData') ###(
535    def makeStudentData(self,student_id,email=None,phone_nr=None):
536        "create Datastructure for a returning Student"
537        #import pdb;pdb.set_trace()
538        logger = logging.getLogger('WAeUPTool.makeStudentData')
539        students_folder = self.portal_url.getPortalObject().campus.students
540        #res = self.students_catalog(id=student_id)
541        #if res:
542        #    st = res[0]
543        #res = self.returning_import(matric_no = st.matric_no)
544        res = self.returning_import(id = student_id)
545        if res:
546            student = res[0]
547        else:
548            logger.info('Id %s not found in returning_import' % student_id)
549            return
550        logger.info('%s creates data structure' % student_id)
551        s_results = self.results_import(matric_no = student.matric_no)
552        if s_results:
553            lnr = self.getLevelFromResultsCosCode(s_results)
554            level = "%d00" % lnr
555            verdict,eligible = self.getVerdict(s_results[0].Verdict)
556            #if eligible:
557            #    level = "%d00" % (lnr + 1)
558        else:
559            logger.info('matric_no %s not found in results_import' % student.matric_no)
560            level = 0
561            verdict = 'N/A'
562        #student should not be allowed to perform this transition
563        #wftool = self.portal_workflow
564        #wftool.doActionFor(student,'return')
565        certcode_org = student.Coursemajorcode
566        certcode = makeCertificateCode(certcode_org)
567        certificate_brain = self.getCertificateBrain(certcode)
568        if not certificate_brain:
569            em = 'Certificate %s org-code %s not found\n' % (certcode, certcode_org)
570            logger.info(em)
571        matric_no = student.matric_no
572        sid = student_id
573        student_obj = getattr(students_folder,sid)
574        student_obj.invokeFactory('StudentApplication','application')
575        application = student_obj.application
576        self.portal_workflow.doActionFor(application,'open',dest_container=application)
577        da = {'Title': 'Application Data'}
578        student_obj.invokeFactory('StudentPersonal','personal')
579        da['jamb_reg_no'] = student.Entryregno
580        em = self.getEntryMode(student.Entryregno)
581        da['entry_mode'] = em
582        personal = student_obj.personal
583        self.portal_workflow.doActionFor(personal,'open',dest_container=personal)
584        dp = {'Title': 'Personal Data'}
585        student_obj.invokeFactory('StudentClearance','clearance')
586        clearance = student_obj.clearance
587        self.portal_workflow.doActionFor(clearance,'open',dest_container=clearance)
588        dc = {'Title': 'Clearance/Eligibility Record'}
589        dc['matric_no'] = matric_no
590        state = student.State
591        lga = student.LGA
592        if state and lga:
593            lga =  state + ' / ' + lga
594        else:
595            lga = "None"
596        da['jamb_lga'] = dc['lga'] = lga
597        da['app_email'] = dp['email'] = email
598        da['app_mobile'] = dp['phone'] = phone_nr
599        dp['firstname'] = student.Firstname
600        dp['middlename'] = student.Middlename
601        dp['lastname'] = student.Lastname
602        da['jamb_lastname'] = "%s %s %s" % (student.Firstname,student.Middlename,student.Lastname)
603        da['jamb_sex'] = student.Sex
604        dp['sex'] = student.Sex == 'F'
605        dp['perm_address'] = student.Permanent_Address
606        application.getContent().edit(mapping=da)
607        self.portal_workflow.doActionFor(application,'close',dest_container=application)
608        personal.getContent().edit(mapping=dp)
609        clearance.getContent().edit(mapping=dc)
610        self.portal_workflow.doActionFor(clearance,'close',dest_container=clearance)
611        #
612        # Study Course
613        #
614        student_obj.invokeFactory('StudentStudyCourse','study_course')
615        studycourse = student_obj.study_course
616        self.portal_workflow.doActionFor(studycourse,'open',dest_container=studycourse)
617        dsc = {}
618        dsc['study_course'] = certcode
619        dsc['current_level'] = level
620        dsc['current_verdict'] = verdict
621        dsc['current_mode'] = em
622        dsc['current_session'] = '05'
623        studycourse.getContent().edit(mapping=dsc)
624        #
625        # Level
626        #
627##        l = getattr(studycourse,level,None)
628##        if l is None:
629##            studycourse.invokeFactory('StudentStudyLevel', level)
630##            l = getattr(studycourse, level)
631##            self.portal_workflow.doActionFor(l,'open',dest_container=l)
632##            l.getContent().edit(mapping={'Title': "Level %s" % level})
633###)
634
635    security.declarePublic('admitOneStudent') ###(
636    def admitOneStudent(self,brain):
637        "create Datastructure for an admitted Student"
638        #import pdb;pdb.set_trace()
639        students_folder = self.portal_url.getPortalObject().campus.students
640        logger = logging.getLogger('WAeUPTool.admitStudent')
641        if brain.status != "admitted":
642            logger.info('status of %s is %s' % (brain.reg_no,brain.status))
643        pin_parts = brain.pin.split('-')
644        if len(pin_parts) != 3:
645            logger.info('invalid pin %s for %s' % (brain.pin,brain.reg_no))
646        student_id = self.generateStudentId(letter)
647        students_folder.invokeFactory('Student', student_id)
648        student_object = getattr(students_folder,student_id)
649        password = pin_parts[2]
650        self.makeStudentMember(student_id,password = password)
651        student.manage_setLocalRoles(sid, ['Owner',])
652        logger.info("creating %s reg_no %s" % (student_id,brain.reg_no))
653        #
654        # application
655        #
656        student_obj.invokeFactory('StudentApplication','application')
657        application = student_obj.application
658        self.portal_workflow.doActionFor(application,'open',dest_container=application)
659        da = {'Title': 'Application Data'}
660        da['jamb_reg_no'] = brain.reg_no
661        da['entry_mode'] = brain.entry_mode
662        da['jamb_lastname'] = brain.lastname
663        da['jamb_sex'] = brain.sex
664        da['jamb_lga'] = brain.lga
665        da['app_email'] = brain.email
666        da['app_mobile'] = brain.phone_nr
667        application.getContent().edit(mapping=da)
668        self.portal_workflow.doActionFor(application,'close',dest_container=application)
669        #
670        # personal
671        #
672        student_obj.invokeFactory('StudentPersonal','personal')
673        personal = student_obj.personal
674        self.portal_workflow.doActionFor(personal,'open',dest_container=personal)
675        dp = {'Title': 'Personal Data'}
676        dp['sex'] = brain.sex
677        dp['email'] = brain.email
678        dp['phone'] = brain.phone_nr
679        dp['lastname'] = brain.lastname
680        personal.getContent().edit(mapping=dp)
681        #
682        # clearance
683        #
684        student_obj.invokeFactory('StudentClearance','clearance')
685        clearance = student_obj.clearance
686        self.portal_workflow.doActionFor(clearance,'open',dest_container=clearance)
687        dc = {'Title': 'Clearance/Eligibility Record'}
688        dc['lga'] = brain.lga
689        clearance.getContent().edit(mapping=dc)
690        self.portal_workflow.doActionFor(clearance,'close',dest_container=clearance)
691        #
692        # study Course
693        #
694        student_obj.invokeFactory('StudentStudyCourse','study_course')
695        studycourse = student_obj.study_course
696        self.portal_workflow.doActionFor(studycourse,'open',dest_container=studycourse)
697        dsc = {}
698        dsc['study_course'] = brain.course_admitted
699        dsc['current_verdict'] = ''
700        dsc['current_mode'] = brain.entry_mode
701        studycourse.getContent().edit(mapping=dsc)
702        #
703        # payments folder
704        student_obj.invokeFactory('PaymentsFolder','payments')
705        payments = getattr(student_obj,'payments')
706        dpay = {}
707        dpay['Title'] = 'Payments'
708        payments.getContent().edit(mapping=dpay)
709        wftool.doActionFor(payments,'open')
710        return student_id
711    ###)
712
713    security.declarePublic('makeStudentLevel') ###(
714    def makeStudentLevel(self,student_id):
715        "create the StudyLevel for a returning Student"
716        #import pdb;pdb.set_trace()
717        logger = logging.getLogger('WAeUPTool.makeStudentLevel')
718        students_folder = self.portal_url.getPortalObject().campus.students
719        res = self.students_catalog(id=student_id)
720        if res:
721            st = res[0]
722        course = st.course
723        matric_no = st.matric_no
724        level = st.level
725        res = self.results_import(matric_no = matric_no)
726        if res:
727            results = res
728        logger.info('%s creating Level %s' % (student_id,level))
729        #
730        # Level
731        #
732        student_obj = getattr(self.portal_url.getPortalObject().campus.students,student_id)
733        studycourse = getattr(student_obj,"study_course",None)
734        self.portal_workflow.doActionFor(studycourse,'close_for_edit',dest_container=studycourse)
735        l = getattr(studycourse,level,None)
736        if l is None:
737            studycourse.invokeFactory('StudentStudyLevel', level)
738            l = getattr(studycourse, level)
739            self.portal_workflow.doActionFor(l,'open',dest_container=l)
740            l.getContent().edit(mapping={'Title': "Level %s" % level})
741        ###)
742
743    security.declarePublic('getHallInfo') ###(
744    def getHallInfo(self,bed):
745        """return Hall Info"""
746        info = {}
747        hall,block,room,letter = bed.split('_')
748        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="AccoHall",id=hall)
749        if res and len(res) == 1:
750            hall_brain = res[0]
751            hall_doc = hall_brain.getObject().getContent()
752        else:
753            return info
754        info['hall_title'] = hall_brain.Title
755        info['maintenance_code'] = hall_doc.maintenance_code
756        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="ScratchCardBatch")
757        batch_doc = None
758        for brain in res:
759            if brain.id.startswith(info['maintenance_code']):
760                batch_doc = brain.getObject().getContent()
761                break
762        if batch_doc is None:
763            info['maintenance_fee'] = None
764        else:
765            info['maintenance_fee'] = batch_doc.cost
766        return info
767    ###)
768
769    security.declareProtected(ModifyPortalContent,'removePictureFolder') ###(
770    def removePictureFolder(self,student_id):
771        """remove picture_folder by renaming it"""
772        path = 'images'
773        picture_path = os.path.join(i_home,path,student_id)
774        if not os.path.exists(picture_path):
775            return False
776        os.rename(picture_path,picture_path + "_removed")
777        return True
778    ###)
779
780    security.declareProtected(ModifyPortalContent,'restorePictureFolder') ###(
781    def restorePictureFolder(self,student_id):
782        """restore picture_folder by renaming it"""
783        path = 'images'
784        picture_path = os.path.join(i_home,path,student_id)
785        if not os.path.exists(picture_path + "_removed"):
786            return False
787        os.rename(picture_path + "_removed",picture_path)
788        return True
789    ###)
790
791    security.declarePublic('picturesExist') ###(
792    def picturesExist(self, ids,student_id=None):
793        """check if pictures exist in the filesystem"""
794        if student_id is None:
795            student_id = self.getStudentId()
796        if student_id is None:
797            return False
798        picture_path = os.path.join(images_base,student_id)
799        if not os.path.exists(picture_path):
800            return False
801        pictures  = [picture[:picture.rfind('_')] for picture in os.listdir(picture_path)]
802        return set(ids).issubset(set(pictures))
803    ###)
804
805    security.declarePublic('picturesList') ###(
806    def picturesList(self):
807        """check if pictures exist in the filesystem"""
808        path = 'images'
809        student_id = self.getStudentId()
810        picture_path = os.path.join(i_home,path,student_id)
811        if not os.path.exists(picture_path):
812            return []
813        return [picture[:picture.rfind('_')] for picture in os.listdir(picture_path)]
814    ###)
815
816    security.declarePublic('showFsPicture') ###(
817    def showFsPicture(self,path):
818        """return a picture from the filesystem"""
819        picture_path = os.path.join(i_home,path)
820        response = self.REQUEST.RESPONSE
821        #import pdb;pdb.set_trace()
822        registry = getToolByName(self, 'mimetypes_registry')
823        mimetype = str(registry.lookupExtension(path.lower()) or
824                    registry.lookupExtension('file.bin'))
825        if os.path.exists(picture_path):
826            response.setHeader('Content-type',mimetype)
827            return open(picture_path).read()
828        picture_path = os.path.join(i_home,'import',path)
829        if os.path.exists(picture_path):
830            return open(picture_path).read()
831    ###)
832
833    security.declareProtected(ModifyPortalContent,'deleteAllCourses') ###(
834    def deleteAllCourses(self,department="All"):
835        ''' delete the courses'''
836        pm = self.portal_membership
837        member = pm.getAuthenticatedMember()
838
839        if str(member) not in ("henrik","joachim"):
840            return "not possible"
841        if department == "All":
842            res = self.portal_catalog({'meta_type': 'Department'})
843        if len(res) < 1:
844            return "No Departments found"
845
846        deleted = []
847        for dep in res:
848            cf = dep.getObject().courses
849            if cf:
850                cf.manage_delObjects(ids=cf.objectIds())
851                deleted.append("deleted Courses in %s" % dep.getId)
852        return "\r".join(deleted)
853    ###)
854
855    security.declareProtected(ModifyPortalContent,'getLogfileLines') ###(
856    def getLogfileLines(self,filename="event.log",numlines=20):
857        """Get last NUMLINES lines of logfile FILENAME.
858
859        Return last lines' of a file in the instances logfile directory as
860        a list. The number of returned lines equals `numlines' or less. If
861        less than `numlines' lines are available, the whole file ist
862        returned. If the file can not be opened or some other error
863        occurs, empty list is returend.
864        """
865        result = []
866        lines_hit = 0
867
868        # We only handle files in instances' log directory...
869        logpath = os.path.join(i_home, "log")
870        filename = str(os.path.abspath( os.path.join( logpath, filename )))
871        if not filename.startswith( logpath ):
872            # Attempt to access file outside log-dir...
873            return []
874
875        try:
876            fd = file( filename, "rb" )
877        except IOError:
878            return []
879        if not fd:
880            return []
881
882        if os.linesep == None:
883            linesep = '\n'
884        else:
885            linesep = os.linesep
886
887        # Try to find 'numlines' times a lineseparator, searching from end
888        # and moving to the beginning of file...
889        fd.seek( 0, 2) # Move to end of file...
890        while lines_hit < numlines:
891            if fd.read(1) == linesep[-1]: # This moves filedescriptor
892                                          # one step forward...
893                lines_hit += 1
894            try:
895                fd.seek( -2, 1) # Go two bytes back from current pos...
896            except IOError:
897                # We cannot go back two bytes. Maybe the file is too small...
898                break
899        fd.seek(2,1)
900
901        # Read all lines from current position...
902        result = fd.readlines()
903        # Remove line endings...
904        result = [x.strip() for x in result]
905        fd.close()
906        return result
907    ###)
908
909    security.declareProtected(ModifyPortalContent,"getCallbacksFromLog")###(
910    def getCallbacksFromLog(self,filename):
911        """fix Online Payment Transactions from Z2.log entries"""
912        import transaction
913        import random
914        from cgi import parse_qs
915        from urlparse import urlparse
916        #from pdb import set_trace
917        wftool = self.portal_workflow
918        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
919        students_folder = self.portal_url.getPortalObject().campus.students
920        s = r'(?P<client_ip>\S+) - (?P<member_id>\S+) \['
921        s += r'(?P<date>.*)\] "(?P<get>.*)" (?P<codes>\d+ \d+) "'
922        s += r'(?P<intersw>.*)" "(?P<agent>.*)"'
923        data = re.compile(s)
924        start = True
925        tr_count = 1
926        total = 0
927        #name = 'pume_results'
928        #name = 'epaymentsuccessful_z2log2'
929        name = filename
930        no_import = []
931        imported = []
932        logger = logging.getLogger('WAeUPTool.getFailedTransactions')
933        try:
934            transactions = open("%s/import/%s" % (i_home,name),"rb").readlines()
935        except:
936            logger.error('Error reading %s' % name)
937            return
938        tas = []
939        for line in transactions:
940            dict = {}
941            items = data.search(line)
942            dict['idict'] = idict = items.groupdict()
943            #print idict
944            #from pdb import set_trace;set_trace()
945            urlparsed = urlparse(idict['get'][4:])
946            #print urlparsed
947            path = urlparsed[2].split('/')
948            dict['student_id'] = student_id = path[8]
949            dict['payment_id'] = payment_id = path[10]
950            dict['qs_dict'] = qs_dict = parse_qs(urlparsed[4])
951            tas.append(dict)
952            tr_count += 1
953        return tas
954    ###)
955
956    security.declareProtected(ModifyPortalContent,"importOnlinePaymentTransactions")###(
957    def importOnlinePaymentTransactions(self):
958        """load Online Payment Transactions from CSV values"""
959        import transaction
960        import random
961        #from pdb import set_trace
962        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
963        opt = self.online_payments_import
964        students_folder = self.portal_url.getPortalObject().campus.students
965        start = True
966        tr_count = 1
967        total = 0
968        #name = 'pume_results'
969        name = 'OnlineTransactions'
970        no_import = []
971        imported = []
972        logger = logging.getLogger('WAeUPTool.importOnlinePaymentTransactions')
973        try:
974            transactions = csv.DictReader(open("%s/import/%s.csv" % (i_home,name),"rb"))
975        except:
976            logger.error('Error reading %s.csv' % name)
977            return
978        for pay_transaction in transactions:
979            if start:
980                start = False
981                logger.info('Start loading from %s.csv' % name)
982                s = ','.join(['"%s"' % fn for fn in pay_transaction.keys()])
983                no_import.append('%s,"Error"' % s)
984                format = ','.join(['"%%(%s)s"' % fn for fn in pay_transaction.keys()])
985                format_error = format + ',"%(Error)s"'
986            data = {}
987
988            # format of the first file sent by Tayo
989            #data['datetime'] = date = DateTime.DateTime(pay_transaction['Date'])
990            #data['student_id'] = student_id = pay_transaction['Payer ID']
991            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
992            #data['response_code'] = response_code = pay_transaction['Resp Code']
993            #data['amount'] = amount = pay_transaction['Amount']
994
995            # format of the second file sent by Tayo
996            #data['datetime'] = date = 0
997            #data['student_id'] = student_id = pay_transaction['Payer ID']
998            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
999            #data['response_code'] = response_code = '00'
1000            #data['amount'] = amount = pay_transaction['Amount']
1001
1002            # format of the third file sent by Kehinde
1003            data['datetime'] = date = 0
1004            data['student_id'] = student_id = pay_transaction['customer_id']
1005            data['order_id'] = order_id = pay_transaction['merchant_reference']
1006            data['response_code'] = response_code = '00'
1007            data['amount'] = amount = pay_transaction['Amount']
1008
1009            dup = False
1010            if response_code == "12":
1011                continue
1012            try:
1013                opt.addRecord(**data)
1014            except ValueError:
1015                dup = True
1016            #from pdb import set_trace;set_trace()
1017            if dup:
1018                if response_code == "00":
1019                    try:
1020                        opt.modifyRecord(**data)
1021                    except:
1022                        logger.info("duplicate uid, order_id %(order_id)s, student_id %(student_id)s, response_code %(response_code)s" % data)
1023                        continue
1024                else:
1025                    pay_transaction['Error'] = "Duplicate order_id"
1026                    no_import.append( format_error % pay_transaction)
1027                    logger.info("duplicate order_id %(order_id)s for %(student_id)s %(response_code)s" % data)
1028                    continue
1029            tr_count += 1
1030            if tr_count > 1000:
1031                if len(no_import) > 0:
1032                    open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
1033                             '\n'.join(no_import) + '\n')
1034                    no_import = []
1035                em = '%d transactions committed\n' % (tr_count)
1036                transaction.commit()
1037                regs = []
1038                logger.info(em)
1039                total += tr_count
1040                tr_count = 0
1041        open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
1042                                                '\n'.join(no_import))
1043        return self.REQUEST.RESPONSE.redirect("%s" % self.REQUEST.get('URL1'))
1044    ###)
1045
1046    security.declareProtected(ModifyPortalContent,'mass_create_faculty') ###(
1047    def mass_create_faculty(self,mapping):
1048        "create a faculty"
1049        logger = logging.getLogger('WAeUPTool.mass_create_faculty')
1050        academics_folder = self.portal_url.getPortalObject().campus.academics
1051        fid = mapping['code']
1052        if getattr(academics_folder,fid,None) is not None:
1053            return '', "Faculty with ID: %s exists" % fid
1054        logger.info('Creating Faculty %(code)s, %(title)s' % mapping)
1055        try:
1056            academics_folder.invokeFactory('Faculty', fid)
1057        except BadRequest,E:
1058            return '', "%s" % E
1059        f = getattr(academics_folder,fid,None)
1060        f.getContent().edit(mapping=mapping)
1061        return fid,''
1062    ###)
1063
1064    security.declareProtected(ModifyPortalContent,'mass_edit_faculty') ###(
1065    def mass_edit_faculty(self,mapping):
1066        "edit a faculty"
1067        logger = logging.getLogger('WAeUPTool.mass_edit_faculty')
1068        academics_folder = self.portal_url.getPortalObject().campus.academics
1069        fid = mapping['code']
1070        f = getattr(academics_folder,fid,None)
1071        if f is None:
1072            return '', "Faculty with ID: %s does not exist" % fid
1073        logger.info('Editing Faculty %(code)s, %(title)s' % mapping)
1074        f.getContent().edit(mapping=mapping)
1075        return fid,''
1076    ###)
1077
1078    security.declareProtected(ModifyPortalContent,'mass_create_department') ###(
1079    def mass_create_department(self,mapping):
1080        "create a department in the correct faculty"
1081        logger = logging.getLogger('WAeUPTool.mass_create_department')
1082        fid = mapping['faculty_code']
1083        if getattr(self,'_v_faculties',None) is None:
1084            res = self.portal_catalog(portal_type = "Faculty")
1085            self._v_faculties = {}
1086            for f in res:
1087                self._v_faculties[f.getId] = f.getObject()
1088        f = self._v_faculties.get(fid,None)
1089        if f is None:
1090            return '', "No Faculty with ID: %s" % fid
1091        else:
1092            did = mapping.get('code')
1093            d = getattr(f,did,None)
1094            if d is None or d.portal_type == "Faculty":
1095                logger.info('Creating Department %(code)s, %(title)s' % mapping)
1096                try:
1097                    f.invokeFactory('Department', did)
1098                except BadRequest,E:
1099                    return '', "%s" % E
1100                d = getattr(f,did)
1101                d.invokeFactory('CoursesFolder','courses')
1102                courses = getattr(d,'courses')
1103                dict = {'Title': 'Courses'}
1104                courses.getContent().edit(mapping=dict)
1105                d.invokeFactory('CertificatesFolder','certificates')
1106                certificates = getattr(d,'certificates')
1107                dict = {'Title': 'Certificates'}
1108                certificates.getContent().edit(mapping=dict)
1109            d.getContent().edit(mapping=mapping)
1110        return did,''
1111    ###)
1112
1113    security.declareProtected(ModifyPortalContent,'mass_edit_department') ###(
1114    def mass_edit_department(self,mapping):
1115        "create a department in the correct faculty"
1116        logger = logging.getLogger('WAeUPTool.mass_create_department')
1117        academics_folder = self.portal_url.getPortalObject().campus.academics
1118        fid = mapping['faculty_code']
1119        did = mapping.get('code')
1120        try:
1121            d = getattr(getattr(academics_folder,fid),did,None)
1122        except KeyError:
1123            return '', "Department %s or Faculty %s wrong" % (did,fid)
1124        else:
1125            if d is None or d.portal_type == "Faculty":
1126                logger.info('Editing Department %(code)s, %(title)s' % mapping)
1127            d.getContent().edit(mapping=mapping)
1128        return did,''
1129    ###)
1130
1131    security.declareProtected(ModifyPortalContent,'mass_create_course') ###(
1132    def mass_create_course(self,mapping):
1133        #import pdb;pdb.set_trace()
1134        if getattr(self,'_v_course_list',None) is None:
1135            self._v_course_list = []
1136        if getattr(self,'_v_departments',None) is None:
1137            res = self.portal_catalog(portal_type = "Department")
1138            self._v_department_courses = {}
1139            for d in res:
1140                self._v_department_courses[d.getId] = getattr(d.getObject(),"courses",None)
1141        did = mapping['department_code']
1142        d = self._v_department_courses.get(did,None)
1143        if d is None:
1144            return '', "No Department with ID: %s" % did
1145        course_id = mapping.get('code')
1146        if course_id in self._v_course_list:
1147            return '', "Duplicate Course ID: %s" % did
1148        c = getattr(d,course_id,None)
1149        if c is not None:
1150            return '', "Duplicate Course ID: %s" % did
1151        try:
1152            d.invokeFactory('Course', course_id)
1153        except BadRequest,E:
1154            return '', "%s" % E
1155        self._v_course_list.append(course_id)
1156        c = getattr(d,course_id)
1157        c.getContent().edit(mapping=mapping)
1158        return course_id,''
1159    ###)
1160
1161    security.declareProtected(ModifyPortalContent,'mass_edit_course') ###(
1162    def mass_edit_course(self,mapping):
1163        #import pdb;pdb.set_trace()
1164        course_id = mapping.get('code')
1165        res = self.portal_catalog(id=course_id)
1166        if not res:
1167            return '', "No Course with ID: %s" % course_id
1168        c = res[0].getObject()
1169        c.getContent().edit(mapping=mapping)
1170        return course_id,''
1171    ###)
1172
1173    security.declareProtected(ModifyPortalContent,'mass_create_certificate') ###(
1174    def mass_create_certificate(self,mapping):
1175        if getattr(self,'_v_certificate_list',None) is None:
1176            self._v_certificate_list = []
1177        if getattr(self,'_v_department_certificates',None) is None:
1178            res = self.portal_catalog(portal_type = "Department")
1179            self._v_department_certificates = {}
1180            for d in res:
1181                self._v_department_certificates[d.getId] = getattr(d.getObject(),"certificates",None)
1182        did = mapping['department_code']
1183        d = self._v_department_certificates.get(did,None)
1184        if d is None:
1185            return '', "No Department with ID: %s" % did
1186        certificate_id = mapping.get('code')
1187        if certificate_id in self._v_certificate_list:
1188            return '', "Duplicate Certificate ID: %s" % did
1189        c = getattr(d,certificate_id,None)
1190        if c is not None:
1191            return '', "Duplicate Certificate ID: %s" % did
1192        try:
1193            d.invokeFactory('Certificate', certificate_id)
1194        except BadRequest,E:
1195            return '', "%s" % E
1196        self._v_certificate_list.append(certificate_id)
1197        c = getattr(d,certificate_id)
1198        c.getContent().edit(mapping=mapping)
1199        return certificate_id,''
1200    ###)
1201
1202    security.declareProtected(ModifyPortalContent,'mass_edit_certificate') ###(
1203    def mass_edit_certificate(self,mapping):
1204        #import pdb;pdb.set_trace()
1205        certificate_id = mapping.get('code')
1206        res = self.portal_catalog(id=certificate_id)
1207        if not res:
1208            return '', "No Certificate with ID: %s" % did
1209        c = res[0].getObject()
1210        c.getContent().edit(mapping=mapping)
1211        return certificate_id,''
1212    ###)
1213
1214    security.declareProtected(ModifyPortalContent,'mass_create_application') ###(
1215    def mass_create_application(self,mapping):
1216        #import pdb;pdb.set_trace()
1217        try:
1218            self.applicants_catalog.addRecord(**mapping)
1219        except ValueError:
1220            return '', "applicant already exists: %s" % mapping.get('reg_no')
1221        return key,''
1222    ###)
1223
1224    security.declareProtected(ModifyPortalContent,'mass_edit_application') ###(
1225    def mass_edit_application(self,mapping):
1226        #import pdb;pdb.set_trace()
1227        reg_no = mapping.get('reg_no')
1228        try:
1229            self.applicants_catalog.modifyRecord(**mapping)
1230        except KeyError:
1231            return '', "applicant does not exists: %s" % reg_no
1232        return reg_no,''
1233    ###)
1234
1235    security.declareProtected(ModifyPortalContent,'mass_create_course_result') ###(
1236    def mass_create_course_result(self,mapping):
1237        #import pdb;pdb.set_trace()
1238        if getattr(self,'_v_courses',None) is None:
1239            res = self.courses_catalog()
1240            self._v_courses = {}
1241            for brain in res:
1242                self._v_courses[brain.code] = brain
1243        course_id = mapping.get('code')
1244        if course_id not in self._v_courses.keys():
1245            return '', "No course with ID: %s" % did
1246        id_key = ''
1247        for id_key in ('student_id','matric_no'):
1248            id_field = mapping.get(id_key,None)
1249            if id_field is not None:
1250                student_id = id_field
1251                break
1252        query = Eq(id_key,id_field)
1253        res = self.students_catalog.evalAdvancedQuery(query)
1254        if not res:
1255            return '', "No student with %(id_field)s: %(id_key)s" % vars()
1256        if id_field != "student_id":
1257            mapping['student_id'] = res[0].id
1258        mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % mapping
1259        for k in ('semester',):
1260            mapping[k] = getattr(self._v_courses[course_id],k)
1261        try:
1262            self.course_results.addRecord(**mapping)
1263        except ValueError:
1264            return '', "course result already exists: %s" % key
1265        return key,''
1266    ###)
1267
1268    security.declareProtected(ModifyPortalContent,'mass_edit_course_result') ###(
1269    def mass_edit_course_result(self,mapping):
1270        #import pdb;pdb.set_trace()
1271        # if getattr(self,'_v_courses',None) is None:
1272        #     res = self.courses_catalog()
1273        #     self._v_courses = {}
1274        #     for brain in res:
1275        #         self._v_courses[brain.code] = brain
1276        # course_id = mapping.get('code')
1277        # if course_id not in self._v_courses.keys():
1278        #     return '', "No course with ID: %s" % did
1279        id_key = ''
1280        for id_key in ('student_id','matric_no'):
1281            id_field = mapping.get(id_key,None)
1282            if id_field is not None:
1283                student_id = id_field
1284                break
1285        query = Eq(id_key,id_field)
1286        res = self.students_catalog.evalAdvancedQuery(query)
1287        if not res:
1288            return '', "No student with %(id_field)s: %(id_key)s" % vars()
1289        if id_field != "student_id":
1290            mapping['student_id'] = res[0].id
1291        mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % mapping
1292        # for k in ('semester',):
1293        #     mapping[k] = getattr(self._v_courses[course_id],k)
1294        try:
1295            self.course_results.modifyRecord(**mapping)
1296        except KeyError:
1297            return '', "No course result to edit: %s" % key
1298        return key,''
1299    ###)
1300
1301    security.declareProtected(ModifyPortalContent,'mass_create_certificate_course') ###(
1302    def mass_create_certificate_course(self,mapping):
1303        if getattr(self,'_v_courses',None) is None:
1304            res = self.courses_catalog()
1305            self._v_courses= [course.code for course in res]
1306        if getattr(self,'_v_ceritficates',None) is None:
1307            res = self.portal_catalog(portal_type = "Certificate")
1308            self._v_certificates = {}
1309            for cert in res:
1310                self._v_certificates[cert.getId] = cert.getObject()
1311        certificate_course_id = mapping.get('code')
1312        if certificate_course_id not in self._v_courses:
1313            return '', "No Course with ID: %s" % certificate_course_id
1314        cert_id = mapping['certificate_code']
1315        cert = self._v_certificates.get(cert_id,None)
1316        if cert is None:
1317            return '', "No Certificate with ID: %s" % cert_id
1318        level_id = mapping.get('level')
1319        level = getattr(cert,level_id,None)
1320        if level is None:
1321            cert.invokeFactory('StudyLevel', level_id)
1322            level = getattr(cert,level_id,None)
1323        elif hasattr(level,certificate_course_id):
1324            return '', "Duplicate CertificateCourse ID: %s in %s/ %s" %\
1325            (certificate_course_id,cert_id,level_id)
1326        level.invokeFactory('CertificateCourse', certificate_course_id)
1327        c = getattr(level,certificate_course_id)
1328        c.getContent().edit(mapping=mapping)
1329        return certificate_course_id,''
1330    ###)
1331
1332    field2types_student = {   ###(
1333                      'StudentApplication':
1334                          {'id': 'application',
1335                           'title': 'Application Data',
1336                           'wf_transition_return': 'close',
1337                           'wf_transition_admit': 'remain',
1338                           'fields':
1339                             ('jamb_reg_no',
1340                              'entry_mode',
1341                              'entry_session',
1342                              'jamb_score',
1343                              'app_email',
1344                              'jamb_age',
1345                              'jamb_state',
1346                              'jamb_lga',
1347                              )
1348                              },
1349                      #'StudentPume':
1350                      #    {'id': 'pume',
1351                      #     'title': 'Pume Data',
1352                      #     'wf_transition_return': 'close',
1353                      #     'wf_transition_admit': 'close',
1354                      #     'fields':
1355                      #       ('pume_score',
1356                      #        )
1357                      #        },
1358                      'StudentClearance':
1359                          {'id': 'clearance',
1360                           'title': 'Clearance/Eligibility Record',
1361                           'wf_transition_return': 'close',
1362                           'wf_transition_admit': 'remain',
1363                           'fields':
1364                             ('matric_no',
1365                              'nationality',
1366                              'lga',
1367                              'birthday',
1368                              )
1369                              },
1370                         'StudentPersonal':
1371                          {'id': 'personal',
1372                           'title': 'Personal Data',
1373                           'wf_transition_return': 'open',
1374                           'wf_transition_admit': 'remain',
1375                           'fields':
1376                             ('firstname',
1377                              'middlename',
1378                              'lastname',
1379                              'sex',
1380                              'email',
1381                              'phone',
1382                              'perm_address',
1383                              )
1384                              },
1385                         'StudentStudyCourse':
1386                          {'id': 'study_course',
1387                           'title': 'Study Course',
1388                           'wf_transition_return': 'open',
1389                           'wf_transition_admit': 'remain',
1390                           'fields':
1391                             ('study_course',
1392                              'current_level',
1393                              'current_session',
1394                              'current_mode',
1395                              'current_verdict',
1396                              'previous_verdict',
1397                              )
1398                              },
1399
1400                         'PaymentsFolder':
1401                          {'id': 'payments',
1402                           'title': 'Payments',
1403                           'wf_transition_return': 'open',
1404                           'wf_transition_admit': 'open',
1405                           'fields':
1406                             ()
1407                              },
1408                         }
1409    ###)
1410
1411    security.declareProtected(ModifyPortalContent,'mass_create_student') ###(
1412    def mass_create_student(self,mapping):
1413        "create a students record due import"
1414        logger = logging.getLogger('WAeUPTool.mass_create_student')
1415        students_folder = self.portal_url.getPortalObject().campus.students
1416        jamb_reg_no = mapping.get('jamb_reg_no',None)
1417        if jamb_reg_no:
1418            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
1419            if res:
1420                return '',"jamb_reg_no exists"
1421        matric_no = mapping.get('matric_no',None)
1422        if matric_no:
1423            res = self.students_catalog(matric_no = matric_no)
1424            if res:
1425                return '',"matric_no exists"
1426        sid = self.waeup_tool.generateStudentId('?')
1427        students_folder.invokeFactory('Student', sid)
1428        student_obj = getattr(students_folder,sid)
1429        f2t = self.field2types_student
1430        d = {}
1431        transition = mapping.get('reg_transition','admit')
1432        if transition not in ('admit','return'):
1433            return '',"no valid transition provided"
1434        for pt in f2t.keys():
1435            student_obj.invokeFactory(pt,f2t[pt]['id'])
1436            sub_obj = getattr(student_obj,f2t[pt]['id'])
1437            sub_doc = sub_obj.getContent()
1438            d['Title'] = f2t[pt]['title']
1439            for field in f2t[pt]['fields']:
1440                d[field] = mapping.get(field,'')
1441
1442            if pt == "StudentApplication":
1443                d['jamb_sex']  = 'M'
1444                if mapping.get('sex'):
1445                    d['jamb_sex']  = 'F'
1446                d['jamb_firstname'] = mapping.get('firstname',None)
1447                d['jamb_middlename'] = mapping.get('middlename',None)
1448                d['jamb_lastname'] = mapping.get('lastname',None)
1449
1450            # if pt == "StudyCourse":
1451            #     for von,zu in (('entry_mode','current_mode'),
1452            #                    ('entry_session','current_session')):
1453            #         if mapping.get(zu,None) is None and mapping.get(von,None) is not None:
1454            #             d[zu] = mapping[von]
1455            sub_doc.edit(mapping = d)
1456
1457            #import pdb;pdb.set_trace()
1458            new_state = f2t[pt]['wf_transition_%(transition)s' % vars()]
1459            if new_state != "remain":
1460                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
1461        self.portal_workflow.doActionFor(student_obj,transition)
1462        student_obj.manage_setLocalRoles(sid, ['Owner',])
1463        return sid,''
1464    ###)
1465
1466    security.declareProtected(ModifyPortalContent,'mass_edit_student') ###(
1467    def mass_edit_student(self,mapping):
1468        "edit a students record due import"
1469        logger = logging.getLogger('WAeUPTool.mass_edit_student')
1470        students_folder = self.portal_url.getPortalObject().campus.students
1471        sid = mapping.get('id',None)
1472        jamb_reg_no = mapping.get('jamb_reg_no',None)
1473        matric_no = mapping.get('matric_no',None)
1474        editable_keys = mapping.keys()
1475        if sid:
1476            res = self.students_catalog(id = sid)
1477            if not res:
1478                return '',"no student with id %s" % sid
1479            if matric_no and res[0].matric_no and\
1480              matric_no != res[0].matric_no:
1481                logger.info("%s, old matric_no %s overwritten with %s" % (res[0].id,res[0].matric_no,matric_no))
1482            if jamb_reg_no and res[0].jamb_reg_no and\
1483              jamb_reg_no != res[0].jamb_reg_no:
1484                logger.info("%s, old reg_no %s overwritten with %s" % (res[0].id,res[0].jamb_reg_no,jamb_reg_no))
1485        elif jamb_reg_no:
1486            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
1487            if not res:
1488                return '',"no student with jamb_reg_no %s" % jamb_reg_no
1489            editable_keys.remove('jamb_reg_no')
1490        elif matric_no:
1491            res = self.students_catalog(matric_no = matric_no)
1492            if not res:
1493                return '',"no student with matric_no %s" % matric_no
1494            editable_keys.remove('matric_no')
1495
1496        ## included only to change wf state from admitted to returning
1497        #if res[0].review_state not in ('admitted','objection_raised'):
1498        #    return '%s' % res[0].id ,"student is not in state admitted or objection_raised"
1499        ## end inclusion
1500
1501        sid = res[0].id
1502        student_obj = getattr(students_folder,sid)
1503        f2t = self.field2types_student
1504        d = {}
1505        #import pdb;pdb.set_trace()
1506        any_change = False
1507        for pt in f2t.keys():
1508            if pt == "student_application":
1509                d['jamb_sex']  = 'M'
1510                if mapping.get('sex'):
1511                    d['jamb_sex']  = 'F'
1512            intersect = set(f2t[pt]['fields']).intersection(set(editable_keys))
1513            if intersect:
1514                sub_obj = getattr(student_obj,f2t[pt]['id'],None)
1515                if sub_obj is None:
1516                    try:
1517                        student_obj.invokeFactory(pt,f2t[pt]['id'])
1518                    except:
1519                        continue
1520                    sub_obj = getattr(student_obj,f2t[pt]['id'])
1521                    d['Title'] = f2t[pt]['title']
1522                sub_doc = sub_obj.getContent()
1523                for field in intersect:
1524                    changed = False
1525                    if getattr(sub_doc,field,None) != mapping.get(field,''):
1526                        any_change = True
1527                        changed = True
1528                        d[field] = mapping.get(field,'')
1529                    if changed:
1530                        sub_doc.edit(mapping = d)
1531
1532
1533        ## included only to change wf state from admitted to returning
1534        #    if res[0].review_state in ('admitted','objection_raised'):
1535        #        new_state = f2t[pt]['wf_transition_return']
1536        #        sub_obj = getattr(student_obj,f2t[pt]['id'],None)
1537        #        if sub_obj and new_state != "remain":
1538        #            try:
1539        #                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
1540        #            except:
1541        #                #logger.info('%s, wf transition %s of %s failed' % (sid,new_state,sub_obj.id))
1542        #                pass
1543        #if res[0].review_state in ('admitted','objection_raised'):
1544        #    wfaction = 'return'
1545        #    try:
1546        #        self.portal_workflow.doActionFor(student_obj,wfaction)
1547        #        logger.info('%s, wf state changed' % sid)
1548        #        any_change = True
1549        #    except:
1550        #        logger.info('%s, wf transition failed, old state = %s' % (sid,res[0].review_state))
1551        #        pass
1552        ## end inclusion
1553
1554
1555        if any_change:
1556            return sid,''
1557        else:
1558            return sid,'not modified'
1559    ###)
1560
1561    security.declareProtected(ModifyPortalContent,"importData")###(
1562    def importData(self,filename,name,edit=False,bypass_queue_catalog=False):
1563        """load data from CSV values"""
1564        import transaction
1565        import random
1566
1567        pm = self.portal_membership
1568        member = pm.getAuthenticatedMember()
1569
1570        logger = logging.getLogger('WAeUPTool.importData')
1571        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
1572        students_folder = self.portal_url.getPortalObject().campus.students
1573        start = True
1574        tr_count = 0
1575        total_imported = 0
1576        total_not_imported = 0
1577        total = 0
1578        iname = "import_%s" % name
1579        if iname == 'import_application':
1580            commit_after = 2000
1581        else:
1582            commit_after = 100
1583        stool = getToolByName(self, 'portal_schemas')
1584        ltool = getToolByName(self, 'portal_layouts')
1585        schema = stool._getOb(iname)
1586        if schema is None:
1587            em = 'No such schema %s' % iname
1588            logger.error('No such schema %s' % iname)
1589            return em
1590        layout = ltool._getOb(iname)
1591        if layout is None:
1592            em = 'No such layout %s' % iname
1593            logger.error(em)
1594            return em
1595        validators = {}
1596        for widget in layout.keys():
1597            validators[widget] = layout[widget].validate
1598        #import pdb;pdb.set_trace()
1599        mode = "create"
1600        if edit:
1601            mode = "edit"
1602        importer_name = "mass_%(mode)s_%(name)s" % vars()
1603        importer = getattr(self, '%s' % importer_name,None)
1604        if importer is None:
1605            em = 'No importer function %s' % importer_name
1606            logger.error(em)
1607            return em
1608        not_imported = []
1609        imported = []
1610        try:
1611            items = csv.DictReader(open("%s/import/%s.csv" % (i_home,filename),"rb"))
1612        except:
1613            em = 'Error reading %s.csv' % filename
1614            logger.error(em)
1615            return em
1616        for item in items:
1617            if start:
1618                start = False
1619                adapters = [MappingStorageAdapter(schema, item)]
1620                logger.info('%s starts import from %s.csv in %s mode with schema and layout %s' % (member,filename,mode,iname))
1621                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb")).next()
1622                import_keys = [k for k in attrs if not k.startswith('ignore')]
1623                diff2schema = set(import_keys).difference(set(schema.keys()))
1624                diff2layout = set(import_keys).difference(set(layout.keys()))
1625                if diff2schema:
1626                    em = 'not ignorable key(s): "%s" found in heading' % ", ".join(diff2schema)
1627                    return em
1628                if mode == "create":
1629                    required_keys = [layout.getIdUnprefixed(id)
1630                                     for id,widget in layout.objectItems()
1631                                     if widget.is_required]
1632                    if not set(required_keys).issubset(set(import_keys)):
1633                        diff2import = set(required_keys).difference(set(import_keys))
1634                        em = 'required key(s): "%s" not found in heading' % ", ".join(diff2import)
1635                        return em
1636                s = ','.join(['"%s"' % fn for fn in import_keys])
1637                open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(s + ',"Error"'+ '\n')
1638                s = '"id",' + s
1639                open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(s + '\n')
1640                format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
1641                format_error = format + ',"%(Error)s"'
1642                format = '"%(id)s",'+ format
1643
1644            dm = DataModel(item, adapters,context=self)
1645            ds = DataStructure(data=item,datamodel=dm)
1646            error_string = ""
1647            total += 1
1648            for k in import_keys:
1649                if not validators[k](ds,mode=mode):
1650                    error_string += " %s : %s" % (k,
1651                                                  self.translation_service(ds.getError(k),
1652                                                                           ds.getErrorMapping(k)))
1653            if not error_string:
1654                temp_item = item.copy()
1655                temp_item.update(dm)
1656                temp_item['id'],error = importer(temp_item)
1657                if error:
1658                    error_string += error
1659                else:
1660                    item = temp_item
1661            if error_string:
1662                item['Error'] = error_string
1663                not_imported.append(format_error % item)
1664                total_not_imported += 1
1665            else:
1666                em = format % item
1667                imported.append(em)
1668                tr_count += 1
1669                total_imported += 1
1670                logger.info("%(total_imported)d of %(total)d %(em)s" % vars())
1671
1672            if total and not total % commit_after:
1673                transaction.commit()
1674                if len(not_imported) > 0:
1675                    open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
1676                             '\n'.join(not_imported) + '\n')
1677                    not_imported = []
1678                if len(imported) > 0:
1679                    open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
1680                             '\n'.join(imported) + '\n')
1681                    imported = []
1682                em = '%d transactions committed\n' % (tr_count)
1683                regs = []
1684                logger.info(em)
1685                tr_count = 0
1686                #if total > 100:
1687                #    return
1688        if len(imported) > 0:
1689            open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
1690                                                '\n'.join(imported))
1691        if len(not_imported) > 0:
1692            open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
1693                                                '\n'.join(not_imported))
1694        em = "Finished: %d imported, %d not imported (of total %d)" % (total_imported,total_not_imported,total)
1695        logger.info(em)
1696        return em
1697    ###)
1698
1699    security.declareProtected(ModifyPortalContent,"moveImagesToFS")###(
1700    def moveImagesToFS(self,student_id="O738726"):
1701        "move the images to the filesystem"
1702        images_dir = os.path.join("%s" % images_base,student_id)
1703        student_folder = getattr(self.portal_url.getPortalObject().campus.students,student_id)
1704        stool = getToolByName(self, 'portal_schemas')
1705        schemas = ['student_application',
1706                   'student_clearance',
1707                   ]
1708        created = False
1709        for schema_id in schemas:
1710            schema = stool._getOb(schema_id)
1711            object = getattr(student_folder,schema_id[len('student_'):],None)
1712            if object is None:
1713                continue
1714            doc = object.getContent()
1715            for key in schema.keys():
1716                if schema[key].meta_type != "CPS Image Field":
1717                    continue
1718                #import pdb;pdb.set_trace()
1719                image = getattr(doc,key,None)
1720                if not image or not hasattr(image,"data"):
1721                    continue
1722                if not created:
1723                    if not os.path.exists(images_dir):
1724                        os.mkdir(images_dir)
1725                    created = True
1726                filename = os.path.join(images_dir,"%(key)s_%(student_id)s.jpg" % vars())
1727                open(filename,"wb").write(str(image.data))
1728    ###)
1729
1730    security.declareProtected(ModifyPortalContent,"movePassportToFS")###(
1731    def movePassportToFS(self,student_id="O738726"):
1732        "move the passports to the filesystem"
1733        images_dir = os.path.join("%s" % i_home,'passports')
1734        student_folder = getattr(self.portal_url.getPortalObject().campus.students,student_id)
1735        stool = getToolByName(self, 'portal_schemas')
1736        schemas = ['student_application',
1737                   #'student_clearance',
1738                   ]
1739        created = False
1740        for schema_id in schemas:
1741            schema = stool._getOb(schema_id)
1742            object = getattr(student_folder,schema_id[len('student_'):],None)
1743            if object is None:
1744                continue
1745            doc = object.getContent()
1746            for key in schema.keys():
1747                if schema[key].meta_type != "CPS Image Field":
1748                    continue
1749                #import pdb;pdb.set_trace()
1750                image = getattr(doc,key)
1751                if not hasattr(image,"data"):
1752                    continue
1753                if not created:
1754                    if not os.path.exists(images_dir):
1755                        os.mkdir(images_dir)
1756                    created = True
1757                filename = os.path.join(images_dir,"%(student_id)s.jpg" % vars())
1758                open(filename,"wb").write(str(image.data))
1759    ###)
1760
1761InitializeClass(WAeUPTool)
Note: See TracBrowser for help on using the repository browser.