source: WAeUP_SRP/base/WAeUPTool.py @ 3164

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

fix for #477

  • Property svn:keywords set to Id
File size: 106.8 KB
RevLine 
[1991]1#-*- mode: python; mode: fold -*-
[197]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 3162 2008-02-14 08:04:50Z joachim $
[1174]20"""The WAeUP Tool Box.
[197]21"""
22
23from AccessControl import ClassSecurityInfo
[828]24from Acquisition import aq_inner
25from Acquisition import aq_parent
26from Globals import DTMLFile
27from Globals import InitializeClass
28from OFS.SimpleItem import SimpleItem
[2283]29from zExceptions import BadRequest
[197]30
[1890]31from Products.CMFCore.utils import getToolByName
[1747]32from Products.CPSSchemas.DataStructure import DataStructure
33from Products.CPSSchemas.DataModel import DataModel
34from Products.CPSSchemas.StorageAdapter import MappingStorageAdapter
[828]35from Products.CMFCore.ActionProviderBase import ActionProviderBase
36from Products.CMFCore.permissions import View
37from Products.ZCatalog.ZCatalog import ZCatalog
38from Products.CMFCore.permissions import ModifyPortalContent
[1890]39from Products.CMFCore.permissions import ManagePortal
[197]40from Products.CMFCore.utils import UniqueObject
[1194]41from Products.CMFCore.URLTool import URLTool
[1747]42from Products.CMFCore.utils import getToolByName
[1151]43from Students import makeCertificateCode
[1285]44from Globals import package_home,INSTANCE_HOME
[2094]45import DateTime,time
[1620]46import logging
[1170]47import transaction
[2193]48import csv,re,os,sys
[2413]49from shutil import copy2
[1707]50from Products.AdvancedQuery import Eq, Between, Le,In
[197]51
[2413]52p_home = package_home(globals())
53i_home = INSTANCE_HOME
54images_base = os.path.join(i_home,"images")
55
[2675]56def getImagesDir(student_id):
57    return os.path.join("%s" % images_base,student_id[0],student_id)
[2413]58
[1707]59def getObject(object,name):
60    if object.hasObject(name):
61        return getattr(object,name)
62    return None
[1720]63
[2999]64
[828]65class WAeUPTool(UniqueObject, SimpleItem, ActionProviderBase):
[197]66    """WAeUP tool"""
67
[828]68    id = 'waeup_tool'
[197]69    meta_type = 'WAeUP Tool'
[828]70    _actions = ()
[197]71    security = ClassSecurityInfo()
[828]72    security.declareObjectProtected(View)
73    manage_options = ( ActionProviderBase.manage_options
74                     + SimpleItem.manage_options
75                     )
76
[2999]77    security.declareProtected(View,'re_split') ###(
78    def re_split(self,split_string,string):
79        return re.split(split_string,string)
[3014]80    ###)
[2999]81
82    security.declareProtected(View,'difference') ###(
83    def difference(self,l1,l2):
84        return set(l1).difference(set(l2))
[3014]85    ###)
[2999]86
[1818]87    def rwrite(self,s): ###(
[1707]88        response = self.REQUEST.RESPONSE
89        response.setHeader('Content-type','text/html; charset=ISO-8859-15')
90        response.write("%s<br />\r\n" % s)
[1818]91    ###)
[1174]92
[2695]93    def addtodict(self,d,key,item): ###(
94        d[key].append(item)
95        return d[key]
96    ###)
97
[1818]98    def sleep(self,secs): ###(
99        "sleep"
100        import time
101        time.sleep(secs)
102        return
[1827]103
[1818]104###)
105
[2695]106    security.declareProtected(View,'updateRoleMappingsFor') ###(
107    def updateRoleMappingsFor(self,wf_definition,ob):
108        "do so for public"
109        wf_def = getattr(self.portal_workflow,wf_definition)
110        wf_def.updateRoleMappingsFor(ob)
111    ###)
[2699]112
[2999]113    security.declareProtected(View,'getStatesLgas') ###(
114    def getStatesLgas(self):
115        """return lga info"""
116        voc = getattr(self.portal_vocabularies,'local_gov_areas')
117        states = []
118        lgas  = []
119        d = {}
120        wd = {}
121        for k,v in voc.items():
122            parts = v.split(' / ')
123            if len(parts) == 1:
124                state = parts[0].lower()
125                lga = ""
126            elif len(parts) == 2:
127                state = "_".join(re.split('[^a-zA-Z0-9/]',parts[0].lower()))
128                lga = "-".join(re.split('[^a-zA-Z0-9/]',parts[1].lower()))
129            else:
130                continue
131            if state not in states:
132                states.append(state)
133            if lga not in lgas:
134                lgas.append(lga)
135            words = re.split('[^a-zA-Z0-9/]',k)
136            words.sort()
137            wd[k] = words
138            d[k] = v
139        mapping = {}
140        mapping['word_dict'] = wd
141        mapping['lga_dict'] = d
142        mapping['states'] = states
143        mapping['lgas'] = lgas
144        return mapping
145    ###)
146
[3014]147    security.declareProtected(View,'findLga') ###(
148    def findLga(self,words,words_dict):
149        words = re.split('[^a-zA-Z0-9/]',words)
150        lga_words = []
151        for word in words:
152            if word:
153                lga_words += word.strip().lower(),
154        lga_words.sort()
155        state_lga = ''
156        while not state_lga:
157            for k,l in words_dict.items():
158                if lga_words == l:
159                    state_lga = k
160                    break
161            break
162        return state_lga
163    ###)
[2975]164    security.declareProtected(View,'getAccessInfo') ###(
165    def getAccessInfo(self,context):
166        "return a dict with access_info"
167        logger = logging.getLogger('WAeUPTool.getAccessInfo')
168        mtool = self.portal_membership
169        member = mtool.getAuthenticatedMember()
170        member_id = str(member)
171        info = {}
172        is_anonymous = info['is_anonymous'] = mtool.isAnonymousUser()
173        is_student = info['is_student'] = ord(member_id[1]) > 48 and ord(member_id[1]) <= 57
174        is_staff = info['is_staff'] = not is_anonymous and not is_student
175        roles = member.getRolesInContext(context)
176        is_sectionofficer = info['is_sectionofficer'] = not is_student and ("SectionOfficer" in roles or
177                                                                            "SectionManager" in roles or
178                                                                            "Manager" in roles)
179        is_allowed = info['is_allowed'] = not is_anonymous
180        requested_id = context.getStudentId()
181        student_id  = None
[3025]182        if is_allowed:
183            if not is_student and requested_id:
184                student_id  = requested_id
185            elif is_anonymous or member_id != requested_id:
186                logger.info('%s tried to access %s of %s' % (member_id,context.portal_type,requested_id))
187            else:
188                student_id = member_id
[2975]189        info['student_id'] = student_id
190        return info
191    ###)
192
[1818]193    security.declareProtected(ModifyPortalContent,'openLog') ###(
[1716]194    def openLog(self,name):
195        """open a log file"""
196        version = 1
197        path = "%s/log/%s_%d.log" % (i_home,name,version)
198        while os.path.exists(path):
199            version += 1
200            path = "%s/log/%s_%d.log" % (i_home,name,version)
201        log = open(path,"w")
202        return log
[2259]203    ###)
[1716]204
[2259]205    security.declareProtected(ModifyPortalContent,'bypassQueueCatalog') ###(
206    def bypassQueueCatalog(self,enable=True):
207        """bypass the QueueCatalog by setting all indexes to process imediate,
208        if enable is True (default) the old settings are restored
209        """
[1818]210
[2259]211    ###)
212
[2094]213    security.declareProtected(ModifyPortalContent,'measureOaT') ###(
214    def measureOaT(self,method="a",probe="1000",nr_pts="1"):
215        """measure Object access Time"""
216        import random
217        if hasattr(self,'portal_catalog_real'):
218            aq_portal = self.portal_catalog_real.evalAdvancedQuery
219        else:
220            aq_portal = self.portal_catalog.evalAdvancedQuery
221        nr_pts = int(nr_pts)
222        probe = int(probe)
223        intervall = probe/10
224        objects = ("application","clearance","personal")
225        portal_types = ("StudentApplication","StudentClearance","StudentPersonal")
226        #i = random.randrange(num_objects)
227        count = 0
228        found = 0
229        not_found = 0
230        t_found = 0
231        t_not_found = 0
232        time_found = time_not_found = 0.0
233        t_time_found = t_time_not_found = 0.0
234        accessed = []
235        t_min = 1000
236        t_max = 0
237        #import pdb;pdb.set_trace()
238        students = self.portal_catalog(portal_type="Student")
239        num_students = len(students)
240        if method == "d":
241            query = Eq('path','/uniben/campus/students') & In('portal_type',portal_types[:nr_pts])
242            res = aq_portal(query)
243            brains = {}
244            for r in res:
245                sid = r.relative_path.split('/')[-2]
246                if brains.has_key(sid):
247                    brains[sid][r.portal_type] = r
248                else:
249                    brains[sid] = {r.portal_type : r}
250            brains_list = brains.keys()
251            num_objects = len(brains_list)
252        else:
253            num_objects = num_students
254        print "="*40
255        print "method: %s probes: %d nr_pts: %d num_objects: %d" % (method,
256                                                                        probe,
257                                                                        nr_pts,
258                                                                        num_objects)
259        print "nr found/not time found/not min/max"
260        elapse = time.time()
261        i_elapse = time.time()
262        c_elapse = time.clock()
263        for c in range(1,probe + 1):
264            i = random.randrange(num_objects)
265            if method in ('a','b','c'):
266                student_brain = students[i]
267            elif method == "d":
268                #import pdb;pdb.set_trace()
269                student_brain = brains[brains_list[i]]
270            if method == "c":
271                query = Eq('path',student_brain.getPath()) & In('portal_type',portal_types[:nr_pts])
272                res = aq_portal(query)
273                this_portal_types = [r.portal_type for r in res]
274            for i in range(nr_pts):
275                oid = objects[i]
276                if method == "a":
277                    try:
278                        student_path = student_brain.getPath()
279                        path = "%s/%s" % (student_path,oid)
280                        doc = self.unrestrictedTraverse(path).getContent()
281                        found += 1
282                        i_time = time.time() - i_elapse
283                        time_found += i_time
284                    except:
285                        not_found += 1
286                        i_time = time.time() - i_elapse
287                        time_not_found += i_time
288                        pass
289                elif method == "b":
290                    try:
291                        student_object = student_brain.getObject()
292                        doc = getattr(student_object,oid).getContent()
293                        found += 1
294                        i_time = time.time() - i_elapse
295                        time_found += i_time
296                    except:
297                        i_time = time.time() - i_elapse
298                        time_not_found += i_time
299                        not_found += 1
300                        pass
301                elif method == "c":
302                    if portal_types[i] in this_portal_types:
303                        found += 1
304                        doc = res[this_portal_types.index(portal_types[i])].getObject().getContent()
305                        i_time = time.time() - i_elapse
306                        time_found += i_time
307                    else:
308                        not_found += 1
309                        i_time = time.time() - i_elapse
310                        time_not_found += i_time
311                elif method == "d":
312                    if student_brain.has_key(portal_types[i]):
313                        found += 1
314                        doc = student_brain[portal_types[i]].getObject().getContent()
315                        i_time = time.time() - i_elapse
316                        time_found += i_time
317                    else:
318                        not_found += 1
319                        i_time = time.time() - i_elapse
320                        time_not_found += i_time
321                i_elapse = time.time()
322            if c and (c % intervall == 0):
323                #i_time = time.time() - i_elapse
324                t_per = 0.0
325                if found:
326                    t_per = time_found/found
327                if t_per > t_max:
328                    t_max = t_per
329                if t_per > 0.0 and t_per < t_min:
[2119]330                    t_min = t_per
[2094]331                itf = 0.0
332                if found:
333                    itf = time_found/found
334                itnf = 0.0
335                if not_found :
336                    itnf = time_not_found / not_found
[2119]337                interval_time = time_found + time_not_found
338                s = "%(c)d: %(found)d/%(not_found)d " % vars()
[2094]339                s += "%(interval_time)6.2f %(itf)6.4f/%(itnf)6.4f " % vars()
340                s += "%(t_min)6.4f/%(t_max)6.4f" %  vars()
341                print s
342                t_found += found
343                t_not_found += not_found
344                t_time_found += time_found
345                t_time_not_found += time_not_found
346                time_found = time_not_found = 0.0
347                found = not_found = 0
348        # t_found += found
349        # t_not_found += not_found
350        elapse = time.time() - elapse
351        itf = 0.0
352        if t_found:
353            itf = t_time_found/t_found
354        itnf = 0.0
355        if t_not_found:
356            itnf = t_time_not_found / t_not_found
357        #c_elapse = time.clock() - c_elapse
[2119]358        s = "%(probe)d: %(t_found)d/%(t_not_found)d " % vars()
[2094]359        s += "%(elapse)6.2f %(itf)6.4f/%(itnf)6.4f " % vars()
360        s += "%(t_min)6.4f/%(t_max)6.4f" %  vars()
361        print "-"*40
362        print s
363        rel_found = float(t_found)/probe
364        rel_not_found = float(t_not_found)/probe
365        estimated_total_time = num_objects*(rel_found*itf + rel_not_found*itnf)
366        print estimated_total_time
367    ###)
368
[1818]369    security.declareProtected(ModifyPortalContent,'writeLog') ###(
[1716]370    def writeLog(self,logfile,s):
371        """write to the log file"""
372        logfile.write(s)
373
[1818]374###)
[1720]375
[1151]376    def generateStudentId(self,letter): ###(
377        import random
378        r = random
[1194]379        ##if letter not in ('ABCDEFGIHKLMNOPQRSTUVWXY'):
380        if letter == '?':
[2695]381            letter= r.choice('ABCDEFGHKLMNPQRSTUVWXY')
[1151]382        sid = "%c%d" % (letter,r.randint(99999,1000000))
[1720]383        students = self.portal_url.getPortalObject().campus.students
[1721]384##        while hasattr(students, sid):
385##            sid = "%c%d" % (letter,r.randint(99999,1000000))
386        while self.students_catalog(id = sid):
[1720]387            sid = "%c%d" % (letter,r.randint(99999,1000000))
[1151]388        return sid
[1460]389    ###)
[1415]390
391    def generatePassword(self,s=None): ###(
392        import random
393        r = random
394        ##if letter not in ('ABCDEFGIHKLMNOPQRSTUVWXY'):
395        if s is None:
396            s = 'abcdefghklmnpqrstuvwxy23456789'
397        pw = ''
398        while len(pw) < 6:
399            pw += r.choice(s)
400        return pw
[1151]401    ###)
[828]402
[2645]403    security.declareProtected(ModifyPortalContent, 'dumpSchoolfeePayments') ###(
404    def dumpSchoolfeePayments(self):
405        "dump paid schoolfees"
[2666]406        mtool = self.portal_membership
407        member = mtool.getAuthenticatedMember()
[2645]408        logger = logging.getLogger('WAeUPTool.dumpSchoolfees')
409        aq_student = self.students_catalog.evalAdvancedQuery
410        query = In('review_state',('schoolfee_paid',
411                                   'courses_registered',
412                                   'courses_validated',
413                                   ))
414        res = aq_student(query)
[2666]415        #import pdb;pdb.set_trace()
[2645]416        l = []
417        logger.info("start for %d" % len(res))
418        count = 1
419        log_after = 100
420        for student in res:
[2656]421            if not count % log_after:
[2645]422                logger.info("processed %d total %d" % (log_after,count))
423            count += 1
424            fee_dict =self.getSchoolFee(student)
425            fulltime = student.mode.endswith('_ft')
426            d = {}
427            d['student_id'] = student.id
[2666]428            d['name'] = student.name
429            d['amount'] = fee_dict.get(new_returning)
[2645]430            l += d,
[2666]431        csv_name = self.dumpListToCSV(l,'payments')
432        logger.info('%s dumped payments to %s' % (member,export_file))
[2645]433    ###)
434
[2666]435    security.declarePublic('dumpListToCSV') ###(
436    def dumpListToCSV(self,l,filename,fields=None):
437        """dump a list of dicts to a CSV file"""
438        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
439        export_file = "%s/export/%s_%s.csv" % (i_home,filename,current,)
440        if fields is None:
441            fields = l[0].keys()
442        headline = ','.join(fields)
443        out = open(export_file,"wb")
444        out.write(headline +'\n')
445        out.close()
446        out = open(export_file,"a")
447        csv_writer = csv.DictWriter(out,fields,)
448        csv_writer.writerows(l)
449        return export_file
450    ###)
451
[2643]452    security.declareProtected(ManagePortal, 'listMembers') ###(
453    def listMembers(self):
454        "list all members"
455        mtool = self.portal_membership
456        member = mtool.getAuthenticatedMember()
457        logger = logging.getLogger('WAeUPTool.listMembers')
458        if str(member) not in ('admin','joachim'):
459            logger.info('%s tried to list members' % (member))
460            return None
461        members = self.portal_directories.members
462        all = members.listEntryIdsAndTitles()
463        l = []
464        for user_id,name in all:
465            d = {}
466            d['user_id'] = user_id
467            d['name'] = name
468            d['pw'] = getattr(getattr(members,user_id),'password')
[2645]469            d['email'] = getattr(getattr(members,user_id),'email')
470            d['groups'] = " ".join(getattr(getattr(members,user_id),'groups'))
471            d['roles'] = " ".join(getattr(getattr(members,user_id),'roles'))
[2643]472            l += d,
473        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
474        export_file = "%s/export/member_list_%s.csv" % (i_home,current,)
475        logger.info('%s dumped memberlist to %s' % (member,export_file))
476        fields = l[0].keys()
477        headline = ','.join(fields)
478        out = open(export_file,"wb")
479        out.write(headline +'\n')
480        out.close()
481        out = open(export_file,"a")
482        csv_writer = csv.DictWriter(out,fields,)
483        csv_writer.writerows(l)
484    ###)
[2656]485
[1890]486    security.declareProtected(ManagePortal, 'removeDeletedDocIds') ###(
487    def removeDeletedDocIds(self, max=1000):
488        """
489        remove deleted docids from repository commit after max
490        """
491        logger = logging.getLogger('WAeUPTool.removeDeletedDocIds')
492        repository = getToolByName(self, 'portal_repository')
493        pxtool = getToolByName(self, 'portal_proxies')
[2632]494        logger.info('start')
[1890]495        pxtool_infos = pxtool.getRevisionsUsed()
[2632]496        logger.info('found  %d used revisions ' % (len(pxtool_infos)))
[1895]497
[1890]498        nb_revs = 0
499        docids_d = {} # all docids
500        unused_docids_d = {} # all docids that are unused
501        ids_unused_revs_docids = [] # ids for revs of unused docids
[2632]502        unused_ids = [] # ids for unused revs
[1890]503        total = 0
[1895]504        idlist = repository.objectIds()
[2632]505        to_delete = 0
506        found = False
[1895]507        for id in idlist:
[1890]508            docid, rev = repository._splitId(id)
509            if docid is None:
510                logger.info("invalid doc_id %s" % docid)
511                continue
512            nb_revs += 1
513            if not pxtool_infos.has_key(docid):
[2632]514                found = True
515                to_delete += 1
516                unused_ids.append(id)
[1890]517            elif not pxtool_infos[docid].has_key(rev):
[2632]518                found = True
519                to_delete += 1
520                unused_ids.append(id)
521            if found and not to_delete % max:
522                found = False
[1890]523                #import pdb;pdb.set_trace()
[2632]524                repository.manage_delObjects(unused_ids)
[1890]525                transaction.commit()
[2632]526                logger.info('removed %d total %d unused docids ' % (max,to_delete))
527        else:
528            if unused_ids:
529                repository.manage_delObjects(unused_ids)
530                transaction.commit()
531        logger.info('finished removing %d unused docids ' % (to_delete))
[1890]532
[1895]533
[1890]534###)
535
[2562]536    security.declareProtected(View,'getCredential') ###(
[1250]537    def getCredential(self,student_id):
538        student_entry = getattr(self.portal_directories.students,student_id,None)
[2555]539        if not self.isStaff():
[2563]540            mtool = self.portal_membership
541            member = mtool.getAuthenticatedMember()
542            logger = logging.getLogger('WAeUPTool.getCredential')
543            logger.info('%s tried to access password of %s' % (member,student_id))
[2555]544            return None
[1250]545        if student_entry is None:
546            return None
[1263]547        return getattr(student_entry,"password","not set")
[1261]548    ###)
[1250]549
[2747]550    security.declarePublic('checkPassword') ###(
[1467]551    def checkPassword(self,student_id,password):
[1460]552        student_entry = getattr(self.portal_directories.students,student_id,None)
553        if student_entry is None:
554            return False
555        return getattr(student_entry,"password","not set") == password
[2747]556    ###)
[2712]557
[2747]558    security.declarePublic('checkGenericPassword') ###(
[2710]559    def checkGenericPassword(self,member_id):
560        member_entry = getattr(self.portal_directories.members,member_id,None)
561        if member_entry is None:
562            return False
[2712]563        ltool = getToolByName(self, 'portal_layouts')
[2710]564        unsecure_words = ltool._getOb('members')['w__password'].check_words
[2712]565        password = getattr(member_entry,"password","not set")
566        is_unsecure = password in unsecure_words
567        if is_unsecure:
[2713]568            logger = logging.getLogger('WAeUPTool.checkGenericPassword')
[2712]569            logger.info('Member %s tried to log in with unsecure password %s' %(member_id,password))
570        return is_unsecure
[2747]571    ###)
[1460]572
[2556]573    security.declareProtected(ModifyPortalContent,'editPassword') ###(
[1467]574    def editPassword(self,student_id,password):
575        "edit a student password"
576        student_entry = getattr(self.portal_directories.students,student_id,None)
577        if student_entry is None:
[1571]578            return
[1467]579        setattr(student_entry,'password',password)
580    ###)
581
[2555]582    security.declareProtected(ModifyPortalContent,'doCommit') ###(
[1401]583    def doCommit(self,logger=None):
584        "commit some transactions"
585        transaction.commit()
586    ###)
587
[1285]588    security.declarePublic('loadStudentFoto') ###(
[1813]589    def loadStudentFoto(self,student,filename,folder):
[1285]590        "return a student passport picture"
[2413]591        #import pdb;pdb.set_trace()
592        picture ="%s/import/%s/%s" % (i_home,folder,filename)
593        student_id = student.getId()
[2675]594        images_dir = getImagesDir(student_id)
[2413]595        if not os.path.exists(images_dir):
596            os.mkdir(images_dir)
597        image_name = os.path.join(images_dir,"passport_%(student_id)s.jpg" % vars())
598        for extension in ('.jpg','.JPG'):
599            fullname = "%(picture)s%(extension)s" % vars()
600            if os.path.exists(fullname):
601                copy2(fullname,image_name)
602                return "successfully copied passport picture"
[2467]603        return "passport picture not found: %s.jpg or .JPG" % picture
[2413]604    ###)
605
[2420]606    def old____loadStudentFoto(self,student,filename,folder): ###(
[2413]607        "return a student passport picture"
[1921]608        app = student.application
609        app_doc = app.getContent()
[1813]610        #clear = student.clearance
611        #clear_doc = clear.getContent()
612        #matric_no = clear_doc.matric_no.upper()
613        picture1 ="%s/import/%s/%s.jpg" % (i_home,folder,filename)
614        picture2 ="%s/import/%s/%s.JPG" % (i_home,folder,filename)
[1285]615        #import pdb;pdb.set_trace()
[1286]616        if os.path.exists(picture1):
617            file = open(picture1)
618        elif os.path.exists(picture2):
[1287]619            file = open(picture2)
[1286]620        else:
[1287]621            return "passport picture not found %s" % picture1
[1921]622        reopened = False
623        if self.portal_workflow.getInfoFor(app,'review_state',None) !='opened':
624            self.portal_workflow.doActionFor(app,'open')
625            reopened = True
[1285]626        outfile = file.read()
627        app_doc.manage_addFile('passport',
628                               file=outfile,
[1818]629                               title="%s.jpg" % filename)
[1921]630        if reopened:
631            self.portal_workflow.doActionFor(app,'close')
[1286]632        return "successfully loaded passport picture"
[1285]633    ###)
634
[1170]635    security.declareProtected(ModifyPortalContent,'createOne') ###(
[1194]636    def createOne(self,students_folder,student_brain,letter,commit=False):
637        sid = self.waeup_tool.generateStudentId(letter)
[1170]638        students_folder.invokeFactory('Student', sid)
639        student = getattr(students_folder,sid)
640        self.portal_workflow.doActionFor(student,'return')
641        student.manage_setLocalRoles(sid, ['Owner',])
642        matric_no = student_brain.matric_no
643        jamb_reg_no = student_brain.Entryregno
644        self.students_catalog.addRecord(id = sid,
645                                           matric_no = matric_no,
646                                           jamb_reg_no = jamb_reg_no,
647                                           sex = student_brain.Sex == "F",
648                                           name = "%s %s %s" % (student_brain.Firstname,
649                                                                student_brain.Middlename,
650                                                                student_brain.Lastname)
651                                        )
652        if commit:
653            transaction.commit()
654        return sid,jamb_reg_no
655    ###)
656
[1752]657    security.declareProtected(ModifyPortalContent,'addStudent') ###(
658    def addStudent(self,dict):
[1415]659        students_folder = self.portal_url.getPortalObject().campus.students
660        sid = self.waeup_tool.generateStudentId('?')
661        students_folder.invokeFactory('Student', sid)
662        student_obj = getattr(students_folder,sid)
[3027]663        f2t = self.field2types_student
[1772]664        #from pdb import set_trace; set_trace()
665        d = {}
[2297]666        #d['jamb_sex']  = 'M'
667        #if dict.get('sex'):
668        #    d['jamb_sex']  = 'F'
[1904]669
[1898]670        entry_session = dict.get('entry_session')
[2454]671        if entry_session == self.getSessionId()[0]:
[1898]672            wfaction = 'admit'
[1991]673            wft = 'wf_transition_admit'
[1900]674            password = None
[1904]675        else:
[1900]676            wfaction = 'return'
[1991]677            wft = 'wf_transition_return'
[1900]678            password = self.generatePassword()
[1904]679            self.makeStudentMember(sid,password)
680
[1728]681        for pt in f2t.keys():
682            student_obj.invokeFactory(pt,f2t[pt]['id'])
683            sub_obj = getattr(student_obj,f2t[pt]['id'])
684            sub_doc = sub_obj.getContent()
[1898]685            #self.portal_workflow.doActionFor(sub_obj,'open',dest_container=sub_obj)
[1772]686            d['Title'] = f2t[pt]['title']
[1728]687            for field in f2t[pt]['fields']:
[1749]688                d[field] = dict.get(field,'')
[1728]689            sub_doc.edit(mapping = d)
[1898]690            new_state = f2t[pt][wft]
[1772]691            if new_state != "remain":
692                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
[1415]693        self.portal_workflow.doActionFor(student_obj,wfaction)
694        student_obj.manage_setLocalRoles(sid, ['Owner',])
695        return sid,password
696    ###)
697
[1151]698    security.declarePublic('getCertificateBrain') ###(
699    def getCertificateBrain(self,cert_id):
700        "do it"
[1849]701        res = ZCatalog.searchResults(self.portal_catalog_real,
[1151]702                                {'portal_type':"Certificate",
703                                      'id': cert_id})
704        if res:
705            return res[0]
706        return None
707    ###)
[1160]708
[1756]709    security.declareProtected(ModifyPortalContent,'get_csv_filenames') ###(
710    def get_csv_filenames(self):
711        "do it"
[1759]712        files = [file for file in os.listdir("%s/import/" % (i_home))
[3143]713                 if file.endswith('.csv') and (file.find('imported') == -1 and
714                                               file.find('pending') == -1)]
[1756]715        return files
716    ###)
717
[1151]718    security.declarePublic('findStudentByMatricelNo') ###(
719    def findStudentByMatricelNo(self,matric_no):
720        "do it"
[1849]721        res = ZCatalog.searchResults(self.portal_catalog_real,
[1151]722                                {'portal_type':"StudentClearance",
723                                 'SearchableText': matric_no})
724        if res:
725            return res[0]
726        return None
727    ###)
728
[2557]729    security.declarePublic('makeStudentMember') ###(
[1151]730    def makeStudentMember(self,sid,password='uNsEt'):
731        """make the student a member"""
732        membership = self.portal_membership
733        membership.addMember(sid,
734                             password ,
735                             roles=('Member',
736                                     'Student',
737                                     ),
738                             domains='',
739                             properties = {'memberareaCreationFlag': False,
740                                           'homeless': True},)
741        member = membership.getMemberById(sid)
742        self.portal_registration.afterAdd(member, sid, password, None)
[1261]743        #self.manage_setLocalRoles(sid, ['Owner',])
[1151]744    ###)
[1160]745
[2560]746    security.declareProtected(View,'makeStudentData') ###(
[1158]747    def makeStudentData(self,student_id,email=None,phone_nr=None):
[1151]748        "create Datastructure for a returning Student"
[1406]749        #import pdb;pdb.set_trace()
[1571]750        logger = logging.getLogger('WAeUPTool.makeStudentData')
[1151]751        students_folder = self.portal_url.getPortalObject().campus.students
[1794]752        #res = self.students_catalog(id=student_id)
753        #if res:
754        #    st = res[0]
755        #res = self.returning_import(matric_no = st.matric_no)
[1798]756        res = self.returning_import(id = student_id)
[1151]757        if res:
[1160]758            student = res[0]
[1794]759        else:
[1816]760            logger.info('Id %s not found in returning_import' % student_id)
[1794]761            return
[1580]762        logger.info('%s creates data structure' % student_id)
[1794]763        s_results = self.results_import(matric_no = student.matric_no)
[1816]764        if s_results:
765            lnr = self.getLevelFromResultsCosCode(s_results)
766            level = "%d00" % lnr
767            verdict,eligible = self.getVerdict(s_results[0].Verdict)
[2454]768            #if eligible:
769            #    level = "%d00" % (lnr + 1)
[1816]770        else:
771            logger.info('matric_no %s not found in results_import' % student.matric_no)
[2656]772            level = ''
773            verdict = ''
[1171]774        #student should not be allowed to perform this transition
[1174]775        #wftool = self.portal_workflow
776        #wftool.doActionFor(student,'return')
[1151]777        certcode_org = student.Coursemajorcode
778        certcode = makeCertificateCode(certcode_org)
779        certificate_brain = self.getCertificateBrain(certcode)
780        if not certificate_brain:
781            em = 'Certificate %s org-code %s not found\n' % (certcode, certcode_org)
782            logger.info(em)
783        matric_no = student.matric_no
784        sid = student_id
785        student_obj = getattr(students_folder,sid)
[3114]786        if not getattr(student_obj,'application'):
787            student_obj.invokeFactory('StudentApplication','application')
[1151]788        application = student_obj.application
[1169]789        self.portal_workflow.doActionFor(application,'open',dest_container=application)
[1151]790        da = {'Title': 'Application Data'}
791        student_obj.invokeFactory('StudentPersonal','personal')
792        da['jamb_reg_no'] = student.Entryregno
[1462]793        em = self.getEntryMode(student.Entryregno)
[1401]794        da['entry_mode'] = em
[1151]795        personal = student_obj.personal
796        self.portal_workflow.doActionFor(personal,'open',dest_container=personal)
797        dp = {'Title': 'Personal Data'}
798        student_obj.invokeFactory('StudentClearance','clearance')
799        clearance = student_obj.clearance
[1169]800        self.portal_workflow.doActionFor(clearance,'open',dest_container=clearance)
[1151]801        dc = {'Title': 'Clearance/Eligibility Record'}
802        dc['matric_no'] = matric_no
803        state = student.State
804        lga = student.LGA
805        if state and lga:
806            lga =  state + ' / ' + lga
807        else:
808            lga = "None"
[1174]809        da['jamb_lga'] = dc['lga'] = lga
[1173]810        da['app_email'] = dp['email'] = email
811        da['app_mobile'] = dp['phone'] = phone_nr
[1411]812        dp['firstname'] = student.Firstname
813        dp['middlename'] = student.Middlename
814        dp['lastname'] = student.Lastname
815        da['jamb_lastname'] = "%s %s %s" % (student.Firstname,student.Middlename,student.Lastname)
[1174]816        da['jamb_sex'] = student.Sex
[1151]817        dp['sex'] = student.Sex == 'F'
818        dp['perm_address'] = student.Permanent_Address
819        application.getContent().edit(mapping=da)
[1169]820        self.portal_workflow.doActionFor(application,'close',dest_container=application)
[1151]821        personal.getContent().edit(mapping=dp)
822        clearance.getContent().edit(mapping=dc)
[1169]823        self.portal_workflow.doActionFor(clearance,'close',dest_container=clearance)
[1151]824        #
825        # Study Course
826        #
827        student_obj.invokeFactory('StudentStudyCourse','study_course')
828        studycourse = student_obj.study_course
829        self.portal_workflow.doActionFor(studycourse,'open',dest_container=studycourse)
830        dsc = {}
831        dsc['study_course'] = certcode
[1401]832        dsc['current_level'] = level
833        dsc['current_verdict'] = verdict
[1827]834        dsc['current_mode'] = em
[2466]835        dsc['current_session'] = '05'
[1151]836        studycourse.getContent().edit(mapping=dsc)
837        #
838        # Level
839        #
[1198]840##        l = getattr(studycourse,level,None)
841##        if l is None:
842##            studycourse.invokeFactory('StudentStudyLevel', level)
843##            l = getattr(studycourse, level)
844##            self.portal_workflow.doActionFor(l,'open',dest_container=l)
845##            l.getContent().edit(mapping={'Title': "Level %s" % level})
[1194]846###)
[1160]847
[2556]848    security.declareProtected(ModifyPortalContent,'admitOneStudent') ###(
[2576]849    def admitOneStudent(self,brain,entry_session,pin_password):
[2514]850        "create Datastructure for an admitted Student"
851        #import pdb;pdb.set_trace()
[2727]852        if not hasattr(self,"_v_certificates"):
853            self._v_certificates = [entry[0] for entry in self.getCertificatesVoc()]
[2514]854        students_folder = self.portal_url.getPortalObject().campus.students
[2716]855        logger = logging.getLogger('WAeUPTool.admitOneStudent')
[2514]856        if brain.status != "admitted":
857            logger.info('status of %s is %s' % (brain.reg_no,brain.status))
[2540]858            return
[2514]859        pin_parts = brain.pin.split('-')
[2755]860        if pin_parts and len(pin_parts) != 3:
[2695]861            logger.info('invalid pin %s for %s' % (brain.pin,brain.reg_no))
[2540]862            return
[2727]863        if brain.course_admitted not in self._v_certificates:
864            logger.info('certificate %s not found for %s' % (brain.course_admitted,brain.reg_no))
865            return
[2959]866        if brain.sex not in (True,False):
867            logger.info('sex of %s not available' % (brain.reg_no))
[3002]868            return
[2540]869        student_id = self.generateStudentId('?')
[2514]870        students_folder.invokeFactory('Student', student_id)
871        student_object = getattr(students_folder,student_id)
[2576]872        if pin_password:
873            password = pin_parts[2]
874            self.makeStudentMember(student_id,password = password)
[2540]875        student_object.manage_setLocalRoles(student_id, ['Owner',])
876        #logger.info("creating %s reg_no %s" % (student_id,brain.reg_no))
[2514]877        #
878        # application
879        #
[2540]880        student_object.invokeFactory('StudentApplication','application')
881        application = student_object.application
[2575]882        #self.portal_workflow.doActionFor(application,'open',dest_container=application)
[2514]883        da = {'Title': 'Application Data'}
884        da['jamb_reg_no'] = brain.reg_no
[2576]885
[2540]886        sex = 'M'
887        if brain.sex:
888            sex = 'F'
889        da['jamb_sex'] = sex
[2670]890        da['jamb_age'] = brain.jamb_age
[2575]891        #da['app_ac_pin'] = brain.pin
[2670]892        da['jamb_lga'] = brain.jamb_lga
893        da['jamb_state'] = brain.jamb_state
[2575]894        da['jamb_score'] = brain.aggregate
[2514]895        da['app_email'] = brain.email
[2540]896        da['app_mobile'] = brain.phone
[2575]897        if brain.entry_mode:
898            da['entry_mode'] = brain.entry_mode
899        elif brain.screening_type == 'pume':
900            da['entry_mode'] = 'ume_ft'
901        elif brain.screening_type == 'pde':
902            da['entry_mode'] = 'de_ft'
[2950]903        elif brain.screening_type == 'pce':
[2959]904            da['entry_mode'] = 'pce'
[2950]905        elif brain.screening_type == 'prence':
[2959]906            da['entry_mode'] = 'prence'
[2575]907        else:
[2921]908            da['entry_mode'] = ''
[2575]909        da['entry_session'] = entry_session
910        da['jamb_lastname'] = brain.lastname
911        da['jamb_middlename'] = brain.middlenames   # different field names!
[2670]912        da['jamb_firstname'] = brain.firstname
[2575]913        da['screening_application_date'] = brain.application_date
914        da['date_of_birth'] = brain.date_of_birth
915        da['jamb_first_cos'] = brain.course1
916        da['jamb_second_cos'] = brain.course2
917        da['course3'] = brain.course3
918        da['screening_type'] = brain.screening_type
919        da['screening_score'] = brain.screening_score
920        da['screening_date'] = brain.screening_date
921        da['hq_type'] = brain.hq_type
922        da['hq_grade'] = brain.hq_grade
923        da['aos'] = brain.aos
[2576]924
[2514]925        application.getContent().edit(mapping=da)
[2575]926        #self.portal_workflow.doActionFor(application,'close',dest_container=application)
[2514]927        #
928        # personal
929        #
[2540]930        student_object.invokeFactory('StudentPersonal','personal')
931        personal = student_object.personal
[2575]932        #self.portal_workflow.doActionFor(personal,'open',dest_container=personal)
[2514]933        dp = {'Title': 'Personal Data'}
934        dp['sex'] = brain.sex
935        dp['email'] = brain.email
[2540]936        dp['phone'] = brain.phone
[2514]937        dp['lastname'] = brain.lastname
[2575]938        dp['middlename'] = brain.middlenames   # different field names!
[2576]939        dp['firstname'] = brain.firstname
[2514]940        personal.getContent().edit(mapping=dp)
941        #
942        # clearance
943        #
[2540]944        student_object.invokeFactory('StudentClearance','clearance')
945        clearance = student_object.clearance
[2575]946        #self.portal_workflow.doActionFor(clearance,'open',dest_container=clearance)
[2514]947        dc = {'Title': 'Clearance/Eligibility Record'}
948        dc['lga'] = brain.lga
[2670]949        dc['birthday'] = brain.date_of_birth
[2514]950        clearance.getContent().edit(mapping=dc)
[2575]951        #self.portal_workflow.doActionFor(clearance,'close',dest_container=clearance)
[2514]952        #
953        # study Course
954        #
[2540]955        student_object.invokeFactory('StudentStudyCourse','study_course')
956        studycourse = student_object.study_course
[2575]957        #self.portal_workflow.doActionFor(studycourse,'open',dest_container=studycourse)
[2514]958        dsc = {}
959        dsc['study_course'] = brain.course_admitted
960        dsc['current_verdict'] = ''
[2575]961        dsc['current_mode'] = da['entry_mode']
962        if da['entry_mode'].startswith('de'):
963            dsc['current_level'] = '200'
[3040]964        elif da['entry_mode'].startswith('pre'):
965            dsc['current_level'] = '000'
[2575]966        else:
967            dsc['current_level'] = '100'
968        dsc['current_session'] = entry_session
[2514]969        studycourse.getContent().edit(mapping=dsc)
970        #
971        # payments folder
[2540]972        student_object.invokeFactory('PaymentsFolder','payments')
973        payments = getattr(student_object,'payments')
[2514]974        dpay = {}
975        dpay['Title'] = 'Payments'
976        payments.getContent().edit(mapping=dpay)
[2540]977        self.portal_workflow.doActionFor(payments,'open')
[2580]978        #
979        # passport foto
980        app_picture ="%s/import/images/%s/%s_passport.jpg" % (i_home,
981                                                              brain.screening_type,
982                                                              brain.reg_no)
[2675]983        images_dir = getImagesDir(student_id)
984        #images_dir = os.path.join("%s" % images_base,student_id)
985        letter_dir,student_dir = os.path.split(images_dir)
986        if not os.path.exists(letter_dir):
987            os.mkdir(letter_dir)
[2580]988        if not os.path.exists(images_dir):
989            os.mkdir(images_dir)
990        image_name = os.path.join(images_dir,"passport_%(student_id)s.jpg" % vars())
991        if os.path.exists(app_picture):
992            copy2(app_picture,image_name)
993        else:
994            logger.info('passport of %s/%s not found: %s' % (student_id,
995                                                             brain.reg_no,
996                                                             app_picture))
[2656]997
[2514]998        return student_id
999    ###)
1000
[2556]1001    security.declareProtected(ModifyPortalContent,'makeStudentLevel') ###(
[1194]1002    def makeStudentLevel(self,student_id):
1003        "create the StudyLevel for a returning Student"
1004        #import pdb;pdb.set_trace()
[1571]1005        logger = logging.getLogger('WAeUPTool.makeStudentLevel')
[1194]1006        students_folder = self.portal_url.getPortalObject().campus.students
1007        res = self.students_catalog(id=student_id)
1008        if res:
1009            st = res[0]
1010        course = st.course
1011        matric_no = st.matric_no
1012        level = st.level
1013        res = self.results_import(matric_no = matric_no)
1014        if res:
1015            results = res
[1571]1016        logger.info('%s creating Level %s' % (student_id,level))
[1194]1017        #
1018        # Level
1019        #
1020        student_obj = getattr(self.portal_url.getPortalObject().campus.students,student_id)
1021        studycourse = getattr(student_obj,"study_course",None)
1022        self.portal_workflow.doActionFor(studycourse,'close_for_edit',dest_container=studycourse)
1023        l = getattr(studycourse,level,None)
1024        if l is None:
1025            studycourse.invokeFactory('StudentStudyLevel', level)
1026            l = getattr(studycourse, level)
1027            self.portal_workflow.doActionFor(l,'open',dest_container=l)
1028            l.getContent().edit(mapping={'Title': "Level %s" % level})
1029        ###)
1030
[2000]1031    security.declarePublic('getHallInfo') ###(
1032    def getHallInfo(self,bed):
1033        """return Hall Info"""
[828]1034        info = {}
1035        hall,block,room,letter = bed.split('_')
[1846]1036        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="AccoHall",id=hall)
[828]1037        if res and len(res) == 1:
1038            hall_brain = res[0]
1039            hall_doc = hall_brain.getObject().getContent()
1040        else:
1041            return info
1042        info['hall_title'] = hall_brain.Title
1043        info['maintenance_code'] = hall_doc.maintenance_code
[1846]1044        res = ZCatalog.searchResults(self.portal_catalog_real,portal_type="ScratchCardBatch")
[828]1045        batch_doc = None
1046        for brain in res:
1047            if brain.id.startswith(info['maintenance_code']):
1048                batch_doc = brain.getObject().getContent()
1049                break
1050        if batch_doc is None:
[2913]1051            info['maintenance_fee'] = ''
[828]1052        else:
1053            info['maintenance_fee'] = batch_doc.cost
1054        return info
[1151]1055    ###)
[828]1056
[2396]1057    security.declareProtected(ModifyPortalContent,'removePictureFolder') ###(
1058    def removePictureFolder(self,student_id):
1059        """remove picture_folder by renaming it"""
1060        path = 'images'
1061        picture_path = os.path.join(i_home,path,student_id)
1062        if not os.path.exists(picture_path):
1063            return False
1064        os.rename(picture_path,picture_path + "_removed")
1065        return True
1066    ###)
1067
1068    security.declareProtected(ModifyPortalContent,'restorePictureFolder') ###(
1069    def restorePictureFolder(self,student_id):
1070        """restore picture_folder by renaming it"""
1071        path = 'images'
1072        picture_path = os.path.join(i_home,path,student_id)
1073        if not os.path.exists(picture_path + "_removed"):
1074            return False
1075        os.rename(picture_path + "_removed",picture_path)
1076        return True
1077    ###)
1078
[2363]1079    security.declarePublic('picturesExist') ###(
[2420]1080    def picturesExist(self, ids,student_id=None):
[2363]1081        """check if pictures exist in the filesystem"""
[2420]1082        if student_id is None:
1083            student_id = self.getStudentId()
1084        if student_id is None:
1085            return False
[2675]1086        picture_path = getImagesDir(student_id)
1087        #picture_path = os.path.join(images_base,student_id)
[2363]1088        if not os.path.exists(picture_path):
1089            return False
1090        pictures  = [picture[:picture.rfind('_')] for picture in os.listdir(picture_path)]
1091        return set(ids).issubset(set(pictures))
1092    ###)
1093
[2364]1094    security.declarePublic('picturesList') ###(
1095    def picturesList(self):
1096        """check if pictures exist in the filesystem"""
1097        path = 'images'
1098        student_id = self.getStudentId()
[2677]1099        #picture_path = os.path.join(i_home,path,student_id)
1100        picture_path = getImagesDir(student_id)
[2364]1101        if not os.path.exists(picture_path):
1102            return []
1103        return [picture[:picture.rfind('_')] for picture in os.listdir(picture_path)]
1104    ###)
1105
[2114]1106    security.declarePublic('showFsPicture') ###(
1107    def showFsPicture(self,path):
1108        """return a picture from the filesystem"""
[2675]1109        #picture_path = os.path.join(i_home,path)
1110        picture_path = os.path.join(images_base,path)
[2335]1111        response = self.REQUEST.RESPONSE
[2342]1112        #import pdb;pdb.set_trace()
[2354]1113        registry = getToolByName(self, 'mimetypes_registry')
1114        mimetype = str(registry.lookupExtension(path.lower()) or
1115                    registry.lookupExtension('file.bin'))
[2114]1116        if os.path.exists(picture_path):
[2354]1117            response.setHeader('Content-type',mimetype)
[2114]1118            return open(picture_path).read()
[2335]1119        picture_path = os.path.join(i_home,'import',path)
1120        if os.path.exists(picture_path):
1121            return open(picture_path).read()
[2114]1122    ###)
1123
[1151]1124    security.declareProtected(ModifyPortalContent,'deleteAllCourses') ###(
1125    def deleteAllCourses(self,department="All"):
1126        ''' delete the courses'''
1127        pm = self.portal_membership
1128        member = pm.getAuthenticatedMember()
[1160]1129
[1151]1130        if str(member) not in ("henrik","joachim"):
1131            return "not possible"
1132        if department == "All":
1133            res = self.portal_catalog({'meta_type': 'Department'})
1134        if len(res) < 1:
1135            return "No Departments found"
[1160]1136
[1151]1137        deleted = []
1138        for dep in res:
1139            cf = dep.getObject().courses
1140            if cf:
1141                cf.manage_delObjects(ids=cf.objectIds())
1142                deleted.append("deleted Courses in %s" % dep.getId)
1143        return "\r".join(deleted)
[1160]1144    ###)
[1151]1145
[1572]1146    security.declareProtected(ModifyPortalContent,'getLogfileLines') ###(
1147    def getLogfileLines(self,filename="event.log",numlines=20):
1148        """Get last NUMLINES lines of logfile FILENAME.
[1160]1149
[1572]1150        Return last lines' of a file in the instances logfile directory as
1151        a list. The number of returned lines equals `numlines' or less. If
1152        less than `numlines' lines are available, the whole file ist
1153        returned. If the file can not be opened or some other error
1154        occurs, empty list is returend.
1155        """
1156        result = []
1157        lines_hit = 0
1158
1159        # We only handle files in instances' log directory...
1160        logpath = os.path.join(i_home, "log")
1161        filename = str(os.path.abspath( os.path.join( logpath, filename )))
1162        if not filename.startswith( logpath ):
1163            # Attempt to access file outside log-dir...
1164            return []
1165
1166        try:
1167            fd = file( filename, "rb" )
1168        except IOError:
1169            return []
1170        if not fd:
1171            return []
1172
1173        if os.linesep == None:
1174            linesep = '\n'
1175        else:
1176            linesep = os.linesep
1177
1178        # Try to find 'numlines' times a lineseparator, searching from end
1179        # and moving to the beginning of file...
1180        fd.seek( 0, 2) # Move to end of file...
1181        while lines_hit < numlines:
1182            if fd.read(1) == linesep[-1]: # This moves filedescriptor
1183                                          # one step forward...
1184                lines_hit += 1
1185            try:
1186                fd.seek( -2, 1) # Go two bytes back from current pos...
1187            except IOError:
1188                # We cannot go back two bytes. Maybe the file is too small...
1189                break
1190        fd.seek(2,1)
1191
1192        # Read all lines from current position...
1193        result = fd.readlines()
1194        # Remove line endings...
1195        result = [x.strip() for x in result]
1196        fd.close()
1197        return result
1198    ###)
1199
[1700]1200    security.declareProtected(ModifyPortalContent,"getCallbacksFromLog")###(
[1665]1201    def getCallbacksFromLog(self,filename):
1202        """fix Online Payment Transactions from Z2.log entries"""
1203        import transaction
1204        import random
1205        from cgi import parse_qs
1206        from urlparse import urlparse
1207        #from pdb import set_trace
1208        wftool = self.portal_workflow
1209        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
1210        students_folder = self.portal_url.getPortalObject().campus.students
1211        s = r'(?P<client_ip>\S+) - (?P<member_id>\S+) \['
1212        s += r'(?P<date>.*)\] "(?P<get>.*)" (?P<codes>\d+ \d+) "'
1213        s += r'(?P<intersw>.*)" "(?P<agent>.*)"'
1214        data = re.compile(s)
1215        start = True
1216        tr_count = 1
1217        total = 0
1218        #name = 'pume_results'
1219        #name = 'epaymentsuccessful_z2log2'
1220        name = filename
1221        no_import = []
1222        imported = []
1223        logger = logging.getLogger('WAeUPTool.getFailedTransactions')
1224        try:
1225            transactions = open("%s/import/%s" % (i_home,name),"rb").readlines()
1226        except:
1227            logger.error('Error reading %s' % name)
1228            return
1229        tas = []
1230        for line in transactions:
1231            dict = {}
1232            items = data.search(line)
1233            dict['idict'] = idict = items.groupdict()
1234            #print idict
1235            #from pdb import set_trace;set_trace()
1236            urlparsed = urlparse(idict['get'][4:])
1237            #print urlparsed
1238            path = urlparsed[2].split('/')
1239            dict['student_id'] = student_id = path[8]
1240            dict['payment_id'] = payment_id = path[10]
1241            dict['qs_dict'] = qs_dict = parse_qs(urlparsed[4])
1242            tas.append(dict)
1243            tr_count += 1
1244        return tas
1245    ###)
1246
[1620]1247    security.declareProtected(ModifyPortalContent,"importOnlinePaymentTransactions")###(
1248    def importOnlinePaymentTransactions(self):
1249        """load Online Payment Transactions from CSV values"""
1250        import transaction
1251        import random
1252        #from pdb import set_trace
1253        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
[1625]1254        opt = self.online_payments_import
[1620]1255        students_folder = self.portal_url.getPortalObject().campus.students
1256        start = True
1257        tr_count = 1
1258        total = 0
1259        #name = 'pume_results'
1260        name = 'OnlineTransactions'
1261        no_import = []
1262        imported = []
1263        logger = logging.getLogger('WAeUPTool.importOnlinePaymentTransactions')
1264        try:
1265            transactions = csv.DictReader(open("%s/import/%s.csv" % (i_home,name),"rb"))
1266        except:
1267            logger.error('Error reading %s.csv' % name)
1268            return
1269        for pay_transaction in transactions:
1270            if start:
1271                start = False
1272                logger.info('Start loading from %s.csv' % name)
1273                s = ','.join(['"%s"' % fn for fn in pay_transaction.keys()])
1274                no_import.append('%s,"Error"' % s)
1275                format = ','.join(['"%%(%s)s"' % fn for fn in pay_transaction.keys()])
1276                format_error = format + ',"%(Error)s"'
1277            data = {}
[1644]1278
[1781]1279            # format of the first file sent by Tayo
[1643]1280            #data['datetime'] = date = DateTime.DateTime(pay_transaction['Date'])
1281            #data['student_id'] = student_id = pay_transaction['Payer ID']
1282            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
1283            #data['response_code'] = response_code = pay_transaction['Resp Code']
1284            #data['amount'] = amount = pay_transaction['Amount']
[1644]1285
[1781]1286            # format of the second file sent by Tayo
[1798]1287            #data['datetime'] = date = 0
1288            #data['student_id'] = student_id = pay_transaction['Payer ID']
1289            #data['order_id'] = order_id = pay_transaction['Order ID (Tranx Ref)']
1290            #data['response_code'] = response_code = '00'
1291            #data['amount'] = amount = pay_transaction['Amount']
[1813]1292
[1798]1293            # format of the third file sent by Kehinde
[1643]1294            data['datetime'] = date = 0
[1798]1295            data['student_id'] = student_id = pay_transaction['customer_id']
1296            data['order_id'] = order_id = pay_transaction['merchant_reference']
[1644]1297            data['response_code'] = response_code = '00'
[1813]1298            data['amount'] = amount = pay_transaction['Amount']
[1644]1299
[1620]1300            dup = False
1301            if response_code == "12":
1302                continue
1303            try:
1304                opt.addRecord(**data)
1305            except ValueError:
1306                dup = True
1307            #from pdb import set_trace;set_trace()
1308            if dup:
1309                if response_code == "00":
[1798]1310                    try:
1311                        opt.modifyRecord(**data)
1312                    except:
1313                        logger.info("duplicate uid, order_id %(order_id)s, student_id %(student_id)s, response_code %(response_code)s" % data)
1314                        continue
[1620]1315                else:
[1674]1316                    pay_transaction['Error'] = "Duplicate order_id"
[1620]1317                    no_import.append( format_error % pay_transaction)
[1674]1318                    logger.info("duplicate order_id %(order_id)s for %(student_id)s %(response_code)s" % data)
[1620]1319                    continue
1320            tr_count += 1
1321            if tr_count > 1000:
1322                if len(no_import) > 0:
1323                    open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
1324                             '\n'.join(no_import) + '\n')
1325                    no_import = []
[1645]1326                em = '%d transactions committed\n' % (tr_count)
[1620]1327                transaction.commit()
1328                regs = []
1329                logger.info(em)
1330                total += tr_count
1331                tr_count = 0
1332        open("%s/import/%s_not_imported%s.csv" % (i_home,name,current),"a").write(
1333                                                '\n'.join(no_import))
1334        return self.REQUEST.RESPONSE.redirect("%s" % self.REQUEST.get('URL1'))
1335    ###)
[2237]1336
[2264]1337    security.declareProtected(ModifyPortalContent,'mass_create_faculty') ###(
1338    def mass_create_faculty(self,mapping):
1339        "create a faculty"
1340        logger = logging.getLogger('WAeUPTool.mass_create_faculty')
1341        academics_folder = self.portal_url.getPortalObject().campus.academics
1342        fid = mapping['code']
1343        if getattr(academics_folder,fid,None) is not None:
1344            return '', "Faculty with ID: %s exists" % fid
[2267]1345        logger.info('Creating Faculty %(code)s, %(title)s' % mapping)
[2283]1346        try:
1347            academics_folder.invokeFactory('Faculty', fid)
1348        except BadRequest,E:
1349            return '', "%s" % E
[2264]1350        f = getattr(academics_folder,fid,None)
1351        f.getContent().edit(mapping=mapping)
1352        return fid,''
1353    ###)
1354
[2288]1355    security.declareProtected(ModifyPortalContent,'mass_edit_faculty') ###(
[2264]1356    def mass_edit_faculty(self,mapping):
1357        "edit a faculty"
1358        logger = logging.getLogger('WAeUPTool.mass_edit_faculty')
1359        academics_folder = self.portal_url.getPortalObject().campus.academics
1360        fid = mapping['code']
1361        f = getattr(academics_folder,fid,None)
1362        if f is None:
1363            return '', "Faculty with ID: %s does not exist" % fid
[2267]1364        logger.info('Editing Faculty %(code)s, %(title)s' % mapping)
[2264]1365        f.getContent().edit(mapping=mapping)
1366        return fid,''
1367    ###)
1368
[2234]1369    security.declareProtected(ModifyPortalContent,'mass_create_department') ###(
[2193]1370    def mass_create_department(self,mapping):
1371        "create a department in the correct faculty"
1372        logger = logging.getLogger('WAeUPTool.mass_create_department')
1373        fid = mapping['faculty_code']
1374        if getattr(self,'_v_faculties',None) is None:
1375            res = self.portal_catalog(portal_type = "Faculty")
1376            self._v_faculties = {}
1377            for f in res:
1378                self._v_faculties[f.getId] = f.getObject()
1379        f = self._v_faculties.get(fid,None)
1380        if f is None:
1381            return '', "No Faculty with ID: %s" % fid
1382        else:
1383            did = mapping.get('code')
1384            d = getattr(f,did,None)
1385            if d is None or d.portal_type == "Faculty":
[2267]1386                logger.info('Creating Department %(code)s, %(title)s' % mapping)
[2283]1387                try:
1388                    f.invokeFactory('Department', did)
1389                except BadRequest,E:
1390                    return '', "%s" % E
[2193]1391                d = getattr(f,did)
1392                d.invokeFactory('CoursesFolder','courses')
1393                courses = getattr(d,'courses')
1394                dict = {'Title': 'Courses'}
1395                courses.getContent().edit(mapping=dict)
1396                d.invokeFactory('CertificatesFolder','certificates')
1397                certificates = getattr(d,'certificates')
1398                dict = {'Title': 'Certificates'}
1399                certificates.getContent().edit(mapping=dict)
1400            d.getContent().edit(mapping=mapping)
1401        return did,''
1402    ###)
[1620]1403
[2234]1404    security.declareProtected(ModifyPortalContent,'mass_edit_department') ###(
[2193]1405    def mass_edit_department(self,mapping):
1406        "create a department in the correct faculty"
1407        logger = logging.getLogger('WAeUPTool.mass_create_department')
1408        academics_folder = self.portal_url.getPortalObject().campus.academics
1409        fid = mapping['faculty_code']
1410        did = mapping.get('code')
1411        try:
1412            d = getattr(getattr(academics_folder,fid),did,None)
1413        except KeyError:
1414            return '', "Department %s or Faculty %s wrong" % (did,fid)
1415        else:
1416            if d is None or d.portal_type == "Faculty":
[2267]1417                logger.info('Editing Department %(code)s, %(title)s' % mapping)
[2193]1418            d.getContent().edit(mapping=mapping)
1419        return did,''
1420    ###)
[1786]1421
[2265]1422    security.declareProtected(ModifyPortalContent,'mass_create_course') ###(
1423    def mass_create_course(self,mapping):
[2315]1424        #import pdb;pdb.set_trace()
[2265]1425        if getattr(self,'_v_course_list',None) is None:
1426            self._v_course_list = []
1427        if getattr(self,'_v_departments',None) is None:
1428            res = self.portal_catalog(portal_type = "Department")
[2313]1429            self._v_department_courses = {}
[2265]1430            for d in res:
[2313]1431                self._v_department_courses[d.getId] = getattr(d.getObject(),"courses",None)
[2265]1432        did = mapping['department_code']
[2313]1433        d = self._v_department_courses.get(did,None)
[2265]1434        if d is None:
[2282]1435            return '', "No Department with ID: %s" % did
[2265]1436        course_id = mapping.get('code')
1437        if course_id in self._v_course_list:
[2313]1438            return '', "Duplicate Course ID: %s" % did
[2265]1439        c = getattr(d,course_id,None)
1440        if c is not None:
[2313]1441            return '', "Duplicate Course ID: %s" % did
[2283]1442        try:
1443            d.invokeFactory('Course', course_id)
1444        except BadRequest,E:
1445            return '', "%s" % E
[2265]1446        self._v_course_list.append(course_id)
1447        c = getattr(d,course_id)
1448        c.getContent().edit(mapping=mapping)
1449        return course_id,''
1450    ###)
1451
[2282]1452    security.declareProtected(ModifyPortalContent,'mass_edit_course') ###(
1453    def mass_edit_course(self,mapping):
1454        #import pdb;pdb.set_trace()
1455        course_id = mapping.get('code')
1456        res = self.portal_catalog(id=course_id)
1457        if not res:
[2289]1458            return '', "No Course with ID: %s" % course_id
[2282]1459        c = res[0].getObject()
1460        c.getContent().edit(mapping=mapping)
1461        return course_id,''
1462    ###)
1463
1464    security.declareProtected(ModifyPortalContent,'mass_create_certificate') ###(
1465    def mass_create_certificate(self,mapping):
1466        if getattr(self,'_v_certificate_list',None) is None:
1467            self._v_certificate_list = []
[2313]1468        if getattr(self,'_v_department_certificates',None) is None:
[2282]1469            res = self.portal_catalog(portal_type = "Department")
[2313]1470            self._v_department_certificates = {}
[2282]1471            for d in res:
[2313]1472                self._v_department_certificates[d.getId] = getattr(d.getObject(),"certificates",None)
[2282]1473        did = mapping['department_code']
[2313]1474        d = self._v_department_certificates.get(did,None)
[2282]1475        if d is None:
1476            return '', "No Department with ID: %s" % did
1477        certificate_id = mapping.get('code')
1478        if certificate_id in self._v_certificate_list:
[2313]1479            return '', "Duplicate Certificate ID: %s" % did
[2282]1480        c = getattr(d,certificate_id,None)
1481        if c is not None:
[2313]1482            return '', "Duplicate Certificate ID: %s" % did
[2283]1483        try:
1484            d.invokeFactory('Certificate', certificate_id)
1485        except BadRequest,E:
1486            return '', "%s" % E
[2282]1487        self._v_certificate_list.append(certificate_id)
1488        c = getattr(d,certificate_id)
1489        c.getContent().edit(mapping=mapping)
1490        return certificate_id,''
1491    ###)
1492
1493    security.declareProtected(ModifyPortalContent,'mass_edit_certificate') ###(
1494    def mass_edit_certificate(self,mapping):
1495        #import pdb;pdb.set_trace()
1496        certificate_id = mapping.get('code')
1497        res = self.portal_catalog(id=certificate_id)
1498        if not res:
[3042]1499            return '', "No Certificate with ID: %s" % certificate_id
[2282]1500        c = res[0].getObject()
1501        c.getContent().edit(mapping=mapping)
1502        return certificate_id,''
1503    ###)
1504
[2520]1505    security.declareProtected(ModifyPortalContent,'mass_create_application') ###(
1506    def mass_create_application(self,mapping):
[2514]1507        #import pdb;pdb.set_trace()
[2547]1508        reg_no = mapping.get('reg_no')
[2514]1509        try:
1510            self.applicants_catalog.addRecord(**mapping)
1511        except ValueError:
[2548]1512            return '', "applicant record with reg_no %s already exists" % reg_no
[2546]1513        return reg_no,''
[2514]1514    ###)
1515
[2520]1516    security.declareProtected(ModifyPortalContent,'mass_edit_application') ###(
1517    def mass_edit_application(self,mapping):
[2514]1518        #import pdb;pdb.set_trace()
1519        reg_no = mapping.get('reg_no')
[2731]1520        status = mapping.get('status')
[2699]1521        res = self.applicants_catalog(reg_no = reg_no)
1522        if len(res):
[2731]1523            if res[0].status == 'created' and status != 'created':
1524                return '', "student object with id %s for %s already created, status cannot be changed" % (res[0].student_id, reg_no)
1525            elif status == 'created' and res[0].status != 'created':
1526                return '', "student object for %s has not yet been created, status cannot be set to 'created'" % (reg_no)
[2699]1527            else:
1528                self.applicants_catalog.modifyRecord(**mapping)
1529                return reg_no,''
1530        else:
[2548]1531            return '', "applicant record with reg_no %s does not exist" % reg_no
[2701]1532
[2514]1533    ###)
1534
[2501]1535    security.declareProtected(ModifyPortalContent,'mass_create_course_result') ###(
1536    def mass_create_course_result(self,mapping):
[2500]1537        #import pdb;pdb.set_trace()
[2747]1538        students_folder = self.portal_url.getPortalObject().campus.students
[2501]1539        if getattr(self,'_v_courses',None) is None:
1540            res = self.courses_catalog()
1541            self._v_courses = {}
1542            for brain in res:
[2520]1543                self._v_courses[brain.code] = brain
[2747]1544        if getattr(self,'_v_level_created',None) is None:
1545            self._v_level_created = []
[2500]1546        course_id = mapping.get('code')
[2501]1547        if course_id not in self._v_courses.keys():
[2754]1548            return '', "No course with ID: %s" % course_id
[2773]1549        id_key = ''
1550        for id_key in ('student_id','matric_no'):
1551            id_field = mapping.get(id_key,None)
1552            if id_field is not None:
1553                student_id = id_field
1554                break
1555        query = Eq(id_key,id_field)
1556        res = self.students_catalog.evalAdvancedQuery(query)
1557        if not res:
[2750]1558            return '', "no student with %(id_key)s %(id_field)s" % vars()
[2773]1559        student_rec = res[0]
[2776]1560        if id_field != "student_id":
1561            mapping['student_id'] = student_rec.id
[2766]1562        student_id = student_rec.id
1563        level_id = mapping['level_id']
1564        code = mapping['code']
[2774]1565        if student_id not in self._v_level_created:
[2747]1566            try:
1567                context = getattr(getattr(students_folder,
[2766]1568                                          "%(student_id)s" % vars()),
[2747]1569                                  'study_course')
1570            except:
[2766]1571                return '', "could not create level %(level_id)s for %(student_id)s" % vars()
1572            if level_id not in context.objectIds():
1573                context.invokeFactory('StudentStudyLevel',"%(level_id)s" % vars())
1574                level = getattr(context,"%(level_id)s" % vars())
[2747]1575                self.portal_workflow.doActionFor(level,'open')
[2754]1576                # the session string must not be copied into the level object
[2775]1577                current_verdict = getattr(student_rec,'current_verdict','')
1578                current_session = getattr(student_rec,'current_session','')
1579                if current_verdict and student_rec.current_level == level_id:
[2771]1580                    level.getContent().edit(mapping={'verdict': "%s" %
[2775]1581                                                     current_verdict,
[2771]1582                                                     'session': "%s" %
[2775]1583                                                     current_session,
[2766]1584                                                    })
1585                    self.portal_workflow.doActionFor(level,'close')
1586            self._v_level_created += student_id,
1587        mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % vars()
[2746]1588        for k in ('semester','credits',):
[2501]1589            mapping[k] = getattr(self._v_courses[course_id],k)
[2500]1590        try:
[2501]1591            self.course_results.addRecord(**mapping)
1592        except ValueError:
1593            return '', "course result already exists: %s" % key
1594        return key,''
[2500]1595    ###)
1596
1597    security.declareProtected(ModifyPortalContent,'mass_edit_course_result') ###(
1598    def mass_edit_course_result(self,mapping):
[3143]1599        #import pdb;pdb.set_trace()
[3133]1600        while True:
1601            result_id = mapping.get('id',None)
1602            if result_id is not None:
1603                mapping['key'] = key = result_id
[2776]1604                break
[3133]1605            for id_key in ('student_id','matric_no'):
1606                id_field = mapping.get(id_key,None)
1607                if id_field is not None:
1608                    student_id = id_field
1609                    break
1610            query = Eq(id_key,id_field)
1611            res = self.students_catalog.evalAdvancedQuery(query)
1612            if not res:
1613                return '', "No student with %(id_key)s %(id_field)s" % vars()
[3143]1614            if id_field == "matric_no":
[3133]1615                mapping['student_id'] = res[0].id
[3143]1616            elif id_field == "student_id":
1617                mapping['matric_no'] = res[0].matric_no
[3133]1618            if not res:
1619                return '', "No student with %(id_key)s %(id_field)s" % vars()
1620            student_rec = res[0]
1621            student_id = student_rec.id
1622            level_id = mapping['level_id']
1623            code = mapping['code']
1624            mapping['key'] = key = "%(student_id)s|%(level_id)s|%(code)s" % vars()
1625            break
[2500]1626        try:
1627            self.course_results.modifyRecord(**mapping)
1628        except KeyError:
[3143]1629            return key, "No course result to edit: %s" % key,mapping
[2500]1630        return key,''
1631    ###)
1632
[3057]1633    security.declareProtected(ModifyPortalContent,'mass_delete_course_result') ###(
1634    def mass_delete_course_result(self,mapping):
1635        id_key = ''
1636        for id_key in ('student_id','matric_no'):
1637            id_field = mapping.get(id_key,None)
1638            if id_field is not None:
1639                student_id = id_field
1640                break
1641        query = Eq(id_key,id_field)
1642        res = self.students_catalog.evalAdvancedQuery(query)
1643        if not res:
1644            return '', "No student with %(id_field)s: %(id_key)s" % vars()
1645        if id_field != "student_id":
1646            mapping['student_id'] = res[0].id
1647        if not res:
1648            return '', "no student with %(id_key)s %(id_field)s" % vars()
1649        student_rec = res[0]
1650        student_id = student_rec.id
1651        level_id = mapping['level_id']
1652        code = mapping['code']
1653        key = "%(student_id)s|%(level_id)s|%(code)s" % vars()
1654        # for k in ('semester',):
1655        #     mapping[k] = getattr(self._v_courses[course_id],k)
1656        if self.course_results.getRecordByKey(key) is None:
1657            return '', "no course-result with %(key)s" % vars()
1658        self.course_results.deleteRecord(key)
1659        return key,''
1660    ###)
1661
[2282]1662    security.declareProtected(ModifyPortalContent,'mass_create_certificate_course') ###(
1663    def mass_create_certificate_course(self,mapping):
[2320]1664        if getattr(self,'_v_courses',None) is None:
1665            res = self.courses_catalog()
[2351]1666            self._v_courses= [course.code for course in res]
[2282]1667        if getattr(self,'_v_ceritficates',None) is None:
1668            res = self.portal_catalog(portal_type = "Certificate")
1669            self._v_certificates = {}
1670            for cert in res:
1671                self._v_certificates[cert.getId] = cert.getObject()
[2284]1672        certificate_course_id = mapping.get('code')
[2351]1673        if certificate_course_id not in self._v_courses:
[2284]1674            return '', "No Course with ID: %s" % certificate_course_id
[2282]1675        cert_id = mapping['certificate_code']
1676        cert = self._v_certificates.get(cert_id,None)
1677        if cert is None:
[2284]1678            return '', "No Certificate with ID: %s" % cert_id
[2282]1679        level_id = mapping.get('level')
1680        level = getattr(cert,level_id,None)
1681        if level is None:
1682            cert.invokeFactory('StudyLevel', level_id)
1683            level = getattr(cert,level_id,None)
1684        elif hasattr(level,certificate_course_id):
1685            return '', "Duplicate CertificateCourse ID: %s in %s/ %s" %\
1686            (certificate_course_id,cert_id,level_id)
1687        level.invokeFactory('CertificateCourse', certificate_course_id)
1688        c = getattr(level,certificate_course_id)
1689        c.getContent().edit(mapping=mapping)
1690        return certificate_course_id,''
1691    ###)
1692
[2193]1693    field2types_student = {   ###(
[1756]1694                      'StudentApplication':
1695                          {'id': 'application',
1696                           'title': 'Application Data',
[1991]1697                           'wf_transition_return': 'close',
1698                           'wf_transition_admit': 'remain',
[1756]1699                           'fields':
1700                             ('jamb_reg_no',
1701                              'entry_mode',
1702                              'entry_session',
1703                              'jamb_score',
[1786]1704                              'app_email',
[2008]1705                              'jamb_age',
[2428]1706                              'jamb_state',
1707                              'jamb_lga',
[3003]1708                              'jamb_sex',
[1756]1709                              )
1710                              },
[1786]1711                      #'StudentPume':
1712                      #    {'id': 'pume',
1713                      #     'title': 'Pume Data',
[1991]1714                      #     'wf_transition_return': 'close',
1715                      #     'wf_transition_admit': 'close',
[1786]1716                      #     'fields':
1717                      #       ('pume_score',
1718                      #        )
1719                      #        },
[1756]1720                      'StudentClearance':
1721                          {'id': 'clearance',
[2009]1722                           'title': 'Clearance/Eligibility Record',
[1991]1723                           'wf_transition_return': 'close',
1724                           'wf_transition_admit': 'remain',
[1756]1725                           'fields':
1726                             ('matric_no',
1727                              'nationality',
1728                              'lga',
[1804]1729                              'birthday',
[1756]1730                              )
1731                              },
1732                         'StudentPersonal':
1733                          {'id': 'personal',
1734                           'title': 'Personal Data',
[1991]1735                           'wf_transition_return': 'open',
1736                           'wf_transition_admit': 'remain',
[1756]1737                           'fields':
1738                             ('firstname',
1739                              'middlename',
1740                              'lastname',
1741                              'sex',
1742                              'email',
1743                              'phone',
1744                              'perm_address',
1745                              )
1746                              },
1747                         'StudentStudyCourse':
1748                          {'id': 'study_course',
1749                           'title': 'Study Course',
[1991]1750                           'wf_transition_return': 'open',
1751                           'wf_transition_admit': 'remain',
[1756]1752                           'fields':
1753                             ('study_course',
1754                              'current_level',
1755                              'current_session',
1756                              'current_mode',
1757                              'current_verdict',
[2478]1758                              'previous_verdict',
[1756]1759                              )
1760                              },
[2846]1761                         # 'StudentStudyLevel':
1762                         #  {'id': 'current_level',
1763                         #   'title': '',
1764                         #   'wf_transition_return': 'open',
1765                         #   'wf_transition_admit': 'remain',
1766                         #   'fields':
1767                         #     ('verdict',
1768                         #      'session',
1769                         #      )
1770                         #      },
[1898]1771                         'PaymentsFolder':
1772                          {'id': 'payments',
1773                           'title': 'Payments',
[1991]1774                           'wf_transition_return': 'open',
1775                           'wf_transition_admit': 'open',
[1898]1776                           'fields':
1777                             ()
1778                              },
[1756]1779                         }
1780    ###)
1781
[2193]1782    security.declareProtected(ModifyPortalContent,'mass_create_student') ###(
1783    def mass_create_student(self,mapping):
[3039]1784        "create student records due import"
[2193]1785        logger = logging.getLogger('WAeUPTool.mass_create_student')
[1747]1786        students_folder = self.portal_url.getPortalObject().campus.students
[1818]1787        jamb_reg_no = mapping.get('jamb_reg_no',None)
[1747]1788        if jamb_reg_no:
1789            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
1790            if res:
[1749]1791                return '',"jamb_reg_no exists"
[1818]1792        matric_no = mapping.get('matric_no',None)
[1747]1793        if matric_no:
1794            res = self.students_catalog(matric_no = matric_no)
1795            if res:
[1749]1796                return '',"matric_no exists"
[1747]1797        sid = self.waeup_tool.generateStudentId('?')
1798        students_folder.invokeFactory('Student', sid)
1799        student_obj = getattr(students_folder,sid)
[2193]1800        f2t = self.field2types_student
[1772]1801        d = {}
[1990]1802        transition = mapping.get('reg_transition','admit')
1803        if transition not in ('admit','return'):
[2008]1804            return '',"no valid transition provided"
[1747]1805        for pt in f2t.keys():
1806            student_obj.invokeFactory(pt,f2t[pt]['id'])
1807            sub_obj = getattr(student_obj,f2t[pt]['id'])
1808            sub_doc = sub_obj.getContent()
[1772]1809            d['Title'] = f2t[pt]['title']
[1747]1810            for field in f2t[pt]['fields']:
[2702]1811                d[field] = mapping.get(field,'')
[2454]1812
1813            if pt == "StudentApplication":
[3002]1814                #d['jamb_sex']  = 'M'
1815                #if mapping.get('sex'):
1816                #    d['jamb_sex']  = 'F'
[2436]1817                d['jamb_firstname'] = mapping.get('firstname',None)
1818                d['jamb_middlename'] = mapping.get('middlename',None)
1819                d['jamb_lastname'] = mapping.get('lastname',None)
1820
[2433]1821            # if pt == "StudyCourse":
1822            #     for von,zu in (('entry_mode','current_mode'),
1823            #                    ('entry_session','current_session')):
1824            #         if mapping.get(zu,None) is None and mapping.get(von,None) is not None:
1825            #             d[zu] = mapping[von]
[1747]1826            sub_doc.edit(mapping = d)
[1990]1827
[1987]1828            #import pdb;pdb.set_trace()
1829            new_state = f2t[pt]['wf_transition_%(transition)s' % vars()]
[1898]1830            if new_state != "remain":
1831                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
[1990]1832        self.portal_workflow.doActionFor(student_obj,transition)
[1747]1833        student_obj.manage_setLocalRoles(sid, ['Owner',])
1834        return sid,''
1835    ###)
1836
[2193]1837    security.declareProtected(ModifyPortalContent,'mass_edit_student') ###(
1838    def mass_edit_student(self,mapping):
[2754]1839        wftool = self.portal_workflow
[3039]1840        "edit student records due import"
[2193]1841        logger = logging.getLogger('WAeUPTool.mass_edit_student')
[1774]1842        students_folder = self.portal_url.getPortalObject().campus.students
[1818]1843        sid = mapping.get('id',None)
1844        jamb_reg_no = mapping.get('jamb_reg_no',None)
1845        matric_no = mapping.get('matric_no',None)
[1875]1846        editable_keys = mapping.keys()
[1774]1847        if sid:
1848            res = self.students_catalog(id = sid)
1849            if not res:
1850                return '',"no student with id %s" % sid
[2268]1851            if matric_no and res[0].matric_no and\
[1774]1852              matric_no != res[0].matric_no:
[2268]1853                logger.info("%s, old matric_no %s overwritten with %s" % (res[0].id,res[0].matric_no,matric_no))
1854            if jamb_reg_no and res[0].jamb_reg_no and\
[1774]1855              jamb_reg_no != res[0].jamb_reg_no:
[2268]1856                logger.info("%s, old reg_no %s overwritten with %s" % (res[0].id,res[0].jamb_reg_no,jamb_reg_no))
[1844]1857        elif jamb_reg_no:
1858            res = self.students_catalog(jamb_reg_no = jamb_reg_no)
1859            if not res:
1860                return '',"no student with jamb_reg_no %s" % jamb_reg_no
[1875]1861            editable_keys.remove('jamb_reg_no')
[1774]1862        elif matric_no:
1863            res = self.students_catalog(matric_no = matric_no)
1864            if not res:
1865                return '',"no student with matric_no %s" % matric_no
[1875]1866            editable_keys.remove('matric_no')
[1965]1867
[2268]1868        ## included only to change wf state from admitted to returning
1869        #if res[0].review_state not in ('admitted','objection_raised'):
1870        #    return '%s' % res[0].id ,"student is not in state admitted or objection_raised"
1871        ## end inclusion
[1965]1872
[1774]1873        sid = res[0].id
1874        student_obj = getattr(students_folder,sid)
[2193]1875        f2t = self.field2types_student
[1774]1876        d = {}
[2265]1877        #import pdb;pdb.set_trace()
[1875]1878        any_change = False
[2846]1879        #special treatment for StudentStudyLevel
1880        d['verdict']  = mapping.get('current_verdict','')
1881        d['session']  = mapping.get('current_session','')
1882        current_level = mapping.get('current_level','')
1883        while d['session'] and d['verdict'] and current_level:
1884            sub_obj = getattr(student_obj,'study_course',None)
1885            if sub_obj is None:
1886                break
1887            level_obj = getattr(sub_obj,current_level,None)
1888            if  level_obj is None:
1889                break
1890            any_change = True
1891            level_obj.getContent().edit(mapping = d)
1892            try:
1893                wftool.doActionFor(level_obj,'close')
1894            except:
1895                pass
1896            break
[1774]1897        for pt in f2t.keys():
[3002]1898            #if pt == "StudentApplication":
1899            #    d['jamb_sex']  = 'M'
1900            #    if mapping.get('sex'):
1901            #        d['jamb_sex']  = 'F'
[1875]1902            intersect = set(f2t[pt]['fields']).intersection(set(editable_keys))
[2753]1903            if intersect and pt not in ('StudentStudyLevel',):
1904                object_id = f2t[pt]['id']
1905                sub_obj = getattr(student_obj,object_id,None)
[1875]1906                if sub_obj is None:
1907                    try:
[2753]1908                        student_obj.invokeFactory(pt,object_id)
[1875]1909                    except:
1910                        continue
[2753]1911                    sub_obj = getattr(student_obj,object_id)
1912                    if f2t[pt]['title'] != '':
1913                        d['Title'] = f2t[pt]['title']
[1875]1914                sub_doc = sub_obj.getContent()
1915                for field in intersect:
1916                    changed = False
[2478]1917                    if getattr(sub_doc,field,None) != mapping.get(field,''):
[1875]1918                        any_change = True
1919                        changed = True
1920                        d[field] = mapping.get(field,'')
1921                    if changed:
1922                        sub_doc.edit(mapping = d)
[1904]1923
[1905]1924
[2268]1925        ## included only to change wf state from admitted to returning
1926        #    if res[0].review_state in ('admitted','objection_raised'):
1927        #        new_state = f2t[pt]['wf_transition_return']
1928        #        sub_obj = getattr(student_obj,f2t[pt]['id'],None)
1929        #        if sub_obj and new_state != "remain":
1930        #            try:
1931        #                self.portal_workflow.doActionFor(sub_obj,new_state,dest_container=sub_obj)
1932        #            except:
1933        #                #logger.info('%s, wf transition %s of %s failed' % (sid,new_state,sub_obj.id))
1934        #                pass
1935        #if res[0].review_state in ('admitted','objection_raised'):
1936        #    wfaction = 'return'
1937        #    try:
1938        #        self.portal_workflow.doActionFor(student_obj,wfaction)
1939        #        logger.info('%s, wf state changed' % sid)
1940        #        any_change = True
1941        #    except:
1942        #        logger.info('%s, wf transition failed, old state = %s' % (sid,res[0].review_state))
1943        #        pass
1944        ## end inclusion
[1904]1945
[1905]1946
[1875]1947        if any_change:
1948            return sid,''
1949        else:
1950            return sid,'not modified'
[1774]1951    ###)
[1905]1952
[3033]1953    security.declareProtected(ModifyPortalContent,'mass_edit_verdict') ###(
1954    def mass_edit_verdict(self,mapping):
1955        wftool = self.portal_workflow
[3039]1956        "edit student verdicts"
[3033]1957        logger = logging.getLogger('WAeUPTool.mass_edit_verdict')
1958        students_folder = self.portal_url.getPortalObject().campus.students
1959        student_id = mapping.get('id',None)
1960        matric_no = mapping.get('matric_no',None)
1961        editable_keys = mapping.keys()
1962        if student_id:
[3094]1963            student_record = self.students_catalog.getRecordByKey(student_id)
[3033]1964            if student_record is None:
1965                return '',"no student with id %s" % student_id
1966            if matric_no and student_record.matric_no and\
1967              matric_no != student_record.matric_no:
[3053]1968                return '', 'student %s: matric_no %s does not match %s' % (student_record.id,
[3036]1969                                                                          student_record.matric_no,
1970                                                                          matric_no)
[3143]1971            mapping['matric_no'] = student_record.matric_no
[3033]1972        elif matric_no:
1973            res = self.students_catalog(matric_no = matric_no)
1974            if not res:
1975                return '',"no student with matric_no %s" % matric_no
1976            student_record = res[0]
1977            editable_keys.remove('matric_no')
[3095]1978        else:
1979            return '',"no id or matric_no specified"
[3033]1980        student_id = student_record.id
1981        d = {}
1982        #import pdb;pdb.set_trace()
1983        any_change = False
1984        #special treatment for StudentStudyLevel
[3064]1985        current_session = d['session'] = mapping.get('current_session','')
[3033]1986        if current_session and student_record.session != current_session:
[3143]1987            msg = 'student %s: imported session %s does not match current_session %s' % (student_id,
[3033]1988                                                                                        current_session,
1989                                                                                        student_record.session)
[3143]1990            return student_id,msg,mapping
[3033]1991        current_level = mapping.get('current_level','')
[3133]1992        if not current_level.isdigit():
1993            return '','student %s: imported level is empty' % (student_id,)
[3033]1994        if current_level and student_record.level != current_level:
[3143]1995            msg = 'student %s: imported level %s does not match current_level %s' % (student_id,
[3033]1996                                                                                    current_level,
1997                                                                                    student_record.level)
[3143]1998            return student_id,msg,mapping
[3133]1999        student_review_state =  student_record.review_state
[3135]2000        if student_review_state == 'deactivated':
2001            return '',"student %s in review_state %s" % (student_id, student_review_state)
[3133]2002        if student_review_state not in ('courses_validated','returning'):
[3143]2003            msg = "student %s in wrong review_state %s" % (student_id, student_review_state)
2004            return student_id,msg,mapping
[3135]2005        student_obj = getattr(students_folder,student_id)
2006        f2t = self.field2types_student
[3036]2007        study_course_obj = getattr(student_obj,'study_course',None)
2008        if study_course_obj is None:
[3053]2009            return '','student %s: no study_course object' % student_id
[3036]2010        level_obj = getattr(study_course_obj,current_level,None)
[3033]2011        if  level_obj is None:
[3053]2012            return '','student %s: no study_level object for level %s' % (student_id,
[3033]2013                                                                             current_level)
[3036]2014        verdict = d['verdict'] = d['current_verdict']  = mapping.get('current_verdict','')
[3071]2015
[3064]2016        #if verdict == student_record.verdict:
2017        #    return '','student %s: verdict already set to %s' % (student_id,
2018        #                                                            verdict)
[3039]2019
[3033]2020        level_review_state = wftool.getInfoFor(level_obj,'review_state',None)
2021        if level_review_state != "closed":
[3063]2022            wftool.doActionFor(level_obj,'close')
2023            # return '','student %s: level %s is not closed' % (student_id,
2024            #                                                      current_level)
[3039]2025
[3036]2026        study_course_obj.getContent().edit(mapping = d)
[3033]2027        level_obj.getContent().edit(mapping = d)
[3036]2028        if student_review_state != "returning":
2029            wftool.doActionFor(student_obj,'return')
[3063]2030        # try:
2031        #     wftool.doActionFor(level_obj,'close')
2032        # except:
2033        #     pass
[3033]2034        return student_id,''
2035    ###)
2036
[1730]2037    security.declareProtected(ModifyPortalContent,"importData")###(
[2259]2038    def importData(self,filename,name,edit=False,bypass_queue_catalog=False):
[1730]2039        """load data from CSV values"""
2040        import transaction
2041        import random
[3133]2042        member = self.portal_membership.getAuthenticatedMember()
2043        logger = logging.getLogger('WAeUPTool.importData')
2044        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
[3143]2045        import_date = DateTime.DateTime().strftime("%d/%m/%y %H:%M:%S")
[3133]2046        students_folder = self.portal_url.getPortalObject().campus.students
2047        start = True
2048        tr_count = 0
2049        total_imported = 0
2050        total_not_imported = 0
2051        total = 0
2052        pending_only = False
2053        pend_str = '_pending'
2054        if name.endswith(pend_str):
2055            pending_only = True
2056            name = name[:-len(pend_str)]
2057        iname = "import_%s" % name
2058        if name in ('application','course_result',):
2059            commit_after = 2000
2060        else:
2061            commit_after = 100
2062        stool = getToolByName(self, 'portal_schemas')
2063        ltool = getToolByName(self, 'portal_layouts')
2064        schema = stool._getOb(iname)
2065        if schema is None:
2066            em = 'No such schema %s' % iname
2067            logger.error('No such schema %s' % iname)
2068            return em
2069        layout = ltool._getOb(iname)
2070        if layout is None:
2071            em = 'No such layout %s' % iname
2072            logger.error(em)
2073            return em
2074        validators = {}
2075        for widget in layout.keys():
2076            validators[widget] = layout[widget].validate
2077        mode = "create"
2078        if edit:
2079            if filename.endswith('_toDelete'):
2080                mode = "delete"
2081            else:
2082                mode = "edit"
2083        importer_name = "mass_%(mode)s_%(name)s" % vars()
2084        importer = getattr(self, '%s' % importer_name,None)
2085        if importer is None:
2086            em = 'No importer function %s' % importer_name
2087            logger.error(em)
2088            return em
[3139]2089        pending_fn = "%s/import/%ss_pending.csv" % (i_home,name)
[3143]2090        pending_imported_fn = "%s/import/%ss_pending_imported%s.csv" % (i_home,name,current)
[3133]2091        if pending_only:
2092            import_source_fn = pending_fn
[3139]2093            imported_fn = "%s/import/%ss_pending_imported%s.csv" % (i_home,name,current)
2094            not_imported_fn = "%s/import/%ss_pending_not_imported%s.csv" % (i_home,name,current)
[3140]2095            if not os.path.exists(pending_fn):
2096                em = 'No  %s' % os.path.split(pending_fn)
2097                return em
[3133]2098        else:
2099            import_source_fn = "%s/import/%s.csv" % (i_home,filename)
2100            imported_fn = "%s/import/%s_imported%s.csv" % (i_home,filename,current)
2101            not_imported_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
[3162]2102        if not os.path.exists(import_source_fn):
2103            em = 'No  %s' % os.path.split(import_soure_fn)
2104            return em
2105        attrs = csv.reader(open(import_source_fn,"rb")).next()
[3139]2106        import_keys = [k.strip() for k in attrs if not (k.strip().startswith('ignore')
[3133]2107                                                        or k.strip() == 'Error')]
2108        diff2schema = set(import_keys).difference(set(schema.keys()))
2109        diff2layout = set(import_keys).difference(set(layout.keys()))
2110        if diff2schema and diff2schema != set(['id',]):
2111            msg = 'not ignorable key(s): "%s" found in heading' % ", ".join(diff2schema)
2112            return msg
[3143]2113        if mode in ("create","edit"):
[3133]2114            required_keys = [layout.getIdUnprefixed(id)
2115                                for id,widget in layout.objectItems()
2116                                if widget.is_required]
2117            if not set(required_keys).issubset(set(import_keys)):
2118                diff2import = set(required_keys).difference(set(import_keys))
2119                msg = 'required key(s): "%s" not found in heading' % ", ".join(diff2import)
2120                return msg
2121        #
2122        # not_imported
2123        #
2124        not_imported_keys = import_keys + ["Error",]
[3155]2125        not_imported_file = open(not_imported_fn,"w")
[3133]2126        not_imported_csv_writer = csv.DictWriter(not_imported_file,
2127                                                    not_imported_keys,
2128                                                    extrasaction='ignore')
2129        mapping = dict((k,k) for k in not_imported_keys)
2130        not_imported_csv_writer.writerow(mapping)
2131        not_imported = []
2132        #
2133        # imported
2134        #
2135        imported_keys = import_keys[:]
2136        if 'id' not in imported_keys:
2137            imported_keys.insert(0,"id")
[3155]2138        imported_file = open(imported_fn,"w")
[3133]2139        imported_csv_writer = csv.DictWriter(imported_file,
2140                                             imported_keys,
2141                                             extrasaction='ignore')
2142        mapping = dict((k,k) for k in imported_keys)
2143        imported_csv_writer.writerow(mapping)
2144        imported = []
2145        #
2146        # pending
2147        #
2148        pending_records = {}
2149        imports_pending = 0
[3143]2150        total_pending_imported = 0
[3133]2151        total_pending = 0
2152        format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
2153        format_error = format + ',"%(Error)s"'
2154        #format = '"%(id)s",'+ format
2155        imported = []
2156        if name in ('verdict','course_result',):
[3143]2157            #pending_keys = imported_keys[:]
2158            sname = "%s_pending" % iname
2159            pending_schema = stool._getOb(sname)
2160            if schema is None:
2161                em = 'No such pending_schema %s' % sname
2162                logger.error('No such schema %s' % sname)
2163                return em
2164            pending_keys = pending_schema.keys()
[3133]2165            pending_keys += "Error",
[3143]2166            if 'id' not in pending_keys:
2167                pending_keys.insert(0,'id')
[3133]2168            pending_records = {}
2169            if os.path.exists(pending_fn):
2170                pending_imports_file = open(pending_fn)
2171                pending_imports = csv.DictReader(pending_imports_file,
2172                                                 fieldnames=pending_keys)
2173                if pending_imports:
2174                    pending_records = dict((row['id'],row) for row in pending_imports
2175                                          if row['id'] != 'id')
2176                pending_imports_file.close()
2177            new_imports_pending = 0
2178            pending_imported = []
[3143]2179            if pending_only:
2180                pending_imported_ids = []
2181                pending_imported = []
[3155]2182                for record in pending_records.values():
2183                    item = {}
2184                    for k,v in record.items():
2185                        if v:
2186                            item[k] = v
[3143]2187                    results = importer(item)
2188                    id = results[0]
2189                    error = results[1]
2190                    is_pending = len(results) == 3
2191                    if is_pending:
2192                        continue
[3155]2193                    del item['Error']
2194                    msg = ";".join(["%s : %s" % (k,v) for k,v in item.items()])
[3133]2195                    logger.info("imported from %(pending_fn)s %(msg)s" % vars())
[3143]2196                    pending_imported.append(item)
2197                    pending_imported_ids += id,
2198                    total_pending_imported = len(pending_imported_ids)
[3155]2199                    logger.info("imported %d pending imports" % len(pending_imported_ids))
[3143]2200                for id in pending_imported_ids:
[3161]2201                    if id:
2202                        del pending_records[id]
2203                    else:
2204                        logger.info("tried to delete record with empty id")
[3133]2205            pending_imports_file = open(pending_fn,'w')
2206            pending_csv_writer = csv.DictWriter(pending_imports_file,
2207                                                pending_keys,
2208                                                extrasaction='ignore')
2209            mapping = dict((k,k) for k in pending_keys)
2210            pending_csv_writer.writerow(mapping)
[3143]2211            if len(pending_imported) > 0:
2212                pending_imported_file = open(pending_imported_fn,'w')
2213                pending_imported_csv_writer = csv.DictWriter(pending_imported_file,
2214                                                             pending_keys,
2215                                                             extrasaction='ignore')
2216                pending_imported_csv_writer.writerow(dict((k,k) for k in imported_keys))
2217                pending_imported_csv_writer.writerows(pending_imported)
[3133]2218        #
2219        if pending_only:
2220            items = []
2221        else:
2222            try:
2223                items = csv.DictReader(open(import_source_fn,"rb"))
2224            except:
2225                msg = 'Error reading %s.csv' % filename
2226                logger.error(msg)
2227                return msg
2228            not_imported = []
[3147]2229        pending_keys = pending_records.keys()[:]
[3133]2230        for item in items:
2231            item = dict((k.strip(),l.strip()) for (k,l) in item.items())
2232            if start:
2233                start = False
2234                adapters = [MappingStorageAdapter(schema, item)]
2235                logger.info('%s starts import from %s.csv in %s mode with schema and layout %s' % (member,filename,mode,iname))
2236            dm = DataModel(item, adapters,context=self)
2237            ds = DataStructure(data=item,datamodel=dm)
2238            error_string = ""
2239            total += 1
2240            error_count = 0
2241            for k in import_keys:
2242                if not validators[k](ds,mode=mode):
2243                    if error_count:
2244                        error_string += ' ++ '
2245                    error_string += "%s: %s" % (k,self.translation_service(ds.getError(k),
2246                                                                           ds.getErrorMapping(k)))
2247                    error_count += 1
2248            if error_string:
2249                item['Error'] = error_string
2250                not_imported.append(item)
2251                total_not_imported += 1
2252                continue
2253            temp_item = item.copy()
2254            temp_item.update(dm)
[3143]2255            #id,error = importer(temp_item)
2256            results = importer(temp_item)
2257            id = results[0]
2258            error = results[1]
2259            is_pending = len(results) == 3
2260            if is_pending:
2261                temp_item = results[2]
2262                temp_item['Error'] = error
2263                msg = format_error % temp_item
[3147]2264                #if id not in pending_records.keys():
2265                if id not in pending_keys:
[3143]2266                    temp_item['id'] = id
2267                    pending_records[id] = temp_item
2268                    logger.info("%(id)s added to pending %(msg)s" % vars())
[3147]2269                    pending_keys += id,
[3143]2270                    new_imports_pending += 1
2271                else:
2272                    logger.info("%(id)s already in pending %(msg)s" % vars())
2273                continue
2274            elif error:
[3133]2275                item['Error'] = error
[3143]2276                not_imported.append(item)
2277                total_not_imported += 1
2278                continue
[3133]2279            item = temp_item
2280            item['id'] = id
2281            imported.append(item)
2282            tr_count += 1
2283            total_imported += 1
2284            msg = format % item
2285            logger.info("%(total_imported)d of %(total)d imported in %(mode)s mode, %(msg)s" % vars())
2286            if total and not total % commit_after:
2287                transaction.commit()
2288                if len(imported) > 0:
2289                    imported_csv_writer.writerows(imported)
2290                    imported = []
2291                if len(not_imported) > 0:
2292                    not_imported_csv_writer.writerows(not_imported)
2293                    not_imported = []
2294                if len(pending_records) > 0:
2295                    pending_csv_writer.writerows(pending_records.values())
2296                    total_pending += len(pending_records)
2297                    pending_records = {}
2298                msg = '%d transactions committed\n' % (tr_count)
2299                regs = []
2300                logger.info(msg)
2301                tr_count = 0
2302        if len(imported) > 0:
2303            imported_csv_writer.writerows(imported)
2304        if len(not_imported) > 0:
2305            not_imported_csv_writer.writerows(not_imported)
2306        if len(pending_records) > 0:
2307            pending_csv_writer.writerows(pending_records.values())
2308            total_pending += len(pending_records)
[3140]2309        import_source_fn = os.path.split(import_source_fn)[1]
[3141]2310        pending_fn = os.path.split(pending_fn)[1]
[3133]2311        msg = "Finished importing in %(mode)s mode from %(import_source_fn)s: " % vars()
[3160]2312        msg += "%(total_imported)d imported, %(total_not_imported)d not imported, " % vars()
[3133]2313        if total_pending:
[3145]2314            if pending_only:
2315                msg += "%(new_imports_pending)d pending added, %(total_pending_imported)d pending imported " % vars()
2316            else:
2317                msg += "%(new_imports_pending)d pending added " % vars()
[3160]2318            msg += "(of total %(total)d), " % vars()
[3143]2319            msg += "%(total_pending)d total pending in %(pending_fn)s " % vars()
[3133]2320        logger.info(msg)
2321        return msg
2322    ###)
[1775]2323
[3133]2324    def old_importData(self,filename,name,edit=False,bypass_queue_catalog=False):###(
2325        """load data from CSV values"""
2326        import transaction
2327        import random
2328
[1773]2329        pm = self.portal_membership
[1775]2330        member = pm.getAuthenticatedMember()
2331
[1747]2332        logger = logging.getLogger('WAeUPTool.importData')
[1730]2333        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
2334        students_folder = self.portal_url.getPortalObject().campus.students
2335        start = True
[2495]2336        tr_count = 0
[1756]2337        total_imported = 0
2338        total_not_imported = 0
[1730]2339        total = 0
[1749]2340        iname = "import_%s" % name
[2754]2341        if iname in ('import_application','import_course_result',):
[2528]2342            commit_after = 2000
2343        else:
2344            commit_after = 100
[1747]2345        stool = getToolByName(self, 'portal_schemas')
2346        ltool = getToolByName(self, 'portal_layouts')
2347        schema = stool._getOb(iname)
[1730]2348        if schema is None:
[1772]2349            em = 'No such schema %s' % iname
2350            logger.error('No such schema %s' % iname)
[1756]2351            return em
[1747]2352        layout = ltool._getOb(iname)
[1730]2353        if layout is None:
[1772]2354            em = 'No such layout %s' % iname
[1756]2355            logger.error(em)
2356            return em
[1730]2357        validators = {}
2358        for widget in layout.keys():
[1749]2359            validators[widget] = layout[widget].validate
[2289]2360        mode = "create"
[1774]2361        if edit:
[3057]2362            if filename.endswith('_toDelete'):
2363                mode = "delete"
2364            else:
2365                mode = "edit"
[2289]2366        importer_name = "mass_%(mode)s_%(name)s" % vars()
[1772]2367        importer = getattr(self, '%s' % importer_name,None)
2368        if importer is None:
2369            em = 'No importer function %s' % importer_name
[1756]2370            logger.error(em)
2371            return em
[1747]2372        not_imported = []
[1730]2373        imported = []
2374        try:
[1747]2375            items = csv.DictReader(open("%s/import/%s.csv" % (i_home,filename),"rb"))
[1730]2376        except:
[1756]2377            em = 'Error reading %s.csv' % filename
2378            logger.error(em)
2379            return em
[1730]2380        for item in items:
[2704]2381            #import pdb;pdb.set_trace()
[3094]2382            item = dict((k.strip(),l.strip()) for (k,l) in item.items())
[1730]2383            if start:
2384                start = False
[1747]2385                adapters = [MappingStorageAdapter(schema, item)]
[2315]2386                logger.info('%s starts import from %s.csv in %s mode with schema and layout %s' % (member,filename,mode,iname))
[2094]2387                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb")).next()
[3033]2388                import_keys = [k.strip() for k in attrs if not k.strip().startswith('ignore')]
[1804]2389                diff2schema = set(import_keys).difference(set(schema.keys()))
2390                diff2layout = set(import_keys).difference(set(layout.keys()))
2391                if diff2schema:
[2282]2392                    em = 'not ignorable key(s): "%s" found in heading' % ", ".join(diff2schema)
[1804]2393                    return em
[2433]2394                if mode == "create":
[2436]2395                    required_keys = [layout.getIdUnprefixed(id)
[2433]2396                                     for id,widget in layout.objectItems()
2397                                     if widget.is_required]
2398                    if not set(required_keys).issubset(set(import_keys)):
2399                        diff2import = set(required_keys).difference(set(import_keys))
2400                        em = 'required key(s): "%s" not found in heading' % ", ".join(diff2import)
2401                        return em
[1753]2402                s = ','.join(['"%s"' % fn for fn in import_keys])
[1772]2403                open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(s + ',"Error"'+ '\n')
2404                s = '"id",' + s
[1756]2405                open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(s + '\n')
[1730]2406                format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
2407                format_error = format + ',"%(Error)s"'
[1756]2408                format = '"%(id)s",'+ format
[2267]2409
[2094]2410            dm = DataModel(item, adapters,context=self)
[1747]2411            ds = DataStructure(data=item,datamodel=dm)
2412            error_string = ""
[2495]2413            total += 1
[2704]2414            error_count = 0
[1730]2415            for k in import_keys:
[2289]2416                if not validators[k](ds,mode=mode):
[2704]2417                    if error_count:
[2710]2418                        error_string += ' ++ '
[2704]2419                    error_string += "%s: %s" % (k,
[2264]2420                                                  self.translation_service(ds.getError(k),
2421                                                                           ds.getErrorMapping(k)))
[2704]2422                    error_count += 1
[1747]2423            if not error_string:
[2433]2424                temp_item = item.copy()
2425                temp_item.update(dm)
[2435]2426                temp_item['id'],error = importer(temp_item)
[1747]2427                if error:
2428                    error_string += error
[2433]2429                else:
2430                    item = temp_item
[2436]2431            if error_string:
[1747]2432                item['Error'] = error_string
2433                not_imported.append(format_error % item)
[1756]2434                total_not_imported += 1
[1747]2435            else:
[1821]2436                em = format % item
2437                imported.append(em)
[1875]2438                tr_count += 1
[1756]2439                total_imported += 1
[3071]2440                logger.info("%(total_imported)d of %(total)d imported in %(mode)s mode, %(em)s" % vars())
[2520]2441
[2528]2442            if total and not total % commit_after:
[1875]2443                transaction.commit()
[1747]2444                if len(not_imported) > 0:
2445                    open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
2446                             '\n'.join(not_imported) + '\n')
2447                    not_imported = []
2448                if len(imported) > 0:
2449                    open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
[1772]2450                             '\n'.join(imported) + '\n')
[1747]2451                    imported = []
[1730]2452                em = '%d transactions committed\n' % (tr_count)
2453                regs = []
2454                logger.info(em)
2455                tr_count = 0
[2528]2456                #if total > 100:
2457                #    return
[1756]2458        if len(imported) > 0:
[1747]2459            open("%s/import/%s_imported%s.csv" % (i_home,filename,current),"a").write(
2460                                                '\n'.join(imported))
[1756]2461        if len(not_imported) > 0:
[1747]2462            open("%s/import/%s_not_imported%s.csv" % (i_home,filename,current),"a").write(
2463                                                '\n'.join(not_imported))
[3071]2464        #if mode.endswith('e'):
2465        #    mode = mode[:-1]
2466        em = "Finished importing in %(mode)s mode from %(filename)s.csv: %(total_imported)d imported, %(total_not_imported)d not imported (of total %(total)d)" % vars()
[2495]2467        logger.info(em)
[1756]2468        return em
[1730]2469    ###)
2470
[2292]2471    security.declareProtected(ModifyPortalContent,"moveImagesToFS")###(
2472    def moveImagesToFS(self,student_id="O738726"):
2473        "move the images to the filesystem"
[2675]2474        images_dir = getImagesDir(student_id)
2475        #images_dir = os.path.join("%s" % images_base,student_id)
[2292]2476        student_folder = getattr(self.portal_url.getPortalObject().campus.students,student_id)
2477        stool = getToolByName(self, 'portal_schemas')
2478        schemas = ['student_application',
2479                   'student_clearance',
2480                   ]
2481        created = False
2482        for schema_id in schemas:
2483            schema = stool._getOb(schema_id)
2484            object = getattr(student_folder,schema_id[len('student_'):],None)
2485            if object is None:
2486                continue
2487            doc = object.getContent()
2488            for key in schema.keys():
2489                if schema[key].meta_type != "CPS Image Field":
2490                    continue
2491                #import pdb;pdb.set_trace()
[2351]2492                image = getattr(doc,key,None)
2493                if not image or not hasattr(image,"data"):
[2292]2494                    continue
2495                if not created:
2496                    if not os.path.exists(images_dir):
2497                        os.mkdir(images_dir)
2498                    created = True
2499                filename = os.path.join(images_dir,"%(key)s_%(student_id)s.jpg" % vars())
2500                open(filename,"wb").write(str(image.data))
[2500]2501    ###)
[2292]2502
[2300]2503    security.declareProtected(ModifyPortalContent,"movePassportToFS")###(
2504    def movePassportToFS(self,student_id="O738726"):
2505        "move the passports to the filesystem"
2506        images_dir = os.path.join("%s" % i_home,'passports')
2507        student_folder = getattr(self.portal_url.getPortalObject().campus.students,student_id)
2508        stool = getToolByName(self, 'portal_schemas')
2509        schemas = ['student_application',
2510                   #'student_clearance',
2511                   ]
2512        created = False
2513        for schema_id in schemas:
2514            schema = stool._getOb(schema_id)
2515            object = getattr(student_folder,schema_id[len('student_'):],None)
2516            if object is None:
2517                continue
2518            doc = object.getContent()
2519            for key in schema.keys():
2520                if schema[key].meta_type != "CPS Image Field":
2521                    continue
2522                #import pdb;pdb.set_trace()
2523                image = getattr(doc,key)
2524                if not hasattr(image,"data"):
2525                    continue
2526                if not created:
2527                    if not os.path.exists(images_dir):
2528                        os.mkdir(images_dir)
2529                    created = True
2530                filename = os.path.join(images_dir,"%(student_id)s.jpg" % vars())
2531                open(filename,"wb").write(str(image.data))
[2500]2532    ###)
[2300]2533
[828]2534InitializeClass(WAeUPTool)
Note: See TracBrowser for help on using the repository browser.