source: WAeUP_SRP/trunk/WAeUPTool.py @ 2098

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

add applicants_catalog

  • Property svn:keywords set to Id
File size: 50.1 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 2094 2007-08-13 12:01:11Z 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
29
30from Products.CMFCore.utils import getToolByName
31from Products.CPSSchemas.DataStructure import DataStructure
32from Products.CPSSchemas.DataModel import DataModel
33from Products.CPSSchemas.StorageAdapter import MappingStorageAdapter
34from Products.CMFCore.ActionProviderBase import ActionProviderBase
35from Products.CMFCore.permissions import View
36from Products.ZCatalog.ZCatalog import ZCatalog
37from Products.CMFCore.permissions import ModifyPortalContent
38from Products.CMFCore.permissions import ManagePortal
39from Products.CMFCore.utils import UniqueObject
40from Products.CMFCore.URLTool import URLTool
41from Products.CMFCore.utils import getToolByName
42from Students import makeCertificateCode
43from Globals import package_home,INSTANCE_HOME
44p_home = package_home(globals())
45i_home = INSTANCE_HOME
46import DateTime,time
47import logging
48import transaction
49import csv,re,os
50from Products.AdvancedQuery import Eq, Between, Le,In
51
52def getObject(object,name):
53    if object.hasObject(name):
54        return getattr(object,name)
55    return None
56
57class WAeUPTool(UniqueObject, SimpleItem, ActionProviderBase):
58    """WAeUP tool"""
59
60    id = 'waeup_tool'
61    meta_type = 'WAeUP Tool'
62    _actions = ()
63
64    security = ClassSecurityInfo()
65    security.declareObjectProtected(View)
66
67    manage_options = ( ActionProviderBase.manage_options
68                     + SimpleItem.manage_options
69                     )
70
71    def rwrite(self,s): ###(
72        response = self.REQUEST.RESPONSE
73        response.setHeader('Content-type','text/html; charset=ISO-8859-15')
74        response.write("%s<br />\r\n" % s)
75    ###)
76
77    def sleep(self,secs): ###(
78        "sleep"
79        import time
80        time.sleep(secs)
81        return
82
83###)
84
85    security.declareProtected(ModifyPortalContent,'openLog') ###(
86    def openLog(self,name):
87        """open a log file"""
88        version = 1
89        path = "%s/log/%s_%d.log" % (i_home,name,version)
90        while os.path.exists(path):
91            version += 1
92            path = "%s/log/%s_%d.log" % (i_home,name,version)
93        log = open(path,"w")
94        return log
95
96###)
97
98    security.declareProtected(ModifyPortalContent,'measureOaT') ###(
99    def measureOaT(self,method="a",probe="1000",nr_pts="1"):
100        """measure Object access Time"""
101        import random
102        if hasattr(self,'portal_catalog_real'):
103            aq_portal = self.portal_catalog_real.evalAdvancedQuery
104        else:
105            aq_portal = self.portal_catalog.evalAdvancedQuery
106        nr_pts = int(nr_pts)
107        probe = int(probe)
108        intervall = probe/10
109        objects = ("application","clearance","personal")
110        portal_types = ("StudentApplication","StudentClearance","StudentPersonal")
111        #i = random.randrange(num_objects)
112        count = 0
113        found = 0
114        not_found = 0
115        t_found = 0
116        t_not_found = 0
117        time_found = time_not_found = 0.0
118        t_time_found = t_time_not_found = 0.0
119        accessed = []
120        t_min = 1000
121        t_max = 0
122        #import pdb;pdb.set_trace()
123        students = self.portal_catalog(portal_type="Student")
124        num_students = len(students)
125        if method == "d":
126            query = Eq('path','/uniben/campus/students') & In('portal_type',portal_types[:nr_pts])
127            res = aq_portal(query)
128            brains = {}
129            for r in res:
130                sid = r.relative_path.split('/')[-2]
131                if brains.has_key(sid):
132                    brains[sid][r.portal_type] = r
133                else:
134                    brains[sid] = {r.portal_type : r}
135            brains_list = brains.keys()
136            num_objects = len(brains_list)
137        else:
138            num_objects = num_students
139        print "="*40
140        print "method: %s probes: %d nr_pts: %d num_objects: %d" % (method,
141                                                                        probe,
142                                                                        nr_pts,
143                                                                        num_objects)
144        print "nr found/not time found/not min/max"
145        elapse = time.time()
146        i_elapse = time.time()
147        c_elapse = time.clock()
148        for c in range(1,probe + 1):
149            i = random.randrange(num_objects)
150            if method in ('a','b','c'):
151                student_brain = students[i]
152            elif method == "d":
153                #import pdb;pdb.set_trace()
154                student_brain = brains[brains_list[i]]
155            if method == "c":
156                query = Eq('path',student_brain.getPath()) & In('portal_type',portal_types[:nr_pts])
157                res = aq_portal(query)
158                this_portal_types = [r.portal_type for r in res]
159            for i in range(nr_pts):
160                oid = objects[i]
161                if method == "a":
162                    try:
163                        student_path = student_brain.getPath()
164                        path = "%s/%s" % (student_path,oid)
165                        doc = self.unrestrictedTraverse(path).getContent()
166                        found += 1
167                        i_time = time.time() - i_elapse
168                        time_found += i_time
169                    except:
170                        not_found += 1
171                        i_time = time.time() - i_elapse
172                        time_not_found += i_time
173                        pass
174                elif method == "b":
175                    try:
176                        student_object = student_brain.getObject()
177                        doc = getattr(student_object,oid).getContent()
178                        found += 1
179                        i_time = time.time() - i_elapse
180                        time_found += i_time
181                    except:
182                        i_time = time.time() - i_elapse
183                        time_not_found += i_time
184                        not_found += 1
185                        pass
186                elif method == "c":
187                    if portal_types[i] in this_portal_types:
188                        found += 1
189                        doc = res[this_portal_types.index(portal_types[i])].getObject().getContent()
190                        i_time = time.time() - i_elapse
191                        time_found += i_time
192                    else:
193                        not_found += 1
194                        i_time = time.time() - i_elapse
195                        time_not_found += i_time
196                elif method == "d":
197                    if student_brain.has_key(portal_types[i]):
198                        found += 1
199                        doc = student_brain[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                i_elapse = time.time()
207            if c and (c % intervall == 0):
208                #i_time = time.time() - i_elapse
209                t_per = 0.0
210                if found:
211                    t_per = time_found/found
212                if t_per > t_max:
213                    t_max = t_per
214                if t_per > 0.0 and t_per < t_min:
215                    t_min = t_per                   
216                itf = 0.0
217                if found:
218                    itf = time_found/found
219                itnf = 0.0
220                if not_found :
221                    itnf = time_not_found / not_found
222                interval_time = time_found + time_not_found
223                s = "%(c)d: %(found)d/%(not_found)d " % vars()
224                s += "%(interval_time)6.2f %(itf)6.4f/%(itnf)6.4f " % vars()
225                s += "%(t_min)6.4f/%(t_max)6.4f" %  vars()
226                print s
227                t_found += found
228                t_not_found += not_found
229                t_time_found += time_found
230                t_time_not_found += time_not_found
231                time_found = time_not_found = 0.0
232                found = not_found = 0
233        # t_found += found
234        # t_not_found += not_found
235        elapse = time.time() - elapse
236        itf = 0.0
237        if t_found:
238            itf = t_time_found/t_found
239        itnf = 0.0
240        if t_not_found:
241            itnf = t_time_not_found / t_not_found
242        #c_elapse = time.clock() - c_elapse
243        s = "%(probe)d: %(t_found)d/%(t_not_found)d " % vars()
244        s += "%(elapse)6.2f %(itf)6.4f/%(itnf)6.4f " % vars()
245        s += "%(t_min)6.4f/%(t_max)6.4f" %  vars()
246        print "-"*40
247        print s
248        rel_found = float(t_found)/probe
249        rel_not_found = float(t_not_found)/probe
250        estimated_total_time = num_objects*(rel_found*itf + rel_not_found*itnf)
251        print estimated_total_time
252    ###)
253
254    security.declareProtected(ModifyPortalContent,'writeLog') ###(
255    def writeLog(self,logfile,s):
256        """write to the log file"""
257        logfile.write(s)
258
259###)
260
261    def generateStudentId(self,letter): ###(
262        import random
263        r = random
264        ##if letter not in ('ABCDEFGIHKLMNOPQRSTUVWXY'):
265        if letter == '?':
266            letter= r.choice('ABCDEFGHKLMNPQRSTUVWXY')
267        sid = "%c%d" % (letter,r.randint(99999,1000000))
268        students = self.portal_url.getPortalObject().campus.students
269##        while hasattr(students, sid):
270##            sid = "%c%d" % (letter,r.randint(99999,1000000))
271        while self.students_catalog(id = sid):
272            sid = "%c%d" % (letter,r.randint(99999,1000000))
273        return sid
274    ###)
275
276    def generatePassword(self,s=None): ###(
277        import random
278        r = random
279        ##if letter not in ('ABCDEFGIHKLMNOPQRSTUVWXY'):
280        if s is None:
281            s = 'abcdefghklmnpqrstuvwxy23456789'
282        pw = ''
283        while len(pw) < 6:
284            pw += r.choice(s)
285        return pw
286    ###)
287
288    security.declareProtected(ManagePortal, 'removeDeletedDocIds') ###(
289    def removeDeletedDocIds(self, max=1000):
290        """
291        remove deleted docids from repository commit after max
292        """
293        logger = logging.getLogger('WAeUPTool.removeDeletedDocIds')
294        repository = getToolByName(self, 'portal_repository')
295        pxtool = getToolByName(self, 'portal_proxies')
296        pxtool_infos = pxtool.getRevisionsUsed()
297
298        nb_revs = 0
299        docids_d = {} # all docids
300        unused_docids_d = {} # all docids that are unused
301        ids_unused_revs_docids = [] # ids for revs of unused docids
302        ids_unused_revs = [] # ids for unused revs
303        total = 0
304        idlist = repository.objectIds()
305        for id in idlist:
306            docid, rev = repository._splitId(id)
307            if docid is None:
308                logger.info("invalid doc_id %s" % docid)
309                continue
310            nb_revs += 1
311            docids_d[docid] = None
312            if not pxtool_infos.has_key(docid):
313                unused_docids_d[docid] = None
314                ids_unused_revs_docids.append(id)
315                ids_unused_revs.append(id)
316            elif not pxtool_infos[docid].has_key(rev):
317                ids_unused_revs.append(id)
318            if len(ids_unused_revs) >= max:
319                repository.manage_delObjects(ids_unused_revs)
320                #import pdb;pdb.set_trace()
321                transaction.commit()
322                total += max
323                logger.info('removed %d total %d unused docids ' % (max,total))
324        anz = len(ids_unused_revs)
325        if anz > 0:
326            repository.manage_delObjects(ids_unused_revs)
327            transaction.commit()
328            total += anz
329            logger.info('finished removing %d unused docids ' % (total))
330
331
332###)
333
334    security.declareProtected(ModifyPortalContent,'getCredential') ###(
335    def getCredential(self,student_id):
336        "return a student password"
337        student_entry = getattr(self.portal_directories.students,student_id,None)
338        if student_entry is None:
339            return None
340        return getattr(student_entry,"password","not set")
341    ###)
342
343    security.declarePublic('checkPassword') ###(
344    def checkPassword(self,student_id,password):
345        "return a student password"
346        student_entry = getattr(self.portal_directories.students,student_id,None)
347        if student_entry is None:
348            return False
349        return getattr(student_entry,"password","not set") == password
350    ###)
351
352    security.declarePublic('editPassword') ###(
353    def editPassword(self,student_id,password):
354        "edit a student password"
355        student_entry = getattr(self.portal_directories.students,student_id,None)
356        if student_entry is None:
357            return
358        setattr(student_entry,'password',password)
359    ###)
360
361    security.declareProtected(View,'doCommit') ###(
362    def doCommit(self,logger=None):
363        "commit some transactions"
364        transaction.commit()
365    ###)
366
367    security.declarePublic('loadStudentFoto') ###(
368    def loadStudentFoto(self,student,filename,folder):
369        "return a student passport picture"
370        app = student.application
371        app_doc = app.getContent()
372        #clear = student.clearance
373        #clear_doc = clear.getContent()
374        #matric_no = clear_doc.matric_no.upper()
375        picture1 ="%s/import/%s/%s.jpg" % (i_home,folder,filename)
376        picture2 ="%s/import/%s/%s.JPG" % (i_home,folder,filename)
377        #import pdb;pdb.set_trace()
378        if os.path.exists(picture1):
379            file = open(picture1)
380        elif os.path.exists(picture2):
381            file = open(picture2)
382        else:
383            return "passport picture not found %s" % picture1
384        reopened = False
385        if self.portal_workflow.getInfoFor(app,'review_state',None) !='opened':
386            self.portal_workflow.doActionFor(app,'open')
387            reopened = True
388        outfile = file.read()
389        app_doc.manage_addFile('passport',
390                               file=outfile,
391                               title="%s.jpg" % filename)
392        if reopened:
393            self.portal_workflow.doActionFor(app,'close')
394        return "successfully loaded passport picture"
395    ###)
396
397    security.declareProtected(ModifyPortalContent,'createOne') ###(
398    def createOne(self,students_folder,student_brain,letter,commit=False):
399        sid = self.waeup_tool.generateStudentId(letter)
400        students_folder.invokeFactory('Student', sid)
401        student = getattr(students_folder,sid)
402        self.portal_workflow.doActionFor(student,'return')
403        student.manage_setLocalRoles(sid, ['Owner',])
404        matric_no = student_brain.matric_no
405        jamb_reg_no = student_brain.Entryregno
406        self.students_catalog.addRecord(id = sid,
407                                           matric_no = matric_no,
408                                           jamb_reg_no = jamb_reg_no,
409                                           sex = student_brain.Sex == "F",
410                                           name = "%s %s %s" % (student_brain.Firstname,
411                                                                student_brain.Middlename,
412                                                                student_brain.Lastname)
413                                        )
414        if commit:
415            transaction.commit()
416        return sid,jamb_reg_no
417    ###)
418
419    security.declareProtected(ModifyPortalContent,'addStudent') ###(
420    def addStudent(self,dict):
421        students_folder = self.portal_url.getPortalObject().campus.students
422        sid = self.waeup_tool.generateStudentId('?')
423        students_folder.invokeFactory('Student', sid)
424        student_obj = getattr(students_folder,sid)
425        f2t = self.student_field2types
426        #from pdb import set_trace; set_trace()
427        d = {}
428        d['jamb_sex']  = 'M'
429        if dict.get('sex'):
430            d['jamb_sex']  = 'F'
431
432        entry_session = dict.get('entry_session')
433        if entry_session == self.getSessionId()[-2:]:
434            wfaction = 'admit'
435            wft = 'wf_transition_admit'
436            password = None
437        else:
438            wfaction = 'return'
439            wft = 'wf_transition_return'
440            password = self.generatePassword()
441            self.makeStudentMember(sid,password)
442
443        for pt in f2t.keys():
444            student_obj.invokeFactory(pt,f2t[pt]['id'])
445            sub_obj = getattr(student_obj,f2t[pt]['id'])
446            sub_doc = sub_obj.getContent()
447            #self.portal_workflow.doActionFor(sub_obj,'open',dest_container=sub_obj)
448            d['Title'] = f2t[pt]['title']
449            for field in f2t[pt]['fields']:
450                d[field] = dict.get(field,'')
451            sub_doc.edit(mapping = d)
452            new_state = f2t[pt][wft]
453            if new_state != "remain":
454                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
455        self.portal_workflow.doActionFor(student_obj,wfaction)
456        student_obj.manage_setLocalRoles(sid, ['Owner',])
457        return sid,password
458    ###)
459
460    security.declarePublic('getCertificateBrain') ###(
461    def getCertificateBrain(self,cert_id):
462        "do it"
463        res = ZCatalog.searchResults(self.portal_catalog_real,
464                                {'portal_type':"Certificate",
465                                      'id': cert_id})
466        if res:
467            return res[0]
468        return None
469    ###)
470
471    security.declareProtected(ModifyPortalContent,'get_csv_filenames') ###(
472    def get_csv_filenames(self):
473        "do it"
474        files = [file for file in os.listdir("%s/import/" % (i_home))
475                 if file.endswith('.csv') and file.find('imported') == -1]
476        return files
477    ###)
478
479    security.declarePublic('findStudentByMatricelNo') ###(
480    def findStudentByMatricelNo(self,matric_no):
481        "do it"
482        res = ZCatalog.searchResults(self.portal_catalog_real,
483                                {'portal_type':"StudentClearance",
484                                 'SearchableText': matric_no})
485        if res:
486            return res[0]
487        return None
488    ###)
489
490    security.declarePublic('makeStudentMember') ###(
491    def makeStudentMember(self,sid,password='uNsEt'):
492        """make the student a member"""
493        membership = self.portal_membership
494        membership.addMember(sid,
495                             password ,
496                             roles=('Member',
497                                     'Student',
498                                     ),
499                             domains='',
500                             properties = {'memberareaCreationFlag': False,
501                                           'homeless': True},)
502        member = membership.getMemberById(sid)
503        self.portal_registration.afterAdd(member, sid, password, None)
504        #self.manage_setLocalRoles(sid, ['Owner',])
505    ###)
506
507    security.declarePublic('makeStudentData') ###(
508    def makeStudentData(self,student_id,email=None,phone_nr=None):
509        "create Datastructure for a returning Student"
510        #import pdb;pdb.set_trace()
511        logger = logging.getLogger('WAeUPTool.makeStudentData')
512        students_folder = self.portal_url.getPortalObject().campus.students
513        #res = self.students_catalog(id=student_id)
514        #if res:
515        #    st = res[0]
516        #res = self.returning_import(matric_no = st.matric_no)
517        res = self.returning_import(id = student_id)
518        if res:
519            student = res[0]
520        else:
521            logger.info('Id %s not found in returning_import' % student_id)
522            return
523        logger.info('%s creates data structure' % student_id)
524        s_results = self.results_import(matric_no = student.matric_no)
525        if s_results:
526            lnr = self.getLevelFromResultsCosCode(s_results)
527            level = "%d00" % lnr
528            verdict,eligible = self.getVerdict(s_results[0].Verdict)
529            if eligible:
530                level = "%d00" % (lnr + 1)
531        else:
532            logger.info('matric_no %s not found in results_import' % student.matric_no)
533            level = 0
534            verdict = 'N/A'
535        #student should not be allowed to perform this transition
536        #wftool = self.portal_workflow
537        #wftool.doActionFor(student,'return')
538        certcode_org = student.Coursemajorcode
539        certcode = makeCertificateCode(certcode_org)
540        certificate_brain = self.getCertificateBrain(certcode)
541        if not certificate_brain:
542            em = 'Certificate %s org-code %s not found\n' % (certcode, certcode_org)
543            logger.info(em)
544        matric_no = student.matric_no
545        sid = student_id
546        student_obj = getattr(students_folder,sid)
547        student_obj.invokeFactory('StudentApplication','application')
548        application = student_obj.application
549        self.portal_workflow.doActionFor(application,'open',dest_container=application)
550        da = {'Title': 'Application Data'}
551        student_obj.invokeFactory('StudentPersonal','personal')
552        da['jamb_reg_no'] = student.Entryregno
553        em = self.getEntryMode(student.Entryregno)
554##        em = student.Mode_of_Entry
555##        if em in ('DIRECT', 'DIRECT ENTRY',):
556##            em = 'DE'
557##        elif em in ('U.M.E', 'UNE',):
558##            em = 'UME'
559##        elif not em:
560##            em = "unknown"
561        da['entry_mode'] = em
562        personal = student_obj.personal
563        self.portal_workflow.doActionFor(personal,'open',dest_container=personal)
564        dp = {'Title': 'Personal Data'}
565        student_obj.invokeFactory('StudentClearance','clearance')
566        clearance = student_obj.clearance
567        self.portal_workflow.doActionFor(clearance,'open',dest_container=clearance)
568        dc = {'Title': 'Clearance/Eligibility Record'}
569        dc['matric_no'] = matric_no
570        state = student.State
571        lga = student.LGA
572        if state and lga:
573            lga =  state + ' / ' + lga
574        else:
575            lga = "None"
576        da['jamb_lga'] = dc['lga'] = lga
577        da['app_email'] = dp['email'] = email
578        da['app_mobile'] = dp['phone'] = phone_nr
579        dp['firstname'] = student.Firstname
580        dp['middlename'] = student.Middlename
581        dp['lastname'] = student.Lastname
582        da['jamb_lastname'] = "%s %s %s" % (student.Firstname,student.Middlename,student.Lastname)
583        da['jamb_sex'] = student.Sex
584        dp['sex'] = student.Sex == 'F'
585        dp['perm_address'] = student.Permanent_Address
586        application.getContent().edit(mapping=da)
587        self.portal_workflow.doActionFor(application,'close',dest_container=application)
588        personal.getContent().edit(mapping=dp)
589        clearance.getContent().edit(mapping=dc)
590        self.portal_workflow.doActionFor(clearance,'close',dest_container=clearance)
591##        catd = {}
592##        catd['id'] = sid
593##        catd['entry_mode']= da['entry_mode']
594##        catd['matric_no'] = matric_no
595##        catd['jamb_reg_no'] = da['jamb_reg_no']
596##        catd['name'] = "%(firstname)s %(middlename)s %(lastname)s" % dp
597##        catd['sex'] = dp['sex']
598##        catd['level'] = level
599##        catd['verdict'] = verdict
600##        if certificate_brain:
601##            cpath = certificate_brain.getPath().split('/')
602##            catd['faculty'] = cpath[-4]
603##            catd['department'] = cpath[-3]
604##            catd['course'] = certcode
605##        self.students_catalog.modifyRecord(**catd)
606        #
607        # Study Course
608        #
609        student_obj.invokeFactory('StudentStudyCourse','study_course')
610        studycourse = student_obj.study_course
611        self.portal_workflow.doActionFor(studycourse,'open',dest_container=studycourse)
612        dsc = {}
613        dsc['study_course'] = certcode
614        dsc['current_level'] = level
615        dsc['current_verdict'] = verdict
616        dsc['current_mode'] = em
617        dsc['current_session'] = '06'
618        studycourse.getContent().edit(mapping=dsc)
619        #
620        # Level
621        #
622##        l = getattr(studycourse,level,None)
623##        if l is None:
624##            studycourse.invokeFactory('StudentStudyLevel', level)
625##            l = getattr(studycourse, level)
626##            self.portal_workflow.doActionFor(l,'open',dest_container=l)
627##            l.getContent().edit(mapping={'Title': "Level %s" % level})
628###)
629
630    security.declarePublic('makeStudentLevel') ###(
631    def makeStudentLevel(self,student_id):
632        "create the StudyLevel for a returning Student"
633        #import pdb;pdb.set_trace()
634        logger = logging.getLogger('WAeUPTool.makeStudentLevel')
635        students_folder = self.portal_url.getPortalObject().campus.students
636        res = self.students_catalog(id=student_id)
637        if res:
638            st = res[0]
639        course = st.course
640        matric_no = st.matric_no
641        level = st.level
642        res = self.results_import(matric_no = matric_no)
643        if res:
644            results = res
645        logger.info('%s creating Level %s' % (student_id,level))
646        #
647        # Level
648        #
649        student_obj = getattr(self.portal_url.getPortalObject().campus.students,student_id)
650        studycourse = getattr(student_obj,"study_course",None)
651        self.portal_workflow.doActionFor(studycourse,'close_for_edit',dest_container=studycourse)
652        l = getattr(studycourse,level,None)
653        if l is None:
654            studycourse.invokeFactory('StudentStudyLevel', level)
655            l = getattr(studycourse, level)
656            self.portal_workflow.doActionFor(l,'open',dest_container=l)
657            l.getContent().edit(mapping={'Title': "Level %s" % level})
658        ###)
659
660    security.declarePublic('getHallInfo') ###(
661    def getHallInfo(self,bed):
662        """return Hall Info"""
663        info = {}
664        hall,block,room,letter = bed.split('_')
665        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="AccoHall",id=hall)
666        if res and len(res) == 1:
667            hall_brain = res[0]
668            hall_doc = hall_brain.getObject().getContent()
669        else:
670            return info
671        info['hall_title'] = hall_brain.Title
672        info['maintenance_code'] = hall_doc.maintenance_code
673        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="ScratchCardBatch")
674        batch_doc = None
675        for brain in res:
676            if brain.id.startswith(info['maintenance_code']):
677                batch_doc = brain.getObject().getContent()
678                break
679        if batch_doc is None:
680            info['maintenance_fee'] = None
681        else:
682            info['maintenance_fee'] = batch_doc.cost
683        return info
684    ###)
685
686    security.declareProtected(ModifyPortalContent,'deleteAllCourses') ###(
687    def deleteAllCourses(self,department="All"):
688        ''' delete the courses'''
689        pm = self.portal_membership
690        member = pm.getAuthenticatedMember()
691
692        if str(member) not in ("henrik","joachim"):
693            return "not possible"
694        if department == "All":
695            res = self.portal_catalog({'meta_type': 'Department'})
696        if len(res) < 1:
697            return "No Departments found"
698
699        deleted = []
700        for dep in res:
701            cf = dep.getObject().courses
702            if cf:
703                cf.manage_delObjects(ids=cf.objectIds())
704                deleted.append("deleted Courses in %s" % dep.getId)
705        return "\r".join(deleted)
706    ###)
707
708    security.declareProtected(ModifyPortalContent,'getLogfileLines') ###(
709    def getLogfileLines(self,filename="event.log",numlines=20):
710        """Get last NUMLINES lines of logfile FILENAME.
711
712        Return last lines' of a file in the instances logfile directory as
713        a list. The number of returned lines equals `numlines' or less. If
714        less than `numlines' lines are available, the whole file ist
715        returned. If the file can not be opened or some other error
716        occurs, empty list is returend.
717        """
718        result = []
719        lines_hit = 0
720
721        # We only handle files in instances' log directory...
722        logpath = os.path.join(i_home, "log")
723        filename = str(os.path.abspath( os.path.join( logpath, filename )))
724        if not filename.startswith( logpath ):
725            # Attempt to access file outside log-dir...
726            return []
727
728        try:
729            fd = file( filename, "rb" )
730        except IOError:
731            return []
732        if not fd:
733            return []
734
735        if os.linesep == None:
736            linesep = '\n'
737        else:
738            linesep = os.linesep
739
740        # Try to find 'numlines' times a lineseparator, searching from end
741        # and moving to the beginning of file...
742        fd.seek( 0, 2) # Move to end of file...
743        while lines_hit < numlines:
744            if fd.read(1) == linesep[-1]: # This moves filedescriptor
745                                          # one step forward...
746                lines_hit += 1
747            try:
748                fd.seek( -2, 1) # Go two bytes back from current pos...
749            except IOError:
750                # We cannot go back two bytes. Maybe the file is too small...
751                break
752        fd.seek(2,1)
753
754        # Read all lines from current position...
755        result = fd.readlines()
756        # Remove line endings...
757        result = [x.strip() for x in result]
758        fd.close()
759        return result
760    ###)
761
762    security.declareProtected(ModifyPortalContent,"getCallbacksFromLog")###(
763    def getCallbacksFromLog(self,filename):
764        """fix Online Payment Transactions from Z2.log entries"""
765        import transaction
766        import random
767        from cgi import parse_qs
768        from urlparse import urlparse
769        #from pdb import set_trace
770        wftool = self.portal_workflow
771        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
772        students_folder = self.portal_url.getPortalObject().campus.students
773        s = r'(?P<client_ip>\S+) - (?P<member_id>\S+) \['
774        s += r'(?P<date>.*)\] "(?P<get>.*)" (?P<codes>\d+ \d+) "'
775        s += r'(?P<intersw>.*)" "(?P<agent>.*)"'
776        data = re.compile(s)
777        start = True
778        tr_count = 1
779        total = 0
780        #name = 'pume_results'
781        #name = 'epaymentsuccessful_z2log2'
782        name = filename
783        no_import = []
784        imported = []
785        logger = logging.getLogger('WAeUPTool.getFailedTransactions')
786        try:
787            transactions = open("%s/import/%s" % (i_home,name),"rb").readlines()
788        except:
789            logger.error('Error reading %s' % name)
790            return
791        tas = []
792        for line in transactions:
793            dict = {}
794            items = data.search(line)
795            dict['idict'] = idict = items.groupdict()
796            #print idict
797            #from pdb import set_trace;set_trace()
798            urlparsed = urlparse(idict['get'][4:])
799            #print urlparsed
800            path = urlparsed[2].split('/')
801            dict['student_id'] = student_id = path[8]
802            dict['payment_id'] = payment_id = path[10]
803            dict['qs_dict'] = qs_dict = parse_qs(urlparsed[4])
804            tas.append(dict)
805            tr_count += 1
806        return tas
807    ###)
808
809    security.declareProtected(ModifyPortalContent,"importOnlinePaymentTransactions")###(
810    def importOnlinePaymentTransactions(self):
811        """load Online Payment Transactions from CSV values"""
812        import transaction
813        import random
814        #from pdb import set_trace
815        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
816        opt = self.online_payments_import
817        students_folder = self.portal_url.getPortalObject().campus.students
818        start = True
819        tr_count = 1
820        total = 0
821        #name = 'pume_results'
822        name = 'OnlineTransactions'
823        no_import = []
824        imported = []
825        logger = logging.getLogger('WAeUPTool.importOnlinePaymentTransactions')
826        try:
827            transactions = csv.DictReader(open("%s/import/%s.csv" % (i_home,name),"rb"))
828        except:
829            logger.error('Error reading %s.csv' % name)
830            return
831        for pay_transaction in transactions:
832            if start:
833                start = False
834                logger.info('Start loading from %s.csv' % name)
835                s = ','.join(['"%s"' % fn for fn in pay_transaction.keys()])
836                no_import.append('%s,"Error"' % s)
837                format = ','.join(['"%%(%s)s"' % fn for fn in pay_transaction.keys()])
838                format_error = format + ',"%(Error)s"'
839            data = {}
840
841            # format of the first file sent by Tayo
842            #data['datetime'] = date = DateTime.DateTime(pay_transaction['Date'])
843            #data['student_id'] = student_id = pay_transaction['Payer ID']
844            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
845            #data['response_code'] = response_code = pay_transaction['Resp Code']
846            #data['amount'] = amount = pay_transaction['Amount']
847
848            # format of the second file sent by Tayo
849            #data['datetime'] = date = 0
850            #data['student_id'] = student_id = pay_transaction['Payer ID']
851            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
852            #data['response_code'] = response_code = '00'
853            #data['amount'] = amount = pay_transaction['Amount']
854
855            # format of the third file sent by Kehinde
856            data['datetime'] = date = 0
857            data['student_id'] = student_id = pay_transaction['customer_id']
858            data['order_id'] = order_id = pay_transaction['merchant_reference']
859            data['response_code'] = response_code = '00'
860            data['amount'] = amount = pay_transaction['Amount']
861
862            dup = False
863            if response_code == "12":
864                continue
865            try:
866                opt.addRecord(**data)
867            except ValueError:
868                dup = True
869            #from pdb import set_trace;set_trace()
870            if dup:
871                if response_code == "00":
872                    try:
873                        opt.modifyRecord(**data)
874                    except:
875                        logger.info("duplicate uid, order_id %(order_id)s, student_id %(student_id)s, response_code %(response_code)s" % data)
876                        continue
877                else:
878                    pay_transaction['Error'] = "Duplicate order_id"
879                    no_import.append( format_error % pay_transaction)
880                    logger.info("duplicate order_id %(order_id)s for %(student_id)s %(response_code)s" % data)
881                    continue
882            tr_count += 1
883            if tr_count > 1000:
884                if len(no_import) > 0:
885                    open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
886                             '\n'.join(no_import) + '\n')
887                    no_import = []
888                em = '%d transactions committed\n' % (tr_count)
889                transaction.commit()
890                regs = []
891                logger.info(em)
892                total += tr_count
893                tr_count = 0
894        open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
895                                                '\n'.join(no_import))
896        return self.REQUEST.RESPONSE.redirect("%s" % self.REQUEST.get('URL1'))
897    ###)
898
899
900    student_field2types = {   ###(
901                      'StudentApplication':
902                          {'id': 'application',
903                           'title': 'Application Data',
904                           'wf_transition_return': 'close',
905                           'wf_transition_admit': 'remain',
906                           'fields':
907                             ('jamb_reg_no',
908                              'entry_mode',
909                              'entry_session',
910                              'jamb_score',
911                              'app_email',
912                              'jamb_age',
913                              'jamb_state',
914                              'jamb_lga',
915                              )
916                              },
917                      #'StudentPume':
918                      #    {'id': 'pume',
919                      #     'title': 'Pume Data',
920                      #     'wf_transition_return': 'close',
921                      #     'wf_transition_admit': 'close',
922                      #     'fields':
923                      #       ('pume_score',
924                      #        )
925                      #        },
926                      'StudentClearance':
927                          {'id': 'clearance',
928                           'title': 'Clearance/Eligibility Record',
929                           'wf_transition_return': 'close',
930                           'wf_transition_admit': 'remain',
931                           'fields':
932                             ('matric_no',
933                              'nationality',
934                              'lga',
935                              'birthday',
936                              )
937                              },
938                         'StudentPersonal':
939                          {'id': 'personal',
940                           'title': 'Personal Data',
941                           'wf_transition_return': 'open',
942                           'wf_transition_admit': 'remain',
943                           'fields':
944                             ('firstname',
945                              'middlename',
946                              'lastname',
947                              'sex',
948                              'email',
949                              'phone',
950                              'perm_address',
951                              )
952                              },
953                         'StudentStudyCourse':
954                          {'id': 'study_course',
955                           'title': 'Study Course',
956                           'wf_transition_return': 'open',
957                           'wf_transition_admit': 'remain',
958                           'fields':
959                             ('study_course',
960                              'current_level',
961                              'current_session',
962                              'current_mode',
963                              'current_verdict',
964                              )
965                              },
966
967                         'PaymentsFolder':
968                          {'id': 'payments',
969                           'title': 'Payments',
970                           'wf_transition_return': 'open',
971                           'wf_transition_admit': 'open',
972                           'fields':
973                             ()
974                              },
975                         }
976    ###)
977
978
979    security.declareProtected(ModifyPortalContent,'importStudent') ###(
980    def importStudent(self,mapping):
981        "create a students record due import"
982        logger = logging.getLogger('WAeUPTool.importStudent')
983        students_folder = self.portal_url.getPortalObject().campus.students
984        jamb_reg_no = mapping.get('jamb_reg_no',None)
985        if jamb_reg_no:
986            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
987            if res:
988                return '',"jamb_reg_no exists"
989        matric_no = mapping.get('matric_no',None)
990        if matric_no:
991            res = self.students_catalog(matric_no = matric_no)
992            if res:
993                return '',"matric_no exists"
994        sid = self.waeup_tool.generateStudentId('?')
995        students_folder.invokeFactory('Student', sid)
996        student_obj = getattr(students_folder,sid)
997        f2t = self.student_field2types
998        d = {}
999        d['jamb_sex']  = 'M'
1000        if mapping.get('sex'):
1001            d['jamb_sex']  = 'F'
1002        transition = mapping.get('reg_transition','admit')
1003        if transition not in ('admit','return'):
1004            return '',"no valid transition provided"
1005        for pt in f2t.keys():
1006            student_obj.invokeFactory(pt,f2t[pt]['id'])
1007            sub_obj = getattr(student_obj,f2t[pt]['id'])
1008            sub_doc = sub_obj.getContent()
1009            d['Title'] = f2t[pt]['title']
1010            for field in f2t[pt]['fields']:
1011                d[field] = mapping.get(field,'')
1012            if pt == "StudyCourse":
1013                for von,zu in (('entry_mode','current_mode'),
1014                               ('entry_session','current_session')):
1015                    if mapping.get(zu,None) is None and mapping.get(von,None) is not None:
1016                        d[zu] = mapping[von]
1017            sub_doc.edit(mapping = d)
1018
1019            #import pdb;pdb.set_trace()
1020            new_state = f2t[pt]['wf_transition_%(transition)s' % vars()]
1021            if new_state != "remain":
1022                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
1023        self.portal_workflow.doActionFor(student_obj,transition)
1024        student_obj.manage_setLocalRoles(sid, ['Owner',])
1025        return sid,''
1026    ###)
1027
1028    security.declareProtected(ModifyPortalContent,'importEditStudent') ###(
1029    def importEditStudent(self,mapping):
1030        "edit a students record due import"
1031        logger = logging.getLogger('WAeUPTool.importEditStudent')
1032        students_folder = self.portal_url.getPortalObject().campus.students
1033        sid = mapping.get('id',None)
1034        jamb_reg_no = mapping.get('jamb_reg_no',None)
1035        matric_no = mapping.get('matric_no',None)
1036        editable_keys = mapping.keys()
1037        if sid:
1038            res = self.students_catalog(id = sid)
1039            if not res:
1040                return '',"no student with id %s" % sid
1041            elif matric_no and res[0].matric_no and\
1042              matric_no != res[0].matric_no:
1043                return '%s' % res[0].id ,"student has no matric_no %s" % matric_no
1044            elif jamb_reg_no and res[0].jamb_reg_no and\
1045              jamb_reg_no != res[0].jamb_reg_no:
1046                return '%s' % res[0].id ,"student has no jamb_reg_no %s" % jamb_reg_no
1047        elif jamb_reg_no:
1048            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
1049            if not res:
1050                return '',"no student with jamb_reg_no %s" % jamb_reg_no
1051            editable_keys.remove('jamb_reg_no')
1052        elif matric_no:
1053            res = self.students_catalog(matric_no = matric_no)
1054            if not res:
1055                return '',"no student with matric_no %s" % matric_no
1056            editable_keys.remove('matric_no')
1057
1058        # included only to change wf state from admitted to returning
1059        if res[0].review_state not in ('admitted','objection_raised'):
1060            return '%s' % res[0].id ,"student is not in state admitted or objection_raised"
1061        # end inclusion
1062
1063        sid = res[0].id
1064        student_obj = getattr(students_folder,sid)
1065        f2t = self.student_field2types
1066        d = {}
1067        #import pdb;pdb.set_trace()
1068        any_change = False
1069        for pt in f2t.keys():
1070            if pt == "student_application":
1071                d['jamb_sex']  = 'M'
1072                if mapping.get('sex'):
1073                    d['jamb_sex']  = 'F'
1074            intersect = set(f2t[pt]['fields']).intersection(set(editable_keys))
1075            if intersect:
1076                sub_obj = getattr(student_obj,f2t[pt]['id'],None)
1077                if sub_obj is None:
1078                    try:
1079                        student_obj.invokeFactory(pt,f2t[pt]['id'])
1080                    except:
1081                        continue
1082                    sub_obj = getattr(student_obj,f2t[pt]['id'])
1083                    d['Title'] = f2t[pt]['title']
1084                sub_doc = sub_obj.getContent()
1085                for field in intersect:
1086                    changed = False
1087                    if getattr(sub_doc,field) != mapping.get(field,''):
1088                        any_change = True
1089                        changed = True
1090                        d[field] = mapping.get(field,'')
1091                    if changed:
1092                        sub_doc.edit(mapping = d)
1093
1094
1095        # included only to change wf state from admitted to returning
1096            if res[0].review_state in ('admitted','objection_raised'):
1097                new_state = f2t[pt]['wf_transition_return']
1098                sub_obj = getattr(student_obj,f2t[pt]['id'],None)
1099                if sub_obj and new_state != "remain":
1100                    try:
1101                        self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
1102                    except:
1103                        #logger.info('%s, wf transition %s of %s failed' % (sid,new_state,sub_obj.id))
1104                        pass
1105        if res[0].review_state in ('admitted','objection_raised'):
1106            wfaction = 'return'
1107            try:
1108                self.portal_workflow.doActionFor(student_obj,wfaction)
1109                logger.info('%s, wf state changed' % sid)
1110                any_change = True
1111            except:
1112                logger.info('%s, wf transition failed, old state = %s' % (sid,res[0].review_state))
1113                pass
1114        # end inclusion
1115
1116
1117        if any_change:
1118            return sid,''
1119        else:
1120            return sid,'not modified'
1121    ###)
1122
1123
1124    security.declareProtected(ModifyPortalContent,"importData")###(
1125    def importData(self,filename,name,edit=False):
1126        """load data from CSV values"""
1127        import transaction
1128        import random
1129
1130        pm = self.portal_membership
1131        member = pm.getAuthenticatedMember()
1132
1133        logger = logging.getLogger('WAeUPTool.importData')
1134        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
1135        students_folder = self.portal_url.getPortalObject().campus.students
1136        start = True
1137        tr_count = 1
1138        total_imported = 0
1139        total_not_imported = 0
1140        total = 0
1141        iname = "import_%s" % name
1142        stool = getToolByName(self, 'portal_schemas')
1143        ltool = getToolByName(self, 'portal_layouts')
1144        schema = stool._getOb(iname)
1145        if schema is None:
1146            em = 'No such schema %s' % iname
1147            logger.error('No such schema %s' % iname)
1148            return em
1149        layout = ltool._getOb(iname)
1150        if layout is None:
1151            em = 'No such layout %s' % iname
1152            logger.error(em)
1153            return em
1154        validators = {}
1155        for widget in layout.keys():
1156            validators[widget] = layout[widget].validate
1157        if edit:
1158            importer_name = "importEdit%s" % name.capitalize()
1159        else:
1160            importer_name = "import%s" % name.capitalize()
1161        importer = getattr(self, '%s' % importer_name,None)
1162        if importer is None:
1163            em = 'No importer function %s' % importer_name
1164            logger.error(em)
1165            return em
1166        not_imported = []
1167        imported = []
1168        try:
1169            items = csv.DictReader(open("%s/import/%s.csv" % (i_home,filename),"rb"))
1170        except:
1171            em = 'Error reading %s.csv' % filename
1172            logger.error(em)
1173            return em
1174        for item in items:
1175            if start:
1176                start = False
1177                adapters = [MappingStorageAdapter(schema, item)]
1178                logger.info('%s starts import from %s.csv' % (member,filename))
1179                #import_keys = [k for k in item.keys() if not k.startswith('ignore')]
1180                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb")).next()
1181                #import pdb;pdb.set_trace()
1182                import_keys = [k for k in attrs if not k.startswith('ignore')]
1183                diff2schema = set(import_keys).difference(set(schema.keys()))
1184                diff2layout = set(import_keys).difference(set(layout.keys()))
1185                if diff2schema:
1186                    em = "not ignorable key(s) %s found in heading" % diff2schema
1187                    return em
1188                s = ','.join(['"%s"' % fn for fn in import_keys])
1189                open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(s + ',"Error"'+ '\n')
1190                s = '"id",' + s
1191                open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(s + '\n')
1192                format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
1193                format_error = format + ',"%(Error)s"'
1194                format = '"%(id)s",'+ format
1195            dm = DataModel(item, adapters,context=self)
1196            ds = DataStructure(data=item,datamodel=dm)
1197            error_string = ""
1198            for k in import_keys:
1199                if not validators[k](ds):
1200                    error_string += " %s : %s" % (k,ds.getError(k))
1201            if not error_string:
1202                item.update(dm)
1203                item['id'],error = importer(item)
1204                if error:
1205                    error_string += error
1206            if error_string:
1207                item['Error'] = error_string
1208                not_imported.append(format_error % item)
1209                total_not_imported += 1
1210            else:
1211                em = format % item
1212                imported.append(em)
1213                logger.info("%(total_imported)d of %(total)d %(em)s" % vars())
1214                tr_count += 1
1215                total_imported += 1
1216            total += 1
1217            if total_imported and not total_imported % 100:
1218                transaction.commit()
1219                if len(not_imported) > 0:
1220                    open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
1221                             '\n'.join(not_imported) + '\n')
1222                    not_imported = []
1223                if len(imported) > 0:
1224                    open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
1225                             '\n'.join(imported) + '\n')
1226                    imported = []
1227                em = '%d transactions committed\n' % (tr_count)
1228                regs = []
1229                logger.info(em)
1230                tr_count = 0
1231        if len(imported) > 0:
1232            open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
1233                                                '\n'.join(imported))
1234        if len(not_imported) > 0:
1235            open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
1236                                                '\n'.join(not_imported))
1237        em = "Imported: %d, not imported: %d of total %d" % (total_imported,total_not_imported,total)
1238        logger.info(em)
1239        return em
1240    ###)
1241
1242InitializeClass(WAeUPTool)
Note: See TracBrowser for help on using the repository browser.