source: WAeUP_SRP/base/WAeUPTool.py @ 2540

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

new function admitStudents please check

  • Property svn:keywords set to Id
File size: 72.6 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 2540 2007-11-05 18:47:31Z joachim $
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            return
644        pin_parts = brain.pin.split('-')
645        if len(pin_parts) != 3:
646            logger.info('invalid pin %s for %s' % (brain.pin,brain.reg_no))
647            return
648        student_id = self.generateStudentId('?')
649        students_folder.invokeFactory('Student', student_id)
650        student_object = getattr(students_folder,student_id)
651        password = pin_parts[2]
652        self.makeStudentMember(student_id,password = password)
653        student_object.manage_setLocalRoles(student_id, ['Owner',])
654        #logger.info("creating %s reg_no %s" % (student_id,brain.reg_no))
655        #
656        # application
657        #
658        student_object.invokeFactory('StudentApplication','application')
659        application = student_object.application
660        self.portal_workflow.doActionFor(application,'open',dest_container=application)
661        da = {'Title': 'Application Data'}
662        da['jamb_reg_no'] = brain.reg_no
663        da['entry_mode'] = brain.entry_mode
664        da['jamb_lastname'] = brain.lastname
665        sex = 'M'
666        if brain.sex:
667            sex = 'F'
668        da['jamb_sex'] = sex
669        da['app_ac_pin'] = brain.pin
670        state_lga = brain.lga.split('_')
671        da['state_lga'] = state_lga[0]
672        da['jamb_lga'] = state_lga[-1]
673        #da['jamb_score'] = brain.????
674        da['app_email'] = brain.email
675        da['app_mobile'] = brain.phone
676        application.getContent().edit(mapping=da)
677        self.portal_workflow.doActionFor(application,'close',dest_container=application)
678        #
679        # personal
680        #
681        student_object.invokeFactory('StudentPersonal','personal')
682        personal = student_object.personal
683        self.portal_workflow.doActionFor(personal,'open',dest_container=personal)
684        dp = {'Title': 'Personal Data'}
685        dp['sex'] = brain.sex
686        dp['email'] = brain.email
687        dp['phone'] = brain.phone
688        dp['lastname'] = brain.lastname
689        personal.getContent().edit(mapping=dp)
690        #
691        # clearance
692        #
693        student_object.invokeFactory('StudentClearance','clearance')
694        clearance = student_object.clearance
695        self.portal_workflow.doActionFor(clearance,'open',dest_container=clearance)
696        dc = {'Title': 'Clearance/Eligibility Record'}
697        dc['lga'] = brain.lga
698        clearance.getContent().edit(mapping=dc)
699        self.portal_workflow.doActionFor(clearance,'close',dest_container=clearance)
700        #
701        # study Course
702        #
703        student_object.invokeFactory('StudentStudyCourse','study_course')
704        studycourse = student_object.study_course
705        self.portal_workflow.doActionFor(studycourse,'open',dest_container=studycourse)
706        dsc = {}
707        dsc['study_course'] = brain.course_admitted
708        dsc['current_verdict'] = ''
709        dsc['current_mode'] = brain.entry_mode
710        studycourse.getContent().edit(mapping=dsc)
711        #
712        # payments folder
713        student_object.invokeFactory('PaymentsFolder','payments')
714        payments = getattr(student_object,'payments')
715        dpay = {}
716        dpay['Title'] = 'Payments'
717        payments.getContent().edit(mapping=dpay)
718        self.portal_workflow.doActionFor(payments,'open')
719        return student_id
720    ###)
721
722    security.declarePublic('makeStudentLevel') ###(
723    def makeStudentLevel(self,student_id):
724        "create the StudyLevel for a returning Student"
725        #import pdb;pdb.set_trace()
726        logger = logging.getLogger('WAeUPTool.makeStudentLevel')
727        students_folder = self.portal_url.getPortalObject().campus.students
728        res = self.students_catalog(id=student_id)
729        if res:
730            st = res[0]
731        course = st.course
732        matric_no = st.matric_no
733        level = st.level
734        res = self.results_import(matric_no = matric_no)
735        if res:
736            results = res
737        logger.info('%s creating Level %s' % (student_id,level))
738        #
739        # Level
740        #
741        student_obj = getattr(self.portal_url.getPortalObject().campus.students,student_id)
742        studycourse = getattr(student_obj,"study_course",None)
743        self.portal_workflow.doActionFor(studycourse,'close_for_edit',dest_container=studycourse)
744        l = getattr(studycourse,level,None)
745        if l is None:
746            studycourse.invokeFactory('StudentStudyLevel', level)
747            l = getattr(studycourse, level)
748            self.portal_workflow.doActionFor(l,'open',dest_container=l)
749            l.getContent().edit(mapping={'Title': "Level %s" % level})
750        ###)
751
752    security.declarePublic('getHallInfo') ###(
753    def getHallInfo(self,bed):
754        """return Hall Info"""
755        info = {}
756        hall,block,room,letter = bed.split('_')
757        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="AccoHall",id=hall)
758        if res and len(res) == 1:
759            hall_brain = res[0]
760            hall_doc = hall_brain.getObject().getContent()
761        else:
762            return info
763        info['hall_title'] = hall_brain.Title
764        info['maintenance_code'] = hall_doc.maintenance_code
765        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="ScratchCardBatch")
766        batch_doc = None
767        for brain in res:
768            if brain.id.startswith(info['maintenance_code']):
769                batch_doc = brain.getObject().getContent()
770                break
771        if batch_doc is None:
772            info['maintenance_fee'] = None
773        else:
774            info['maintenance_fee'] = batch_doc.cost
775        return info
776    ###)
777
778    security.declareProtected(ModifyPortalContent,'removePictureFolder') ###(
779    def removePictureFolder(self,student_id):
780        """remove picture_folder by renaming it"""
781        path = 'images'
782        picture_path = os.path.join(i_home,path,student_id)
783        if not os.path.exists(picture_path):
784            return False
785        os.rename(picture_path,picture_path + "_removed")
786        return True
787    ###)
788
789    security.declareProtected(ModifyPortalContent,'restorePictureFolder') ###(
790    def restorePictureFolder(self,student_id):
791        """restore picture_folder by renaming it"""
792        path = 'images'
793        picture_path = os.path.join(i_home,path,student_id)
794        if not os.path.exists(picture_path + "_removed"):
795            return False
796        os.rename(picture_path + "_removed",picture_path)
797        return True
798    ###)
799
800    security.declarePublic('picturesExist') ###(
801    def picturesExist(self, ids,student_id=None):
802        """check if pictures exist in the filesystem"""
803        if student_id is None:
804            student_id = self.getStudentId()
805        if student_id is None:
806            return False
807        picture_path = os.path.join(images_base,student_id)
808        if not os.path.exists(picture_path):
809            return False
810        pictures  = [picture[:picture.rfind('_')] for picture in os.listdir(picture_path)]
811        return set(ids).issubset(set(pictures))
812    ###)
813
814    security.declarePublic('picturesList') ###(
815    def picturesList(self):
816        """check if pictures exist in the filesystem"""
817        path = 'images'
818        student_id = self.getStudentId()
819        picture_path = os.path.join(i_home,path,student_id)
820        if not os.path.exists(picture_path):
821            return []
822        return [picture[:picture.rfind('_')] for picture in os.listdir(picture_path)]
823    ###)
824
825    security.declarePublic('showFsPicture') ###(
826    def showFsPicture(self,path):
827        """return a picture from the filesystem"""
828        picture_path = os.path.join(i_home,path)
829        response = self.REQUEST.RESPONSE
830        #import pdb;pdb.set_trace()
831        registry = getToolByName(self, 'mimetypes_registry')
832        mimetype = str(registry.lookupExtension(path.lower()) or
833                    registry.lookupExtension('file.bin'))
834        if os.path.exists(picture_path):
835            response.setHeader('Content-type',mimetype)
836            return open(picture_path).read()
837        picture_path = os.path.join(i_home,'import',path)
838        if os.path.exists(picture_path):
839            return open(picture_path).read()
840    ###)
841
842    security.declareProtected(ModifyPortalContent,'deleteAllCourses') ###(
843    def deleteAllCourses(self,department="All"):
844        ''' delete the courses'''
845        pm = self.portal_membership
846        member = pm.getAuthenticatedMember()
847
848        if str(member) not in ("henrik","joachim"):
849            return "not possible"
850        if department == "All":
851            res = self.portal_catalog({'meta_type': 'Department'})
852        if len(res) < 1:
853            return "No Departments found"
854
855        deleted = []
856        for dep in res:
857            cf = dep.getObject().courses
858            if cf:
859                cf.manage_delObjects(ids=cf.objectIds())
860                deleted.append("deleted Courses in %s" % dep.getId)
861        return "\r".join(deleted)
862    ###)
863
864    security.declareProtected(ModifyPortalContent,'getLogfileLines') ###(
865    def getLogfileLines(self,filename="event.log",numlines=20):
866        """Get last NUMLINES lines of logfile FILENAME.
867
868        Return last lines' of a file in the instances logfile directory as
869        a list. The number of returned lines equals `numlines' or less. If
870        less than `numlines' lines are available, the whole file ist
871        returned. If the file can not be opened or some other error
872        occurs, empty list is returend.
873        """
874        result = []
875        lines_hit = 0
876
877        # We only handle files in instances' log directory...
878        logpath = os.path.join(i_home, "log")
879        filename = str(os.path.abspath( os.path.join( logpath, filename )))
880        if not filename.startswith( logpath ):
881            # Attempt to access file outside log-dir...
882            return []
883
884        try:
885            fd = file( filename, "rb" )
886        except IOError:
887            return []
888        if not fd:
889            return []
890
891        if os.linesep == None:
892            linesep = '\n'
893        else:
894            linesep = os.linesep
895
896        # Try to find 'numlines' times a lineseparator, searching from end
897        # and moving to the beginning of file...
898        fd.seek( 0, 2) # Move to end of file...
899        while lines_hit < numlines:
900            if fd.read(1) == linesep[-1]: # This moves filedescriptor
901                                          # one step forward...
902                lines_hit += 1
903            try:
904                fd.seek( -2, 1) # Go two bytes back from current pos...
905            except IOError:
906                # We cannot go back two bytes. Maybe the file is too small...
907                break
908        fd.seek(2,1)
909
910        # Read all lines from current position...
911        result = fd.readlines()
912        # Remove line endings...
913        result = [x.strip() for x in result]
914        fd.close()
915        return result
916    ###)
917
918    security.declareProtected(ModifyPortalContent,"getCallbacksFromLog")###(
919    def getCallbacksFromLog(self,filename):
920        """fix Online Payment Transactions from Z2.log entries"""
921        import transaction
922        import random
923        from cgi import parse_qs
924        from urlparse import urlparse
925        #from pdb import set_trace
926        wftool = self.portal_workflow
927        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
928        students_folder = self.portal_url.getPortalObject().campus.students
929        s = r'(?P<client_ip>\S+) - (?P<member_id>\S+) \['
930        s += r'(?P<date>.*)\] "(?P<get>.*)" (?P<codes>\d+ \d+) "'
931        s += r'(?P<intersw>.*)" "(?P<agent>.*)"'
932        data = re.compile(s)
933        start = True
934        tr_count = 1
935        total = 0
936        #name = 'pume_results'
937        #name = 'epaymentsuccessful_z2log2'
938        name = filename
939        no_import = []
940        imported = []
941        logger = logging.getLogger('WAeUPTool.getFailedTransactions')
942        try:
943            transactions = open("%s/import/%s" % (i_home,name),"rb").readlines()
944        except:
945            logger.error('Error reading %s' % name)
946            return
947        tas = []
948        for line in transactions:
949            dict = {}
950            items = data.search(line)
951            dict['idict'] = idict = items.groupdict()
952            #print idict
953            #from pdb import set_trace;set_trace()
954            urlparsed = urlparse(idict['get'][4:])
955            #print urlparsed
956            path = urlparsed[2].split('/')
957            dict['student_id'] = student_id = path[8]
958            dict['payment_id'] = payment_id = path[10]
959            dict['qs_dict'] = qs_dict = parse_qs(urlparsed[4])
960            tas.append(dict)
961            tr_count += 1
962        return tas
963    ###)
964
965    security.declareProtected(ModifyPortalContent,"importOnlinePaymentTransactions")###(
966    def importOnlinePaymentTransactions(self):
967        """load Online Payment Transactions from CSV values"""
968        import transaction
969        import random
970        #from pdb import set_trace
971        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
972        opt = self.online_payments_import
973        students_folder = self.portal_url.getPortalObject().campus.students
974        start = True
975        tr_count = 1
976        total = 0
977        #name = 'pume_results'
978        name = 'OnlineTransactions'
979        no_import = []
980        imported = []
981        logger = logging.getLogger('WAeUPTool.importOnlinePaymentTransactions')
982        try:
983            transactions = csv.DictReader(open("%s/import/%s.csv" % (i_home,name),"rb"))
984        except:
985            logger.error('Error reading %s.csv' % name)
986            return
987        for pay_transaction in transactions:
988            if start:
989                start = False
990                logger.info('Start loading from %s.csv' % name)
991                s = ','.join(['"%s"' % fn for fn in pay_transaction.keys()])
992                no_import.append('%s,"Error"' % s)
993                format = ','.join(['"%%(%s)s"' % fn for fn in pay_transaction.keys()])
994                format_error = format + ',"%(Error)s"'
995            data = {}
996
997            # format of the first file sent by Tayo
998            #data['datetime'] = date = DateTime.DateTime(pay_transaction['Date'])
999            #data['student_id'] = student_id = pay_transaction['Payer ID']
1000            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
1001            #data['response_code'] = response_code = pay_transaction['Resp Code']
1002            #data['amount'] = amount = pay_transaction['Amount']
1003
1004            # format of the second file sent by Tayo
1005            #data['datetime'] = date = 0
1006            #data['student_id'] = student_id = pay_transaction['Payer ID']
1007            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
1008            #data['response_code'] = response_code = '00'
1009            #data['amount'] = amount = pay_transaction['Amount']
1010
1011            # format of the third file sent by Kehinde
1012            data['datetime'] = date = 0
1013            data['student_id'] = student_id = pay_transaction['customer_id']
1014            data['order_id'] = order_id = pay_transaction['merchant_reference']
1015            data['response_code'] = response_code = '00'
1016            data['amount'] = amount = pay_transaction['Amount']
1017
1018            dup = False
1019            if response_code == "12":
1020                continue
1021            try:
1022                opt.addRecord(**data)
1023            except ValueError:
1024                dup = True
1025            #from pdb import set_trace;set_trace()
1026            if dup:
1027                if response_code == "00":
1028                    try:
1029                        opt.modifyRecord(**data)
1030                    except:
1031                        logger.info("duplicate uid, order_id %(order_id)s, student_id %(student_id)s, response_code %(response_code)s" % data)
1032                        continue
1033                else:
1034                    pay_transaction['Error'] = "Duplicate order_id"
1035                    no_import.append( format_error % pay_transaction)
1036                    logger.info("duplicate order_id %(order_id)s for %(student_id)s %(response_code)s" % data)
1037                    continue
1038            tr_count += 1
1039            if tr_count > 1000:
1040                if len(no_import) > 0:
1041                    open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
1042                             '\n'.join(no_import) + '\n')
1043                    no_import = []
1044                em = '%d transactions committed\n' % (tr_count)
1045                transaction.commit()
1046                regs = []
1047                logger.info(em)
1048                total += tr_count
1049                tr_count = 0
1050        open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
1051                                                '\n'.join(no_import))
1052        return self.REQUEST.RESPONSE.redirect("%s" % self.REQUEST.get('URL1'))
1053    ###)
1054
1055    security.declareProtected(ModifyPortalContent,'mass_create_faculty') ###(
1056    def mass_create_faculty(self,mapping):
1057        "create a faculty"
1058        logger = logging.getLogger('WAeUPTool.mass_create_faculty')
1059        academics_folder = self.portal_url.getPortalObject().campus.academics
1060        fid = mapping['code']
1061        if getattr(academics_folder,fid,None) is not None:
1062            return '', "Faculty with ID: %s exists" % fid
1063        logger.info('Creating Faculty %(code)s, %(title)s' % mapping)
1064        try:
1065            academics_folder.invokeFactory('Faculty', fid)
1066        except BadRequest,E:
1067            return '', "%s" % E
1068        f = getattr(academics_folder,fid,None)
1069        f.getContent().edit(mapping=mapping)
1070        return fid,''
1071    ###)
1072
1073    security.declareProtected(ModifyPortalContent,'mass_edit_faculty') ###(
1074    def mass_edit_faculty(self,mapping):
1075        "edit a faculty"
1076        logger = logging.getLogger('WAeUPTool.mass_edit_faculty')
1077        academics_folder = self.portal_url.getPortalObject().campus.academics
1078        fid = mapping['code']
1079        f = getattr(academics_folder,fid,None)
1080        if f is None:
1081            return '', "Faculty with ID: %s does not exist" % fid
1082        logger.info('Editing Faculty %(code)s, %(title)s' % mapping)
1083        f.getContent().edit(mapping=mapping)
1084        return fid,''
1085    ###)
1086
1087    security.declareProtected(ModifyPortalContent,'mass_create_department') ###(
1088    def mass_create_department(self,mapping):
1089        "create a department in the correct faculty"
1090        logger = logging.getLogger('WAeUPTool.mass_create_department')
1091        fid = mapping['faculty_code']
1092        if getattr(self,'_v_faculties',None) is None:
1093            res = self.portal_catalog(portal_type = "Faculty")
1094            self._v_faculties = {}
1095            for f in res:
1096                self._v_faculties[f.getId] = f.getObject()
1097        f = self._v_faculties.get(fid,None)
1098        if f is None:
1099            return '', "No Faculty with ID: %s" % fid
1100        else:
1101            did = mapping.get('code')
1102            d = getattr(f,did,None)
1103            if d is None or d.portal_type == "Faculty":
1104                logger.info('Creating Department %(code)s, %(title)s' % mapping)
1105                try:
1106                    f.invokeFactory('Department', did)
1107                except BadRequest,E:
1108                    return '', "%s" % E
1109                d = getattr(f,did)
1110                d.invokeFactory('CoursesFolder','courses')
1111                courses = getattr(d,'courses')
1112                dict = {'Title': 'Courses'}
1113                courses.getContent().edit(mapping=dict)
1114                d.invokeFactory('CertificatesFolder','certificates')
1115                certificates = getattr(d,'certificates')
1116                dict = {'Title': 'Certificates'}
1117                certificates.getContent().edit(mapping=dict)
1118            d.getContent().edit(mapping=mapping)
1119        return did,''
1120    ###)
1121
1122    security.declareProtected(ModifyPortalContent,'mass_edit_department') ###(
1123    def mass_edit_department(self,mapping):
1124        "create a department in the correct faculty"
1125        logger = logging.getLogger('WAeUPTool.mass_create_department')
1126        academics_folder = self.portal_url.getPortalObject().campus.academics
1127        fid = mapping['faculty_code']
1128        did = mapping.get('code')
1129        try:
1130            d = getattr(getattr(academics_folder,fid),did,None)
1131        except KeyError:
1132            return '', "Department %s or Faculty %s wrong" % (did,fid)
1133        else:
1134            if d is None or d.portal_type == "Faculty":
1135                logger.info('Editing Department %(code)s, %(title)s' % mapping)
1136            d.getContent().edit(mapping=mapping)
1137        return did,''
1138    ###)
1139
1140    security.declareProtected(ModifyPortalContent,'mass_create_course') ###(
1141    def mass_create_course(self,mapping):
1142        #import pdb;pdb.set_trace()
1143        if getattr(self,'_v_course_list',None) is None:
1144            self._v_course_list = []
1145        if getattr(self,'_v_departments',None) is None:
1146            res = self.portal_catalog(portal_type = "Department")
1147            self._v_department_courses = {}
1148            for d in res:
1149                self._v_department_courses[d.getId] = getattr(d.getObject(),"courses",None)
1150        did = mapping['department_code']
1151        d = self._v_department_courses.get(did,None)
1152        if d is None:
1153            return '', "No Department with ID: %s" % did
1154        course_id = mapping.get('code')
1155        if course_id in self._v_course_list:
1156            return '', "Duplicate Course ID: %s" % did
1157        c = getattr(d,course_id,None)
1158        if c is not None:
1159            return '', "Duplicate Course ID: %s" % did
1160        try:
1161            d.invokeFactory('Course', course_id)
1162        except BadRequest,E:
1163            return '', "%s" % E
1164        self._v_course_list.append(course_id)
1165        c = getattr(d,course_id)
1166        c.getContent().edit(mapping=mapping)
1167        return course_id,''
1168    ###)
1169
1170    security.declareProtected(ModifyPortalContent,'mass_edit_course') ###(
1171    def mass_edit_course(self,mapping):
1172        #import pdb;pdb.set_trace()
1173        course_id = mapping.get('code')
1174        res = self.portal_catalog(id=course_id)
1175        if not res:
1176            return '', "No Course with ID: %s" % course_id
1177        c = res[0].getObject()
1178        c.getContent().edit(mapping=mapping)
1179        return course_id,''
1180    ###)
1181
1182    security.declareProtected(ModifyPortalContent,'mass_create_certificate') ###(
1183    def mass_create_certificate(self,mapping):
1184        if getattr(self,'_v_certificate_list',None) is None:
1185            self._v_certificate_list = []
1186        if getattr(self,'_v_department_certificates',None) is None:
1187            res = self.portal_catalog(portal_type = "Department")
1188            self._v_department_certificates = {}
1189            for d in res:
1190                self._v_department_certificates[d.getId] = getattr(d.getObject(),"certificates",None)
1191        did = mapping['department_code']
1192        d = self._v_department_certificates.get(did,None)
1193        if d is None:
1194            return '', "No Department with ID: %s" % did
1195        certificate_id = mapping.get('code')
1196        if certificate_id in self._v_certificate_list:
1197            return '', "Duplicate Certificate ID: %s" % did
1198        c = getattr(d,certificate_id,None)
1199        if c is not None:
1200            return '', "Duplicate Certificate ID: %s" % did
1201        try:
1202            d.invokeFactory('Certificate', certificate_id)
1203        except BadRequest,E:
1204            return '', "%s" % E
1205        self._v_certificate_list.append(certificate_id)
1206        c = getattr(d,certificate_id)
1207        c.getContent().edit(mapping=mapping)
1208        return certificate_id,''
1209    ###)
1210
1211    security.declareProtected(ModifyPortalContent,'mass_edit_certificate') ###(
1212    def mass_edit_certificate(self,mapping):
1213        #import pdb;pdb.set_trace()
1214        certificate_id = mapping.get('code')
1215        res = self.portal_catalog(id=certificate_id)
1216        if not res:
1217            return '', "No Certificate with ID: %s" % did
1218        c = res[0].getObject()
1219        c.getContent().edit(mapping=mapping)
1220        return certificate_id,''
1221    ###)
1222
1223    security.declareProtected(ModifyPortalContent,'mass_create_application') ###(
1224    def mass_create_application(self,mapping):
1225        #import pdb;pdb.set_trace()
1226        try:
1227            self.applicants_catalog.addRecord(**mapping)
1228        except ValueError:
1229            return '', "applicant already exists: %s" % mapping.get('reg_no')
1230        return key,''
1231    ###)
1232
1233    security.declareProtected(ModifyPortalContent,'mass_edit_application') ###(
1234    def mass_edit_application(self,mapping):
1235        #import pdb;pdb.set_trace()
1236        reg_no = mapping.get('reg_no')
1237        try:
1238            self.applicants_catalog.modifyRecord(**mapping)
1239        except KeyError:
1240            return '', "applicant does not exists: %s" % reg_no
1241        return reg_no,''
1242    ###)
1243
1244    security.declareProtected(ModifyPortalContent,'mass_create_course_result') ###(
1245    def mass_create_course_result(self,mapping):
1246        #import pdb;pdb.set_trace()
1247        if getattr(self,'_v_courses',None) is None:
1248            res = self.courses_catalog()
1249            self._v_courses = {}
1250            for brain in res:
1251                self._v_courses[brain.code] = brain
1252        course_id = mapping.get('code')
1253        if course_id not in self._v_courses.keys():
1254            return '', "No course with ID: %s" % did
1255        id_key = ''
1256        for id_key in ('student_id','matric_no'):
1257            id_field = mapping.get(id_key,None)
1258            if id_field is not None:
1259                student_id = id_field
1260                break
1261        query = Eq(id_key,id_field)
1262        res = self.students_catalog.evalAdvancedQuery(query)
1263        if not res:
1264            return '', "No student with %(id_field)s: %(id_key)s" % vars()
1265        if id_field != "student_id":
1266            mapping['student_id'] = res[0].id
1267        mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % mapping
1268        for k in ('semester',):
1269            mapping[k] = getattr(self._v_courses[course_id],k)
1270        try:
1271            self.course_results.addRecord(**mapping)
1272        except ValueError:
1273            return '', "course result already exists: %s" % key
1274        return key,''
1275    ###)
1276
1277    security.declareProtected(ModifyPortalContent,'mass_edit_course_result') ###(
1278    def mass_edit_course_result(self,mapping):
1279        #import pdb;pdb.set_trace()
1280        # if getattr(self,'_v_courses',None) is None:
1281        #     res = self.courses_catalog()
1282        #     self._v_courses = {}
1283        #     for brain in res:
1284        #         self._v_courses[brain.code] = brain
1285        # course_id = mapping.get('code')
1286        # if course_id not in self._v_courses.keys():
1287        #     return '', "No course with ID: %s" % did
1288        id_key = ''
1289        for id_key in ('student_id','matric_no'):
1290            id_field = mapping.get(id_key,None)
1291            if id_field is not None:
1292                student_id = id_field
1293                break
1294        query = Eq(id_key,id_field)
1295        res = self.students_catalog.evalAdvancedQuery(query)
1296        if not res:
1297            return '', "No student with %(id_field)s: %(id_key)s" % vars()
1298        if id_field != "student_id":
1299            mapping['student_id'] = res[0].id
1300        mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % mapping
1301        # for k in ('semester',):
1302        #     mapping[k] = getattr(self._v_courses[course_id],k)
1303        try:
1304            self.course_results.modifyRecord(**mapping)
1305        except KeyError:
1306            return '', "No course result to edit: %s" % key
1307        return key,''
1308    ###)
1309
1310    security.declareProtected(ModifyPortalContent,'mass_create_certificate_course') ###(
1311    def mass_create_certificate_course(self,mapping):
1312        if getattr(self,'_v_courses',None) is None:
1313            res = self.courses_catalog()
1314            self._v_courses= [course.code for course in res]
1315        if getattr(self,'_v_ceritficates',None) is None:
1316            res = self.portal_catalog(portal_type = "Certificate")
1317            self._v_certificates = {}
1318            for cert in res:
1319                self._v_certificates[cert.getId] = cert.getObject()
1320        certificate_course_id = mapping.get('code')
1321        if certificate_course_id not in self._v_courses:
1322            return '', "No Course with ID: %s" % certificate_course_id
1323        cert_id = mapping['certificate_code']
1324        cert = self._v_certificates.get(cert_id,None)
1325        if cert is None:
1326            return '', "No Certificate with ID: %s" % cert_id
1327        level_id = mapping.get('level')
1328        level = getattr(cert,level_id,None)
1329        if level is None:
1330            cert.invokeFactory('StudyLevel', level_id)
1331            level = getattr(cert,level_id,None)
1332        elif hasattr(level,certificate_course_id):
1333            return '', "Duplicate CertificateCourse ID: %s in %s/ %s" %\
1334            (certificate_course_id,cert_id,level_id)
1335        level.invokeFactory('CertificateCourse', certificate_course_id)
1336        c = getattr(level,certificate_course_id)
1337        c.getContent().edit(mapping=mapping)
1338        return certificate_course_id,''
1339    ###)
1340
1341    field2types_student = {   ###(
1342                      'StudentApplication':
1343                          {'id': 'application',
1344                           'title': 'Application Data',
1345                           'wf_transition_return': 'close',
1346                           'wf_transition_admit': 'remain',
1347                           'fields':
1348                             ('jamb_reg_no',
1349                              'entry_mode',
1350                              'entry_session',
1351                              'jamb_score',
1352                              'app_email',
1353                              'jamb_age',
1354                              'jamb_state',
1355                              'jamb_lga',
1356                              )
1357                              },
1358                      #'StudentPume':
1359                      #    {'id': 'pume',
1360                      #     'title': 'Pume Data',
1361                      #     'wf_transition_return': 'close',
1362                      #     'wf_transition_admit': 'close',
1363                      #     'fields':
1364                      #       ('pume_score',
1365                      #        )
1366                      #        },
1367                      'StudentClearance':
1368                          {'id': 'clearance',
1369                           'title': 'Clearance/Eligibility Record',
1370                           'wf_transition_return': 'close',
1371                           'wf_transition_admit': 'remain',
1372                           'fields':
1373                             ('matric_no',
1374                              'nationality',
1375                              'lga',
1376                              'birthday',
1377                              )
1378                              },
1379                         'StudentPersonal':
1380                          {'id': 'personal',
1381                           'title': 'Personal Data',
1382                           'wf_transition_return': 'open',
1383                           'wf_transition_admit': 'remain',
1384                           'fields':
1385                             ('firstname',
1386                              'middlename',
1387                              'lastname',
1388                              'sex',
1389                              'email',
1390                              'phone',
1391                              'perm_address',
1392                              )
1393                              },
1394                         'StudentStudyCourse':
1395                          {'id': 'study_course',
1396                           'title': 'Study Course',
1397                           'wf_transition_return': 'open',
1398                           'wf_transition_admit': 'remain',
1399                           'fields':
1400                             ('study_course',
1401                              'current_level',
1402                              'current_session',
1403                              'current_mode',
1404                              'current_verdict',
1405                              'previous_verdict',
1406                              )
1407                              },
1408
1409                         'PaymentsFolder':
1410                          {'id': 'payments',
1411                           'title': 'Payments',
1412                           'wf_transition_return': 'open',
1413                           'wf_transition_admit': 'open',
1414                           'fields':
1415                             ()
1416                              },
1417                         }
1418    ###)
1419
1420    security.declareProtected(ModifyPortalContent,'mass_create_student') ###(
1421    def mass_create_student(self,mapping):
1422        "create a students record due import"
1423        logger = logging.getLogger('WAeUPTool.mass_create_student')
1424        students_folder = self.portal_url.getPortalObject().campus.students
1425        jamb_reg_no = mapping.get('jamb_reg_no',None)
1426        if jamb_reg_no:
1427            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
1428            if res:
1429                return '',"jamb_reg_no exists"
1430        matric_no = mapping.get('matric_no',None)
1431        if matric_no:
1432            res = self.students_catalog(matric_no = matric_no)
1433            if res:
1434                return '',"matric_no exists"
1435        sid = self.waeup_tool.generateStudentId('?')
1436        students_folder.invokeFactory('Student', sid)
1437        student_obj = getattr(students_folder,sid)
1438        f2t = self.field2types_student
1439        d = {}
1440        transition = mapping.get('reg_transition','admit')
1441        if transition not in ('admit','return'):
1442            return '',"no valid transition provided"
1443        for pt in f2t.keys():
1444            student_obj.invokeFactory(pt,f2t[pt]['id'])
1445            sub_obj = getattr(student_obj,f2t[pt]['id'])
1446            sub_doc = sub_obj.getContent()
1447            d['Title'] = f2t[pt]['title']
1448            for field in f2t[pt]['fields']:
1449                d[field] = mapping.get(field,'')
1450
1451            if pt == "StudentApplication":
1452                d['jamb_sex']  = 'M'
1453                if mapping.get('sex'):
1454                    d['jamb_sex']  = 'F'
1455                d['jamb_firstname'] = mapping.get('firstname',None)
1456                d['jamb_middlename'] = mapping.get('middlename',None)
1457                d['jamb_lastname'] = mapping.get('lastname',None)
1458
1459            # if pt == "StudyCourse":
1460            #     for von,zu in (('entry_mode','current_mode'),
1461            #                    ('entry_session','current_session')):
1462            #         if mapping.get(zu,None) is None and mapping.get(von,None) is not None:
1463            #             d[zu] = mapping[von]
1464            sub_doc.edit(mapping = d)
1465
1466            #import pdb;pdb.set_trace()
1467            new_state = f2t[pt]['wf_transition_%(transition)s' % vars()]
1468            if new_state != "remain":
1469                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
1470        self.portal_workflow.doActionFor(student_obj,transition)
1471        student_obj.manage_setLocalRoles(sid, ['Owner',])
1472        return sid,''
1473    ###)
1474
1475    security.declareProtected(ModifyPortalContent,'mass_edit_student') ###(
1476    def mass_edit_student(self,mapping):
1477        "edit a students record due import"
1478        logger = logging.getLogger('WAeUPTool.mass_edit_student')
1479        students_folder = self.portal_url.getPortalObject().campus.students
1480        sid = mapping.get('id',None)
1481        jamb_reg_no = mapping.get('jamb_reg_no',None)
1482        matric_no = mapping.get('matric_no',None)
1483        editable_keys = mapping.keys()
1484        if sid:
1485            res = self.students_catalog(id = sid)
1486            if not res:
1487                return '',"no student with id %s" % sid
1488            if matric_no and res[0].matric_no and\
1489              matric_no != res[0].matric_no:
1490                logger.info("%s, old matric_no %s overwritten with %s" % (res[0].id,res[0].matric_no,matric_no))
1491            if jamb_reg_no and res[0].jamb_reg_no and\
1492              jamb_reg_no != res[0].jamb_reg_no:
1493                logger.info("%s, old reg_no %s overwritten with %s" % (res[0].id,res[0].jamb_reg_no,jamb_reg_no))
1494        elif jamb_reg_no:
1495            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
1496            if not res:
1497                return '',"no student with jamb_reg_no %s" % jamb_reg_no
1498            editable_keys.remove('jamb_reg_no')
1499        elif matric_no:
1500            res = self.students_catalog(matric_no = matric_no)
1501            if not res:
1502                return '',"no student with matric_no %s" % matric_no
1503            editable_keys.remove('matric_no')
1504
1505        ## included only to change wf state from admitted to returning
1506        #if res[0].review_state not in ('admitted','objection_raised'):
1507        #    return '%s' % res[0].id ,"student is not in state admitted or objection_raised"
1508        ## end inclusion
1509
1510        sid = res[0].id
1511        student_obj = getattr(students_folder,sid)
1512        f2t = self.field2types_student
1513        d = {}
1514        #import pdb;pdb.set_trace()
1515        any_change = False
1516        for pt in f2t.keys():
1517            if pt == "student_application":
1518                d['jamb_sex']  = 'M'
1519                if mapping.get('sex'):
1520                    d['jamb_sex']  = 'F'
1521            intersect = set(f2t[pt]['fields']).intersection(set(editable_keys))
1522            if intersect:
1523                sub_obj = getattr(student_obj,f2t[pt]['id'],None)
1524                if sub_obj is None:
1525                    try:
1526                        student_obj.invokeFactory(pt,f2t[pt]['id'])
1527                    except:
1528                        continue
1529                    sub_obj = getattr(student_obj,f2t[pt]['id'])
1530                    d['Title'] = f2t[pt]['title']
1531                sub_doc = sub_obj.getContent()
1532                for field in intersect:
1533                    changed = False
1534                    if getattr(sub_doc,field,None) != mapping.get(field,''):
1535                        any_change = True
1536                        changed = True
1537                        d[field] = mapping.get(field,'')
1538                    if changed:
1539                        sub_doc.edit(mapping = d)
1540
1541
1542        ## included only to change wf state from admitted to returning
1543        #    if res[0].review_state in ('admitted','objection_raised'):
1544        #        new_state = f2t[pt]['wf_transition_return']
1545        #        sub_obj = getattr(student_obj,f2t[pt]['id'],None)
1546        #        if sub_obj and new_state != "remain":
1547        #            try:
1548        #                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
1549        #            except:
1550        #                #logger.info('%s, wf transition %s of %s failed' % (sid,new_state,sub_obj.id))
1551        #                pass
1552        #if res[0].review_state in ('admitted','objection_raised'):
1553        #    wfaction = 'return'
1554        #    try:
1555        #        self.portal_workflow.doActionFor(student_obj,wfaction)
1556        #        logger.info('%s, wf state changed' % sid)
1557        #        any_change = True
1558        #    except:
1559        #        logger.info('%s, wf transition failed, old state = %s' % (sid,res[0].review_state))
1560        #        pass
1561        ## end inclusion
1562
1563
1564        if any_change:
1565            return sid,''
1566        else:
1567            return sid,'not modified'
1568    ###)
1569
1570    security.declareProtected(ModifyPortalContent,"importData")###(
1571    def importData(self,filename,name,edit=False,bypass_queue_catalog=False):
1572        """load data from CSV values"""
1573        import transaction
1574        import random
1575
1576        pm = self.portal_membership
1577        member = pm.getAuthenticatedMember()
1578
1579        logger = logging.getLogger('WAeUPTool.importData')
1580        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
1581        students_folder = self.portal_url.getPortalObject().campus.students
1582        start = True
1583        tr_count = 0
1584        total_imported = 0
1585        total_not_imported = 0
1586        total = 0
1587        iname = "import_%s" % name
1588        if iname == 'import_application':
1589            commit_after = 2000
1590        else:
1591            commit_after = 100
1592        stool = getToolByName(self, 'portal_schemas')
1593        ltool = getToolByName(self, 'portal_layouts')
1594        schema = stool._getOb(iname)
1595        if schema is None:
1596            em = 'No such schema %s' % iname
1597            logger.error('No such schema %s' % iname)
1598            return em
1599        layout = ltool._getOb(iname)
1600        if layout is None:
1601            em = 'No such layout %s' % iname
1602            logger.error(em)
1603            return em
1604        validators = {}
1605        for widget in layout.keys():
1606            validators[widget] = layout[widget].validate
1607        #import pdb;pdb.set_trace()
1608        mode = "create"
1609        if edit:
1610            mode = "edit"
1611        importer_name = "mass_%(mode)s_%(name)s" % vars()
1612        importer = getattr(self, '%s' % importer_name,None)
1613        if importer is None:
1614            em = 'No importer function %s' % importer_name
1615            logger.error(em)
1616            return em
1617        not_imported = []
1618        imported = []
1619        try:
1620            items = csv.DictReader(open("%s/import/%s.csv" % (i_home,filename),"rb"))
1621        except:
1622            em = 'Error reading %s.csv' % filename
1623            logger.error(em)
1624            return em
1625        for item in items:
1626            if start:
1627                start = False
1628                adapters = [MappingStorageAdapter(schema, item)]
1629                logger.info('%s starts import from %s.csv in %s mode with schema and layout %s' % (member,filename,mode,iname))
1630                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb")).next()
1631                import_keys = [k for k in attrs if not k.startswith('ignore')]
1632                diff2schema = set(import_keys).difference(set(schema.keys()))
1633                diff2layout = set(import_keys).difference(set(layout.keys()))
1634                if diff2schema:
1635                    em = 'not ignorable key(s): "%s" found in heading' % ", ".join(diff2schema)
1636                    return em
1637                if mode == "create":
1638                    required_keys = [layout.getIdUnprefixed(id)
1639                                     for id,widget in layout.objectItems()
1640                                     if widget.is_required]
1641                    if not set(required_keys).issubset(set(import_keys)):
1642                        diff2import = set(required_keys).difference(set(import_keys))
1643                        em = 'required key(s): "%s" not found in heading' % ", ".join(diff2import)
1644                        return em
1645                s = ','.join(['"%s"' % fn for fn in import_keys])
1646                open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(s + ',"Error"'+ '\n')
1647                s = '"id",' + s
1648                open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(s + '\n')
1649                format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
1650                format_error = format + ',"%(Error)s"'
1651                format = '"%(id)s",'+ format
1652
1653            dm = DataModel(item, adapters,context=self)
1654            ds = DataStructure(data=item,datamodel=dm)
1655            error_string = ""
1656            total += 1
1657            for k in import_keys:
1658                if not validators[k](ds,mode=mode):
1659                    error_string += " %s : %s" % (k,
1660                                                  self.translation_service(ds.getError(k),
1661                                                                           ds.getErrorMapping(k)))
1662            if not error_string:
1663                temp_item = item.copy()
1664                temp_item.update(dm)
1665                temp_item['id'],error = importer(temp_item)
1666                if error:
1667                    error_string += error
1668                else:
1669                    item = temp_item
1670            if error_string:
1671                item['Error'] = error_string
1672                not_imported.append(format_error % item)
1673                total_not_imported += 1
1674            else:
1675                em = format % item
1676                imported.append(em)
1677                tr_count += 1
1678                total_imported += 1
1679                logger.info("%(total_imported)d of %(total)d %(em)s" % vars())
1680
1681            if total and not total % commit_after:
1682                transaction.commit()
1683                if len(not_imported) > 0:
1684                    open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
1685                             '\n'.join(not_imported) + '\n')
1686                    not_imported = []
1687                if len(imported) > 0:
1688                    open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
1689                             '\n'.join(imported) + '\n')
1690                    imported = []
1691                em = '%d transactions committed\n' % (tr_count)
1692                regs = []
1693                logger.info(em)
1694                tr_count = 0
1695                #if total > 100:
1696                #    return
1697        if len(imported) > 0:
1698            open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
1699                                                '\n'.join(imported))
1700        if len(not_imported) > 0:
1701            open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
1702                                                '\n'.join(not_imported))
1703        em = "Finished: %d imported, %d not imported (of total %d)" % (total_imported,total_not_imported,total)
1704        logger.info(em)
1705        return em
1706    ###)
1707
1708    security.declareProtected(ModifyPortalContent,"moveImagesToFS")###(
1709    def moveImagesToFS(self,student_id="O738726"):
1710        "move the images to the filesystem"
1711        images_dir = os.path.join("%s" % images_base,student_id)
1712        student_folder = getattr(self.portal_url.getPortalObject().campus.students,student_id)
1713        stool = getToolByName(self, 'portal_schemas')
1714        schemas = ['student_application',
1715                   'student_clearance',
1716                   ]
1717        created = False
1718        for schema_id in schemas:
1719            schema = stool._getOb(schema_id)
1720            object = getattr(student_folder,schema_id[len('student_'):],None)
1721            if object is None:
1722                continue
1723            doc = object.getContent()
1724            for key in schema.keys():
1725                if schema[key].meta_type != "CPS Image Field":
1726                    continue
1727                #import pdb;pdb.set_trace()
1728                image = getattr(doc,key,None)
1729                if not image or not hasattr(image,"data"):
1730                    continue
1731                if not created:
1732                    if not os.path.exists(images_dir):
1733                        os.mkdir(images_dir)
1734                    created = True
1735                filename = os.path.join(images_dir,"%(key)s_%(student_id)s.jpg" % vars())
1736                open(filename,"wb").write(str(image.data))
1737    ###)
1738
1739    security.declareProtected(ModifyPortalContent,"movePassportToFS")###(
1740    def movePassportToFS(self,student_id="O738726"):
1741        "move the passports to the filesystem"
1742        images_dir = os.path.join("%s" % i_home,'passports')
1743        student_folder = getattr(self.portal_url.getPortalObject().campus.students,student_id)
1744        stool = getToolByName(self, 'portal_schemas')
1745        schemas = ['student_application',
1746                   #'student_clearance',
1747                   ]
1748        created = False
1749        for schema_id in schemas:
1750            schema = stool._getOb(schema_id)
1751            object = getattr(student_folder,schema_id[len('student_'):],None)
1752            if object is None:
1753                continue
1754            doc = object.getContent()
1755            for key in schema.keys():
1756                if schema[key].meta_type != "CPS Image Field":
1757                    continue
1758                #import pdb;pdb.set_trace()
1759                image = getattr(doc,key)
1760                if not hasattr(image,"data"):
1761                    continue
1762                if not created:
1763                    if not os.path.exists(images_dir):
1764                        os.mkdir(images_dir)
1765                    created = True
1766                filename = os.path.join(images_dir,"%(student_id)s.jpg" % vars())
1767                open(filename,"wb").write(str(image.data))
1768    ###)
1769
1770InitializeClass(WAeUPTool)
Note: See TracBrowser for help on using the repository browser.