source: WAeUP_SRP/base/WAeUPTool.py @ 2566

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

add logging message

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