source: WAeUP_SRP/base/WAeUPTool.py @ 2643

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

resolve #396

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