source: WAeUP_SRP/trunk/WAeUPTables.py @ 5240

Last change on this file since 5240 was 5202, checked in by Henrik Bettermann, 14 years ago

add metadata firstname, middlename and lastname in students_catalog

  • Property svn:keywords set to Id
File size: 75.3 KB
Line 
1#-*- mode: python; mode: fold -*-
2# (C) Copyright 2005 AixtraWare <http://aixtraware.de>
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: WAeUPTables.py 5202 2010-05-23 04:26:23Z henrik $
20
21from zope.interface import implements
22from Globals import InitializeClass
23from Products.ZCatalog.ZCatalog import ZCatalog
24from Products.ZCatalog.ProgressHandler import ZLogHandler
25from AccessControl import ClassSecurityInfo
26from Products.CMFCore.permissions import ModifyPortalContent
27from Products.CMFCore.utils import getToolByName
28from Products.CMFCore.CatalogTool import CatalogTool
29from Products.CPSSchemas.StorageAdapter import MappingStorageAdapter
30from Products.CPSSchemas.DataStructure import DataStructure
31from Products.CPSSchemas.DataModel import DataModel
32from Products.AdvancedQuery import Eq, Between, Le,In
33import urllib
34import DateTime,time
35import csv,re,os
36import logging
37import Globals
38p_home = Globals.package_home(globals())
39i_home = Globals.INSTANCE_HOME
40
41ADDING_SHEDULED = "adding_sheduled"
42OBJECT_CREATED = "object_created"
43NOT_OCCUPIED = 'not_occupied'
44
45from interfaces import IWAeUPTable
46
47class AttributeHolder(object):
48    pass
49
50def dict2ob(dict):
51    ob = AttributeHolder()
52    for key, value in dict.items():
53        setattr(ob, key, value)
54    return ob
55
56class WAeUPTable(ZCatalog): ###(
57
58    implements(IWAeUPTable)
59    security = ClassSecurityInfo()
60    meta_type = None
61
62    def __init__(self,name=None):
63        if name ==  None:
64            name = self.name
65        ZCatalog.__init__(self,name)
66
67    def refreshCatalog(self, clear=0, pghandler=None): ###(
68        """ don't refresh for a normal table """
69
70        if self.REQUEST and self.REQUEST.RESPONSE:
71            self.REQUEST.RESPONSE.redirect(
72              URL1 +
73              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20refresh%20not%20implemented')
74
75###)
76
77    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None): ###(
78        """ clears the whole enchilada """
79
80        #if REQUEST and RESPONSE:
81        #    RESPONSE.redirect(
82        #      URL1 +
83        #      '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Clearing%20disabled')
84
85        self._catalog.clear()
86        if REQUEST and RESPONSE:
87            RESPONSE.redirect(
88              URL1 +
89              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20cleared')
90
91###)
92
93    def record2dict(self,fields,record,index): ###(
94        d = {}
95        for key in fields:
96            v = getattr(record, key, None)
97            v_dump = v
98            if key == 'sex':
99                if v == True:
100                    v_dump = 'F'
101                elif v == False:
102                    v_dump = 'M'
103                d[key] = v_dump
104            elif v:
105                if index == 'translate':
106                    if key == 'lga':
107                        v_dump = self.portal_vocabularies.local_gov_areas.get(v)
108                        if not v_dump:
109                            v_dump = v
110                    elif key == 'aos':
111                        v_dump = self.portal_vocabularies.aos.get(v)
112                d[key] = v_dump
113            else:
114                d[key] = ''
115        return d
116
117###)
118
119    def addRecord(self, **data): ###(
120        # The uid is the same as "bed".
121        uid = data[self.key]
122        res = self.searchResults({"%s" % self.key : uid})
123        if len(res) > 0:
124            raise ValueError("More than one record with uid %s" % uid)
125        self.catalog_object(dict2ob(data), uid=uid)
126        return uid
127
128###)
129
130    def deleteRecord(self, uid):
131        self.uncatalog_object(uid)
132
133    def getRecordByKey(self,key):
134        if not key:
135            return None
136        res = self.evalAdvancedQuery(Eq(self.key,key))
137        if res:
138            return res[0]
139        return None
140
141    def searchAndSetRecord(self, **data):
142        raise NotImplemented
143
144    def modifyRecord(self, record=None, **data): ###(
145        #records = self.searchResults(uid=uid)
146        uid = data[self.key]
147        if record is None:
148            records = self.searchResults({"%s" % self.key : uid})
149            if len(records) > 1:
150                # Can not happen, but anyway...
151                raise ValueError("More than one record with uid %s" % uid)
152            if len(records) == 0:
153                raise KeyError("No record for uid %s" % uid)
154            record = records[0]
155        record_data = {}
156        for field in self.schema() + self.indexes():
157            record_data[field] = getattr(record, field)
158        # Add the updated data:
159        record_data.update(data)
160        self.catalog_object(dict2ob(record_data), uid)
161
162###)
163
164    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
165        if isinstance(name, str):
166            name =  (name,)
167        paths = self._catalog.uids.items()
168        i = 0
169        #import pdb;pdb.set_trace()
170        for p,rid in paths:
171            i += 1
172            metadata = self.getMetadataForRID(rid)
173            record_data = {}
174            for field in name:
175                record_data[field] = metadata.get(field)
176            uid = metadata.get(self.key)
177            self.catalog_object(dict2ob(record_data), uid, idxs=name,
178                                update_metadata=0)
179
180###)
181
182    security.declareProtected(ModifyPortalContent,"exportAllRecords") ###(
183    def exportAllRecords(self):
184        "export a WAeUPTable"
185        #import pdb;pdb.set_trace()
186        fields = [field for field in self.schema()]
187        format = ','.join(['"%%(%s)s"' % fn for fn in fields])
188        csv = []
189        csv.append(','.join(['"%s"' % fn for fn in fields]))
190        for uid in self._catalog.uids:
191            records = self.searchResults({"%s" % self.key : uid})
192            if len(records) > 1:
193                # Can not happen, but anyway...
194                raise ValueError("More than one record with uid %s" % uid)
195            if len(records) == 0:
196                raise KeyError("No record for uid %s" % uid)
197            rec = records[0]
198            csv.append(format % rec)
199        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
200        open("%s/import/%s-%s.csv" % (i_home,self.getId(),current),"w+").write('\n'.join(csv))
201
202###)
203
204    security.declareProtected(ModifyPortalContent,"dumpAll")###(
205    def dumpAll(self,index=None,value=None):
206        """dump all data in the table to a csv"""
207        member = self.portal_membership.getAuthenticatedMember()
208        logger = logging.getLogger('WAeUPTables.WAeUPTable.dumpAll')
209        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
210        export_file = "%s/export/%s_%s.csv" % (i_home,self.__name__,current,)
211        res_list = []
212        lines = []
213        if hasattr(self,"export_keys"):
214            fields = self.export_keys
215        else:
216            fields = []
217            for f in self.schema():
218                fields.append(f)
219        headline = ','.join(fields)
220        out = open(export_file,"wb")
221        out.write(headline +'\n')
222        out.close()
223        out = open(export_file,"a")
224        csv_writer = csv.DictWriter(out,fields,)
225        if index is not None and value is not None:
226            records = self.evalAdvancedQuery(Eq(index,value))
227        else:
228            records = self()
229        nr2export = len(records)
230        logger.info('%s starts dumping, %s records to export' % (member,nr2export))
231        chunk = 2000
232        total = 0
233        start = DateTime.DateTime().timeTime()
234        start_chunk = DateTime.DateTime().timeTime()
235        for record in records:
236            not_all = False
237            d = self.record2dict(fields,record,index)
238            lines.append(d)
239            total += 1
240            if total and not total % chunk or total == len(records):
241                csv_writer.writerows(lines)
242                anz = len(lines)
243                logger.info("wrote %(anz)d  total written %(total)d" % vars())
244                end_chunk = DateTime.DateTime().timeTime()
245                duration = end_chunk-start_chunk
246                per_record = duration/anz
247                till_now = end_chunk - start
248                avarage_per_record = till_now/total
249                estimated_end = DateTime.DateTime(start + avarage_per_record * nr2export)
250                estimated_end = estimated_end.strftime("%H:%M:%S")
251                logger.info('%(duration)4.1f, %(per_record)4.3f,end %(estimated_end)s' % vars())
252                start_chunk = DateTime.DateTime().timeTime()
253                lines = []
254        end = DateTime.DateTime().timeTime()
255        logger.info('total time %6.2f m' % ((end-start)/60))
256        import os
257        filename, extension = os.path.splitext(export_file)
258        from subprocess import call
259        msg = "wrote %(total)d records to %(export_file)s" % vars()
260        #try:
261        #    retcode = call('gzip %s' % (export_file),shell=True)
262        #    if retcode == 0:
263        #        msg = "wrote %(total)d records to %(export_file)s.gz" % vars()
264        #except OSError, e:
265        #    retcode = -99
266        #    logger.info("zip failed with %s" % e)
267        logger.info(msg)
268        args = {'portal_status_message': msg}
269        #url = self.REQUEST.get('URL1') + '?' + urlencode(args)
270        url = self.REQUEST.get('URL2')
271        return 'ready'
272        #return self.REQUEST.RESPONSE.redirect(url)
273    ###)
274
275    security.declarePrivate("_import_old") ###(
276    def _import_old(self,filename,schema,layout, mode,logger):
277        "import data from csv"
278        import transaction
279        import random
280        pm = self.portal_membership
281        member = pm.getAuthenticatedMember()
282        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
283        import_fn = "%s/import/%s.csv" % (i_home,filename)
284        imported_fn = "%s/import/%s_imported%s.csv" % (i_home,filename,current)
285        not_imported_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
286        start = True
287        tr_count = 1
288        total_imported = 0
289        total_not_imported = 0
290        total = 0
291        iname =  "%s" % filename
292        not_imported = []
293        imported = []
294        valid_records = []
295        invalid_records = []
296        d = {}
297        d['mode'] = mode
298        d['imported'] = total_imported
299        d['not_imported'] = total_not_imported
300        d['valid_records'] = valid_records
301        d['invalid_records'] = invalid_records
302        d['import_fn'] = import_fn
303        d['imported_fn'] = imported_fn
304        d['not_imported_fn'] = not_imported_fn
305        if schema is None:
306            em = 'No schema specified'
307            logger.error(em)
308            return d
309        if layout is None:
310            em = 'No layout specified'
311            logger.error(em)
312            return d
313        validators = {}
314        for widget in layout.keys():
315            try:
316                validators[widget] = layout[widget].validate
317            except AttributeError:
318                logger.info('%s has no validate attribute' % widget)
319                return d
320        # if mode == 'edit':
321        #     importer = self.importEdit
322        # elif mode == 'add':
323        #     importer = self.importAdd
324        # else:
325        #     importer = None
326        try:
327            items = csv.DictReader(open(import_fn,"rb"),
328                                   dialect="excel",
329                                   skipinitialspace=True)
330        except:
331            em = 'Error reading %s.csv' % filename
332            logger.error(em)
333            return d
334        #import pdb;pdb.set_trace()
335        for item in items:
336            if start:
337                start = False
338                logger.info('%s starts import from %s.csv' % (member,filename))
339                #import_keys = [k for k in item.keys() if not k.startswith('ignore')]
340                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb"),
341                                   dialect="excel",
342                                   skipinitialspace=True).next()
343                import_keys = [k for k in attrs if not (k.startswith('ignore') or k.isupper())]
344                diff2schema = set(import_keys).difference(set(schema.keys()))
345                diff2layout = set(import_keys).difference(set(layout.keys()))
346                if diff2layout:
347                    em = "not ignorable key(s) %s found in heading" % diff2layout
348                    logger.info(em)
349                    return d
350                s = ','.join(['"%s"' % fn for fn in import_keys])
351                open(not_imported_fn,"a").write(s + ',"Error"'+ '\n')
352                #s = '"id",' + s
353                open(imported_fn,"a").write(s + '\n')
354                format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
355                format_error = format + ',"%(Error)s"'
356                #format = '"%(id)s",'+ format
357                adapters = [MappingStorageAdapter(schema, item)]
358            dm = DataModel(item, adapters,context=self)
359            ds = DataStructure(data=item,datamodel=dm)
360            error_string = ""
361            #import pdb;pdb.set_trace()
362            for k in import_keys:
363                if not validators[k](ds,mode=mode):
364                    error_string += " %s : %s" % (k,ds.getError(k))
365            # if not error_string and importer:
366            #     item.update(dm)
367            #     item['id'],error = importer(item)
368            #     if error:
369            #         error_string += error
370            if error_string:
371                item['Error'] = error_string
372                invalid_records.append(dm)
373                not_imported.append(format_error % item)
374                total_not_imported += 1
375            else:
376                em = format % item
377                valid_records.append(dm)
378                imported.append(em)
379                #logger.info("%(total_imported)d of %(total)d %(em)s" % vars())
380                tr_count += 1
381                total_imported += 1
382            total += 1
383        if len(imported) > 0:
384            open(imported_fn,"a").write('\n'.join(imported))
385        if len(not_imported) > 0:
386            open(not_imported_fn,"a").write('\n'.join(not_imported))
387        #em = "Imported: %d, not imported: %d of total %d" % (total_imported,total_not_imported,total)
388        d['imported'] = total_imported
389        d['not_imported'] = total_not_imported
390        d['valid_records'] = valid_records
391        d['invalid_records'] = invalid_records
392        d['imported_fn'] = imported_fn
393        d['not_imported_fn'] = not_imported_fn
394        #logger.info(em)
395        return d
396    ###)
397
398    security.declarePrivate("_import") ###(
399    def _import_new(self,csv_items,schema, layout, mode,logger):
400        "import data from csv.Dictreader Instance"
401        start = True
402        tr_count = 1
403        total_imported = 0
404        total_not_imported = 0
405        total = 0
406        iname =  "%s" % filename
407        not_imported = []
408        valid_records = []
409        invalid_records = []
410        duplicate_records = []
411        d = {}
412        d['mode'] = mode
413        d['valid_records'] = valid_records
414        d['invalid_records'] = invalid_records
415        d['invalid_records'] = duplicate_records
416        # d['import_fn'] = import_fn
417        # d['imported_fn'] = imported_fn
418        # d['not_imported_fn'] = not_imported_fn
419        validators = {}
420        for widget in layout.keys():
421            try:
422                validators[widget] = layout[widget].validate
423            except AttributeError:
424                logger.info('%s has no validate attribute' % widget)
425                return d
426        for item in csv_items:
427            if start:
428                start = False
429                logger.info('%s starts import from %s.csv' % (member,filename))
430                #import_keys = [k for k in item.keys() if not k.startswith('ignore')]
431                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb")).next()
432                import_keys = [k for k in attrs if not (k.startswith('ignore') or k.isupper())]
433                diff2schema = set(import_keys).difference(set(schema.keys()))
434                diff2layout = set(import_keys).difference(set(layout.keys()))
435                if diff2layout:
436                    em = "not ignorable key(s) %s found in heading" % diff2layout
437                    logger.info(em)
438                    return d
439                # s = ','.join(['"%s"' % fn for fn in import_keys])
440                # open(not_imported_fn,"a").write(s + ',"Error"'+ '\n')
441                # #s = '"id",' + s
442                # open(imported_fn,"a").write(s + '\n')
443                # format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
444                # format_error = format + ',"%(Error)s"'
445                # #format = '"%(id)s",'+ format
446                adapters = [MappingStorageAdapter(schema, item)]
447            dm = DataModel(item, adapters,context=self)
448            ds = DataStructure(data=item,datamodel=dm)
449            error_string = ""
450            for k in import_keys:
451                if not validators[k](ds,mode=mode):
452                    error_string += " %s : %s" % (k,ds.getError(k))
453            if error_string:
454                item['Error'] = error_string
455                #invalid_records.append(dm)
456                invalid_records.append(item)
457                total_not_imported += 1
458            else:
459                em = format % item
460                valid_records.append(dm)
461                #logger.info("%(total_imported)d of %(total)d %(em)s" % vars())
462                tr_count += 1
463                total_imported += 1
464            total += 1
465        # if len(imported) > 0:
466        #     open(imported_fn,"a").write('\n'.join(imported))
467        # if len(not_imported) > 0:
468        #     open(not_imported_fn,"a").write('\n'.join(not_imported))
469        #em = "Imported: %d, not imported: %d of total %d" % (total_imported,total_not_imported,total)
470        d['imported'] = total_imported
471        d['not_imported'] = total_not_imported
472        d['valid_records'] = valid_records
473        d['invalid_records'] = invalid_records
474        return d
475    ###)
476
477    security.declarePublic("missingValue")###(
478    def missingValue(self):
479        from Missing import MV
480        return MV
481    ###)
482###)
483
484class AccommodationTable(WAeUPTable): ###(
485
486    meta_type = 'WAeUP Accommodation Tool'
487    name = "portal_accommodation"
488    key = "bed"
489    not_occupied = NOT_OCCUPIED
490    def __init__(self,name=None):
491        if name ==  None:
492            name = self.name
493        WAeUPTable.__init__(self, name)
494
495    def searchAndReserveBed(self, student_id,bed_type,random_order=False): ###(
496        logger = logging.getLogger('WAeUPTables.AccommodationTable.searchAndReserveBed')
497        records = self.evalAdvancedQuery(Eq('student',student_id))
498        if len(records) == 1:
499            #return -1,"Student with Id %s already booked bed %s." % (student_id,records[0].bed)
500            logger.info('%s found (reserved) bed %s' % (student_id,records[0].bed))
501            return -1,records[0].bed
502        elif len(records) > 1:
503            logger.info('%s found more than one (reserved) bed' % (student_id))
504            return -3,'more than one bed'
505        query = Eq('bed_type',bed_type) & Eq('student',NOT_OCCUPIED)
506        records = self.evalAdvancedQuery(query,sortSpecs=('sort_id','bed'))
507        if len(records) == 0:
508            logger.info('no bed %s available for %s' % (bed_type,student_id))
509            return -2,"no bed"
510        if random_order:
511            import random
512            bed_no = random.randint(0,len(records)-1)
513        else:
514            bed_no = 0
515        rec = records[bed_no]
516        self.modifyRecord(bed=rec.bed,student=student_id)
517        logger.info('%s booked bed %s' % (student_id,rec.bed))
518        return 1,rec.bed
519    ###)
520
521
522InitializeClass(AccommodationTable)
523
524###)
525
526class PinTable(WAeUPTable): ###(
527    from ZODB.POSException import ConflictError
528    security = ClassSecurityInfo()
529    meta_type = 'WAeUP Pin Tool'
530    name = "portal_pins"
531    key = 'pin'
532
533    def __init__(self,name=None):
534        if name ==  None:
535            name = self.name
536        WAeUPTable.__init__(self, name)
537
538    security.declareProtected(ModifyPortalContent,"dumpAll")###(
539    def dumpAll(self,include_unused=None,index=None):
540        """dump all data in the table to a csv"""
541        member = self.portal_membership.getAuthenticatedMember()
542        logger = logging.getLogger('WAeUPTables.PinTable.dumpAll')
543        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
544        export_file = "%s/export/%s_%s.csv" % (i_home,self.__name__,current,)
545        res_list = []
546        lines = []
547        if hasattr(self,"export_keys"):
548            fields = self.export_keys
549        else:
550            fields = []
551            for f in self.schema():
552                fields.append(f)
553        headline = ','.join(fields)
554        out = open(export_file,"wb")
555        out.write(headline +'\n')
556        out.close()
557        out = open(export_file,"a")
558        csv_writer = csv.DictWriter(out,fields,)
559        if include_unused is not None and str(member) not in ('admin','joachim'):
560            logger.info('%s tries to dump pintable with unused pins' % (member))
561            return
562        if include_unused is not None:
563            records = self()
564        else:
565            records = self.evalAdvancedQuery(~Eq('student',''))
566        nr2export = len(records)
567        logger.info('%s starts dumping, %s records to export' % (member,nr2export))
568        chunk = 2000
569        total = 0
570        start = DateTime.DateTime().timeTime()
571        start_chunk = DateTime.DateTime().timeTime()
572        for record in records:
573            not_all = False
574            d = self.record2dict(fields,record,index)
575            lines.append(d)
576            total += 1
577            if total and not total % chunk or total == len(records):
578                csv_writer.writerows(lines)
579                anz = len(lines)
580                logger.info("wrote %(anz)d  total written %(total)d" % vars())
581                end_chunk = DateTime.DateTime().timeTime()
582                duration = end_chunk-start_chunk
583                per_record = duration/anz
584                till_now = end_chunk - start
585                avarage_per_record = till_now/total
586                estimated_end = DateTime.DateTime(start + avarage_per_record * nr2export)
587                estimated_end = estimated_end.strftime("%H:%M:%S")
588                logger.info('%(duration)4.1f, %(per_record)4.3f,end %(estimated_end)s' % vars())
589                start_chunk = DateTime.DateTime().timeTime()
590                lines = []
591        end = DateTime.DateTime().timeTime()
592        logger.info('total time %6.2f m' % ((end-start)/60))
593        import os
594        filename, extension = os.path.splitext(export_file)
595        from subprocess import call
596        msg = "wrote %(total)d records to %(export_file)s" % vars()
597        #try:
598        #    retcode = call('gzip %s' % (export_file),shell=True)
599        #    if retcode == 0:
600        #        msg = "wrote %(total)d records to %(export_file)s.gz" % vars()
601        #except OSError, e:
602        #    retcode = -99
603        #    logger.info("zip failed with %s" % e)
604        logger.info(msg)
605        args = {'portal_status_message': msg}
606        #url = self.REQUEST.get('URL1') + '?' + urlencode(args)
607        url = self.REQUEST.get('URL2')
608        return self.REQUEST.RESPONSE.redirect(url)
609    ###)
610
611
612
613    def searchAndSetRecord(self, uid, student_id,prefix):
614
615        # The following line must be activated after resetting the
616        # the portal_pins table. This is to avoid duplicate entries
617        # and disable duplicate payments.
618
619        #student_id = student_id.upper()
620
621        #records = self.searchResults(student = student_id)
622        #if len(records) > 0 and prefix in ('CLR','APP'):
623        #    for r in records:
624        #        if r.pin != uid and r.prefix_batch.startswith(prefix):
625        #            return -2
626        records = self.searchResults({"%s" % self.key : uid})
627        if len(records) > 1:
628            # Can not happen, but anyway...
629            raise ValueError("More than one record with uid %s" % uid)
630        if len(records) == 0:
631            return -1,None
632        record = records[0]
633        if record.student == "":
634            record_data = {}
635            for field in self.schema() + self.indexes():
636                record_data[field] = getattr(record, field)
637            # Add the updated data:
638            record_data['student'] = student_id
639            try:
640                self.catalog_object(dict2ob(record_data), uid)
641                return 1,record
642            except ConflictError:
643                return 2,record
644        if record.student.upper() != student_id.upper():
645            return 0,record
646        if record.student.upper() == student_id.upper():
647            return 2,record
648        return -3,record
649InitializeClass(PinTable)
650###)
651
652class PumeResultsTable(WAeUPTable): ###(
653
654    meta_type = 'WAeUP PumeResults Tool'
655    name = "portal_pumeresults"
656    key = "jamb_reg_no"
657    def __init__(self,name=None):
658        if name ==  None:
659            name = self.name
660        WAeUPTable.__init__(self, name)
661
662
663InitializeClass(PumeResultsTable)
664
665###)
666
667class ApplicantsCatalog(WAeUPTable): ###(
668
669    meta_type = 'WAeUP Applicants Catalog'
670    name = "applicants_catalog"
671    key = "reg_no"
672    security = ClassSecurityInfo()
673    #export_keys = (
674    #               "reg_no",
675    #               "status",
676    #               "lastname",
677    #               "sex",
678    #               "date_of_birth",
679    #               "lga",
680    #               "email",
681    #               "phone",
682    #               "passport",
683    #               "entry_mode",
684    #               "pin",
685    #               "screening_type",
686    #               "registration_date",
687    #               "testdate",
688    #               "application_date",
689    #               "screening_date",
690    #               "faculty",
691    #               "department",
692    #               "course1",
693    #               "course2",
694    #               "course3",
695    #               "eng_score",
696    #               "subj1",
697    #               "subj1score",
698    #               "subj2",
699    #               "subj2score",
700    #               "subj3",
701    #               "subj3score",
702    #               "aggregate",
703    #               "course_admitted",
704    #               )
705
706    def __init__(self,name=None):
707        if name ==  None:
708            name = self.name
709        WAeUPTable.__init__(self, name)
710
711    security.declareProtected(ModifyPortalContent,"new_importCSV")###(
712    def new_importCSV(self,filename="JAMB_data",
713                  schema_id="application",
714                  layout_id="import_application",
715                  mode='add'):
716        """ import JAMB data """
717        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
718        pm = self.portal_membership
719        member = pm.getAuthenticatedMember()
720        logger = logging.getLogger('WAeUPTables.ApplicantsCatalog.importCSV')
721        lock_fn = "%s/import/%s_import_lock" % (i_home,filename)
722        import_fn = "%s/import/%s.csv" % (i_home,filename)
723        if mode not in ('add','edit'):
724            logger.info("invalid mode: %s" % mode)
725        if os.path.exists(lock_fn):
726            logger.info("import of %(import_fn)s already in progress" % vars())
727            return
728        lock_file = open(lock_fn,"w")
729        lock_file.write("%(current)s \n" % vars())
730        lock_file.close()
731        invalid_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
732        duplicate_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
733        stool = getToolByName(self, 'portal_schemas')
734        ltool = getToolByName(self, 'portal_layouts')
735        schema = stool._getOb(schema_id)
736        if schema is None:
737            em = 'No such schema %s' % schema_id
738            logger.error(em)
739            return
740        for postfix in ('_import',''):
741            layout_name = "%(layout_id)s%(postfix)s" % vars()
742            if hasattr(ltool,layout_name):
743                break
744        layout = ltool._getOb(layout_name)
745        if layout is None:
746            em = 'No such layout %s' % layout_id
747            logger.error(em)
748            return
749        try:
750            csv_file = csv.DictReader(open(import_fn,"rb"))
751        except:
752            em = 'Error reading %s.csv' % filename
753            logger.error(em)
754            return
755        d = self._import_new(csv_items,schema,layout,mode,logger)
756        imported = []
757        edited = []
758        duplicates = []
759        not_found = []
760        if len(d['valid_records']) > 0:
761            for record in d['valid_records']:
762                #import pdb;pdb.set_trace()
763                if mode == "add":
764                    try:
765                        self.addRecord(**dict(record.items()))
766                        imported.append(**dict(record.items()))
767                        logger.info("added %s" % record.items())
768                    except ValueError:
769                        dupplicate.append(**dict(record.items()))
770                        logger.info("duplicate %s" % record.items())
771                elif mode == "edit":
772                    try:
773                        self.modifyRecord(**dict(record.items()))
774                        edited.append(**dict(record.items()))
775                        logger.info("edited %s" % record.items())
776                    except KeyError:
777                        not_found.append(**dict(record.items()))
778                        logger.info("not found %s" % record.items())
779        invalid = d['invalid_records']
780        for itype in ("imported","edited","not_found","duplicate","invalid"):
781            outlist = locals[itype]
782            if len(outlist):
783                d = {}
784                for k in outlist[0].keys():
785                    d[k] = k
786                outlist[0] = d
787                outfile = open("file_name_%s" % itype,'w')
788                csv.DictWriter(outfile,outlist[0].keys()).writerows(outlist)
789                logger.info("wrote %(itype)s records to %(, written to %(not_imported_fn)s" % d)
790###)
791
792    security.declareProtected(ModifyPortalContent,"importCSV")###(
793    def importCSV(self,filename="JAMB_data",
794                  schema_id="application",
795                  layout_id="application_pce",
796                  mode='add'):
797        """ import JAMB data """
798        stool = getToolByName(self, 'portal_schemas')
799        ltool = getToolByName(self, 'portal_layouts')
800        schema = stool._getOb(schema_id)
801        if schema is None:
802            em = 'No such schema %s' % schema_id
803            logger.error(em)
804            return
805        layout = ltool._getOb(layout_id)
806        if layout is None:
807            em = 'No such layout %s' % layout_id
808            logger.error(em)
809            return
810        logger = logging.getLogger('WAeUPTables.ApplicantsCatalog.importCSV')
811        d = self._import_old(filename,schema,layout,mode,logger)
812        if len(d['valid_records']) > 0:
813            for record in d['valid_records']:
814                #import pdb;pdb.set_trace()
815                if mode == "add":
816                    self.addRecord(**dict(record.items()))
817                    logger.info("added %s" % record.items())
818                elif mode == "edit":
819                    self.modifyRecord(**dict(record.items()))
820                    logger.info("edited %s" % record.items())
821                else:
822                    logger.info("invalid mode: %s" % mode)
823        logger.info("%(mode)sed %(imported)d records, invalid written to %(not_imported_fn)s" % d)
824    ###)
825
826InitializeClass(ApplicantsCatalog)
827
828###)
829
830class StudentsCatalog(WAeUPTable): ###(
831    security = ClassSecurityInfo()
832
833    meta_type = 'WAeUP Students Catalog'
834    name = "students_catalog"
835    key = "id"
836    affected_types = {   ###(
837                      'StudentApplication':
838                      {'id': 'application',
839                       'fields':
840                       ('jamb_reg_no',
841                        'entry_mode',
842                        #'entry_level',
843                        'entry_session',
844                       )
845                      },
846                      'StudentClearance':
847                      {'id': 'clearance',
848                       'fields':
849                       ('matric_no',
850                        'lga',
851                        'date_of_birth',  # birthday
852                       )
853                      },
854                      'StudentPersonal':
855                      {'id': 'personal',
856                       'fields':
857                       ('name',
858                        'sex',
859                        'perm_address',
860                        'email',
861                        'phone',
862                        'marit_stat',
863                        'firstname',
864                        'middlename',
865                        'lastname',
866                       )
867                      },
868                      'StudentStudyCourse':
869                      {'id': 'study_course',
870                       'fields':
871                       ('course', # study_course
872                        'faculty', # from certificate
873                        'department', # from certificate
874                        'end_level', # from certificate
875                        'level', # current_level
876                        'mode',  # from certificate
877                        'session', # current_session
878                        'verdict', # current_verdict
879                       )
880                      },
881                     }
882    ###)
883
884    def __init__(self,name=None):
885        if name ==  None:
886            name = self.name
887        WAeUPTable.__init__(self, name)
888        return
889
890    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None):
891        """ clears the whole enchilada """
892        self._catalog.clear()
893
894        if REQUEST and RESPONSE:
895            RESPONSE.redirect(
896              URL1 +
897              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Cleared')
898
899    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
900        """ clear the catalog, then re-index everything """
901
902        elapse = time.time()
903        c_elapse = time.clock()
904
905        pgthreshold = self._getProgressThreshold()
906        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
907        self.refreshCatalog(clear=1, pghandler=handler)
908
909        elapse = time.time() - elapse
910        c_elapse = time.clock() - c_elapse
911
912        RESPONSE.redirect(
913            URL1 +
914            '/manage_catalogAdvanced?manage_tabs_message=' +
915            urllib.quote('Catalog Updated \n'
916                         'Total time: %s\n'
917                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
918    ###)
919
920    def fill_certificates_dict(self): ###(
921        "return certificate data in  dict"
922        certificates_brains = self.portal_catalog(portal_type ='Certificate')
923        d = {}
924        for cb in certificates_brains:
925            certificate_doc = cb.getObject().getContent()
926            cb_path = cb.getPath().split('/')
927            ld = {}
928            ld['faculty'] = cb_path[-4]
929            ld['department'] = cb_path[-3]
930            ld['end_level'] = getattr(certificate_doc,'end_level','999')
931            ld['study_mode'] = getattr(certificate_doc,'study_mode','')
932            d[cb.getId] = ld
933        return d
934    ###)
935
936    def get_from_doc_department(self,doc,cached_data={}): ###(
937        "return the students department"
938        if doc is None:
939            return None
940        if hasattr(self,"_v_certificates") and self._v_certificates.has_key(doc.study_course):
941            return self._v_certificates[doc.study_course]['department']
942        certificate_res = self.portal_catalog(id = doc.study_course)
943        if len(certificate_res) != 1:
944            return None
945        return certificate_res[0].getPath().split('/')[-3]
946
947    def get_from_doc_faculty(self,doc,cached_data={}):
948        "return the students faculty"
949        if doc is None:
950            return None
951        if hasattr(self,"_v_certificates") and self._v_certificates.has_key(doc.study_course):
952            return self._v_certificates[doc.study_course]['faculty']
953        certificate_res = self.portal_catalog(id = doc.study_course)
954        if len(certificate_res) != 1:
955            return None
956        return certificate_res[0].getPath().split('/')[-4]
957
958    def get_from_doc_end_level(self,doc,cached_data={}):
959        "return the students end_level"
960        if doc is None:
961            return None
962        if hasattr(self,"_v_certificates") and self._v_certificates.has_key(doc.study_course):
963            return self._v_certificates[doc.study_course]['end_level']
964        certificate_res = self.portal_catalog(id = doc.study_course)
965        if len(certificate_res) != 1:
966            return None
967        return getattr(certificate_res[0].getObject().getContent(),'end_level','unknown')
968
969    def get_from_doc_level(self,doc,cached_data={}):
970        "return the students level"
971        if doc is None:
972            return None
973        return getattr(doc,'current_level',None)
974
975    #def get_from_doc_mode(self,doc,cached_data={}):
976    #    "return the students mode"
977    #    if doc is None:
978    #        return None
979    #    cm = getattr(doc,'current_mode',None)
980    #    return cm
981   
982    def get_from_doc_mode(self,doc,cached_data={}):
983        "return the students mode"
984        if doc is None:
985            return None
986        if hasattr(self,"_v_certificates") and self._v_certificates.has_key(doc.study_course):
987            return self._v_certificates[doc.study_course]['study_mode']
988        certificate_res = self.portal_catalog(id = doc.study_course)
989        if len(certificate_res) != 1:
990            return None
991        return getattr(certificate_res[0].getObject().getContent(),'study_mode','unknown')   
992
993
994    def get_from_doc_marit_stat(self,doc,cached_data={}):
995        "return the students marit_stat"
996        if doc is None:
997            return None
998        ms = getattr(doc,'marit_stat',None)
999        if ms == True:
1000            return 'married'
1001        elif ms == False:
1002            return 'single'
1003        else:
1004            return 'undefined'
1005           
1006    def get_from_doc_date_of_birth(self,doc,cached_data={}):
1007        "return the students date of birth"
1008        if doc is None:
1009            return None
1010        return getattr(doc,'birthday',None)           
1011
1012    def get_from_doc_session(self,doc,cached_data={}):
1013        "return the students current_session"
1014        if doc is None:
1015            return None
1016        return getattr(doc,'current_session',None)
1017
1018    def get_from_doc_entry_session(self,doc,cached_data={}):
1019        "return the students entry_session"
1020        if doc is None:
1021            return None
1022        es = getattr(doc,'entry_session',None)
1023        if es is not None and len(es) < 3:
1024            return es
1025        elif len(es) == 9:
1026            return es[2:4]   
1027        try:
1028            digit = int(doc.jamb_reg_no[0])
1029        except:
1030            return "-1"
1031        if digit < 9:
1032            return "0%c" % doc.jamb_reg_no[0]
1033        return "9%c" % doc.jamb_reg_no[0]
1034
1035    def get_from_doc_course(self,doc,cached_data={}):
1036        "return the students study_course"
1037        if doc is None:
1038            return None
1039        return getattr(doc,'study_course',None)
1040
1041    def get_from_doc_name(self,doc,cached_data={}):
1042        "return the students name from the personal"
1043        if doc is None:
1044            return None
1045        return "%s %s %s" % (doc.firstname,doc.middlename,doc.lastname)
1046
1047    def get_from_doc_verdict(self,doc,cached_data={}):
1048        "return the students study_course"
1049        if doc is None:
1050            return None
1051        return getattr(doc,'current_verdict',None)
1052    ###)
1053
1054    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
1055        if not hasattr(self,'_v_certificates'):
1056            self._v_certificates = self.fill_certificates_dict()
1057        if isinstance(name, str):
1058            name = (name,)
1059        reindextypes = {}
1060        reindex_special = []
1061        for n in name:
1062            if n in ("review_state"):
1063                reindex_special.append(n)
1064            else:
1065                for pt in self.affected_types.keys():
1066                    if n in self.affected_types[pt]['fields']:
1067                        if reindextypes.has_key(pt):
1068                            reindextypes[pt].append(n)
1069                        else:
1070                            reindextypes[pt]= [n]
1071                        break
1072        #cached_data = {}
1073        #if set(name).intersection(set(('faculty','department','end_level','mode'))):
1074        #    cached_data = self.fill_certificates_dict()
1075        students = self.portal_catalog(portal_type="Student")
1076        if hasattr(self,'portal_catalog_real'):
1077            aq_portal = self.portal_catalog_real.evalAdvancedQuery
1078        else:
1079            aq_portal = self.portal_catalog.evalAdvancedQuery
1080        num_objects = len(students)
1081        if pghandler:
1082            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1083        noattr = set(('StudentClearance','StudentPersonal')) & set(reindextypes.keys())
1084        #import pdb;pdb.set_trace()
1085        for i in xrange(num_objects):
1086            if pghandler: pghandler.report(i)
1087            student_brain = students[i]
1088            student_object = student_brain.getObject()
1089            data = {}
1090            modified = False
1091            sid = data['id'] = student_brain.getId
1092            if reindex_special and 'review_state' in reindex_special:
1093                modified = True
1094                data['review_state'] = student_object.portal_workflow.getInfoFor(student_object,'review_state',None)
1095            sub_objects = False
1096            for pt in reindextypes.keys():
1097                modified = True
1098                try:
1099                    doc = getattr(student_object,self.affected_types[pt]['id']).getContent()
1100                    sub_objects = True
1101                except:
1102                    continue
1103                for field in set(name).intersection(self.affected_types[pt]['fields']):
1104                    if hasattr(self,'get_from_doc_%s' % field):
1105                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc)
1106                    else:
1107                        data[field] = getattr(doc,field)
1108            if not sub_objects and noattr:
1109                import_res = self.returning_import(id = sid)
1110                if not import_res:
1111                    continue
1112                import_record = import_res[0]
1113                data['matric_no'] = import_record.matric_no
1114                data['sex'] = import_record.Sex == 'F'
1115                data['name'] = "%s %s %s" % (import_record.Firstname,
1116                                             import_record.Middlename,
1117                                             import_record.Lastname)
1118                data['jamb_reg_no'] = import_record.Entryregno
1119            if modified:
1120                self.modifyRecord(**data)
1121        if pghandler: pghandler.finish()
1122    ###)
1123
1124    def refreshCatalog(self, clear=0, pghandler=None): ###(
1125        """ re-index everything we can find """
1126        students_folder = self.portal_url.getPortalObject().campus.students
1127        if clear:
1128            self._catalog.clear()
1129        students = self.portal_catalog(portal_type="Student")
1130        num_objects = len(students)
1131        #cached_data = self.fill_certificates_dict()
1132        if not hasattr(self,'_v_certificates'):
1133            self._v_certificates = self.fill_certificates_dict()
1134        if pghandler:
1135            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1136        for i in xrange(num_objects):
1137            if pghandler: pghandler.report(i)
1138            student_brain = students[i]
1139            spath = student_brain.getPath()
1140            student_object = student_brain.getObject()
1141            data = {}
1142            sid = data['id'] = student_brain.getId
1143            #data['review_state'] = student_brain.review_state
1144            data['review_state'] = student_object.portal_workflow.getInfoFor(student_object,'review_state',None)
1145            sub_objects = False
1146            for pt in self.affected_types.keys():
1147                modified = True
1148                try:
1149                    doc = getattr(student_object,self.affected_types[pt]['id']).getContent()
1150                    sub_objects = True
1151                except:
1152                    #from pdb import set_trace;set_trace()
1153                    continue
1154                for field in self.affected_types[pt]['fields']:
1155                    if hasattr(self,'get_from_doc_%s' % field):
1156                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc,
1157                                                                              cached_data=cached_data)
1158                    else:
1159                        data[field] = getattr(doc,field,None)
1160            if not sub_objects:
1161                import_res = self.returning_import(id = sid)
1162                if not import_res:
1163                    continue
1164                import_record = import_res[0]
1165                data['matric_no'] = import_record.matric_no
1166                data['sex'] = import_record.Sex == 'F'
1167                data['name'] = "%s %s %s" % (import_record.Firstname,
1168                                             import_record.Middlename,
1169                                             import_record.Lastname)
1170                data['jamb_reg_no'] = import_record.Entryregno
1171            self.addRecord(**data)
1172        if pghandler: pghandler.finish()
1173    ###)
1174
1175    security.declarePrivate('notify_event_listener') ###(
1176    def notify_event_listener(self,event_type,object,infos):
1177        "listen for events"
1178        if not infos.has_key('rpath'):
1179            return
1180        pt = getattr(object,'portal_type',None)
1181        mt = getattr(object,'meta_type',None)
1182        students_catalog = self
1183        data = {}
1184        if pt == 'Student' and\
1185           mt == 'CPS Proxy Folder' and\
1186           event_type.startswith('workflow'):
1187            data['id'] = object.getId()
1188            data['review_state'] = self.portal_workflow.getInfoFor(object,'review_state',None)
1189            students_catalog.modifyRecord(**data)
1190            return
1191        rpl = infos['rpath'].split('/')
1192        if pt == 'Student' and mt == 'CPS Proxy Folder':
1193            student_id = object.id
1194            if event_type == "sys_add_object":
1195                try:
1196                    self.addRecord(id = student_id)
1197                except ValueError:
1198                    pass
1199                return
1200            elif event_type == 'sys_del_object':
1201                self.deleteRecord(student_id)
1202        if pt not in self.affected_types.keys():
1203            return
1204        if event_type not in ('sys_modify_object'):
1205            return
1206        if mt == 'CPS Proxy Folder':
1207            return
1208        if not hasattr(self,'_v_certificates'):
1209            self._v_certificates = self.fill_certificates_dict()
1210        for field in self.affected_types[pt]['fields']:
1211            if hasattr(self,'get_from_doc_%s' % field):
1212                data[field] = getattr(self,'get_from_doc_%s' % field)(object)
1213            else:
1214                data[field] = getattr(object,field)
1215        data['id'] = rpl[2]
1216        self.modifyRecord(**data)
1217    ###)
1218
1219
1220InitializeClass(StudentsCatalog)
1221
1222###)
1223
1224class CertificatesCatalog(WAeUPTable): ###(
1225    security = ClassSecurityInfo()
1226
1227    meta_type = 'WAeUP Certificates Catalog'
1228    name =  "certificates_catalog"
1229    key = "code"
1230    def __init__(self,name=None):
1231        if name ==  None:
1232            name =  self.name
1233        WAeUPTable.__init__(self, name)
1234
1235    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
1236        """ clear the catalog, then re-index everything """
1237
1238        elapse = time.time()
1239        c_elapse = time.clock()
1240
1241        pgthreshold = self._getProgressThreshold()
1242        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
1243        self.refreshCatalog(clear=1, pghandler=handler)
1244
1245        elapse = time.time() - elapse
1246        c_elapse = time.clock() - c_elapse
1247
1248        RESPONSE.redirect(
1249            URL1 +
1250            '/manage_catalogAdvanced?manage_tabs_message=' +
1251            urllib.quote('Catalog Updated \n'
1252                         'Total time: %s\n'
1253                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
1254    ###)
1255
1256    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
1257        if isinstance(name, str):
1258            name = (name,)
1259        certificates = self.portal_catalog(portal_type="Certificate")
1260        num_objects = len(certificates)
1261        if pghandler:
1262            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1263        for i in xrange(num_objects):
1264            if pghandler: pghandler.report(i)
1265            certificate_brain = certificates[i]
1266            certificate_object = certificate_brain.getObject()
1267            pl = certificate_brain.getPath().split('/')
1268            data = {}
1269            cid = data[self.key] = certificate_brain.getId
1270            data['faculty'] = pl[-4]
1271            data['department'] = pl[-3]
1272            doc = certificate_object.getContent()
1273            for field in name:
1274                if field not in (self.key,'faculty','department'):
1275                    data[field] = getattr(doc,field)
1276            self.modifyRecord(**data)
1277        if pghandler: pghandler.finish()
1278    ###)
1279
1280    def refreshCatalog(self, clear=0, pghandler=None): ###(
1281        """ re-index everything we can find """
1282        if clear:
1283            self._catalog.clear()
1284        certificates = self.portal_catalog(portal_type="Certificate")
1285        num_objects = len(certificates)
1286        if pghandler:
1287            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1288        #from pdb import set_trace;set_trace()
1289        for i in xrange(num_objects):
1290            if pghandler: pghandler.report(i)
1291            certificate_brain = certificates[i]
1292            certificate_doc = certificate_brain.getObject().getContent()
1293            pl = certificate_brain.getPath().split('/')
1294            data = {}
1295            for field in self.schema():
1296                data[field] = getattr(certificate_doc,field,None)
1297            data[self.key] = certificate_brain.getId
1298            ai = pl.index('academics')
1299            data['faculty'] = pl[ai +1]
1300            data['department'] = pl[ai +2]
1301            if clear:
1302                self.addRecord(**data)
1303            else:
1304                self.modifyRecord(**data)
1305        if pghandler: pghandler.finish()
1306    ###)
1307
1308    security.declarePrivate('notify_event_listener') ###(
1309    def notify_event_listener(self,event_type,object,infos):
1310        "listen for events"
1311        if not infos.has_key('rpath'):
1312            return
1313        pt = getattr(object,'portal_type',None)
1314        mt = getattr(object,'meta_type',None)
1315        if pt != 'Certificate':
1316            return
1317        data = {}
1318        rpl = infos['rpath'].split('/')
1319        if event_type not in ("sys_add_object","sys_modify_object","sys_del_object"):
1320            return
1321        certificate_id = object.getId()
1322        data[self.key] = certificate_id
1323        if event_type == "sys_add_object" and mt == 'CPS Proxy Folder':
1324            try:
1325                self.addRecord(**data)
1326            except ValueError:
1327                return
1328            certificate_id = object.getId()
1329            doc = object.getContent()
1330            if doc is None:
1331                return
1332            for field in self.schema():
1333                data[field] = getattr(doc,field,None)
1334            data[self.key] = certificate_id
1335            ai = rpl.index('academics')
1336            data['faculty'] = rpl[ai +1]
1337            data['department'] = rpl[ai +2]
1338            self.modifyRecord(**data)
1339            return
1340        if event_type == "sys_del_object":
1341            self.deleteRecord(certificate_id)
1342            return
1343        if event_type == "sys_modify_object" and mt == 'Certificate':
1344            #from pdb import set_trace;set_trace()
1345            for field in self.schema():
1346                data[field] = getattr(object,field,None)
1347            certificate_id = object.aq_parent.getId()
1348            data[self.key] = certificate_id
1349            ai = rpl.index('academics')
1350            data['faculty'] = rpl[ai +1]
1351            data['department'] = rpl[ai +2]
1352            self.modifyRecord(**data)
1353    ###)
1354
1355
1356InitializeClass(CertificatesCatalog)
1357###)
1358
1359class CoursesCatalog(WAeUPTable): ###(
1360    security = ClassSecurityInfo()
1361
1362    meta_type = 'WAeUP Courses Catalog'
1363    name =  "courses_catalog"
1364    key = "code"
1365    def __init__(self,name=None):
1366        if name ==  None:
1367            name =  self.name
1368        WAeUPTable.__init__(self, name)
1369
1370    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
1371        """ clear the catalog, then re-index everything """
1372
1373        elapse = time.time()
1374        c_elapse = time.clock()
1375
1376        pgthreshold = self._getProgressThreshold()
1377        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
1378        self.refreshCatalog(clear=1, pghandler=handler)
1379
1380        elapse = time.time() - elapse
1381        c_elapse = time.clock() - c_elapse
1382
1383        RESPONSE.redirect(
1384            URL1 +
1385            '/manage_catalogAdvanced?manage_tabs_message=' +
1386            urllib.quote('Catalog Updated \n'
1387                         'Total time: %s\n'
1388                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
1389    ###)
1390
1391    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
1392        if isinstance(name, str):
1393            name = (name,)
1394        courses = self.portal_catalog(portal_type="Course")
1395        num_objects = len(courses)
1396        if pghandler:
1397            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1398        for i in xrange(num_objects):
1399            if pghandler: pghandler.report(i)
1400            course_brain = courses[i]
1401            course_object = course_brain.getObject()
1402            pl = course_brain.getPath().split('/')
1403            data = {}
1404            cid = data[self.key] = course_brain.getId
1405            data['faculty'] = pl[-4]
1406            data['department'] = pl[-3]
1407            doc = course_object.getContent()
1408            for field in name:
1409                if field not in (self.key,'faculty','department'):
1410                    data[field] = getattr(doc,field)
1411            self.modifyRecord(**data)
1412        if pghandler: pghandler.finish()
1413    ###)
1414
1415    def refreshCatalog(self, clear=0, pghandler=None): ###(
1416        """ re-index everything we can find """
1417        if clear:
1418            self._catalog.clear()
1419        courses = self.portal_catalog(portal_type="Course")
1420        num_objects = len(courses)
1421        if pghandler:
1422            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1423        #from pdb import set_trace;set_trace()
1424        for i in xrange(num_objects):
1425            if pghandler: pghandler.report(i)
1426            course_brain = courses[i]
1427            course_doc = course_brain.getObject().getContent()
1428            pl = course_brain.getPath().split('/')
1429            data = {}
1430            for field in self.schema():
1431                data[field] = getattr(course_doc,field,None)
1432            data[self.key] = course_brain.getId
1433            ai = pl.index('academics')
1434            data['faculty'] = pl[ai +1]
1435            data['department'] = pl[ai +2]
1436            if clear:
1437                self.addRecord(**data)
1438            else:
1439                self.modifyRecord(**data)
1440        if pghandler: pghandler.finish()
1441    ###)
1442
1443    security.declarePrivate('notify_event_listener') ###(
1444    def notify_event_listener(self,event_type,object,infos):
1445        "listen for events"
1446        if not infos.has_key('rpath'):
1447            return
1448        pt = getattr(object,'portal_type',None)
1449        mt = getattr(object,'meta_type',None)
1450        if pt != 'Course':
1451            return
1452        data = {}
1453        rpl = infos['rpath'].split('/')
1454        if event_type not in ("sys_add_object","sys_modify_object","sys_del_object"):
1455            return
1456        course_id = object.getId()
1457        data[self.key] = course_id
1458        if event_type == "sys_add_object" and mt == 'CPS Proxy Folder':
1459            try:
1460                self.addRecord(**data)
1461            except ValueError:
1462                return
1463            course_id = object.getId()
1464            doc = object.getContent()
1465            if doc is None:
1466                return
1467            for field in self.schema():
1468                data[field] = getattr(doc,field,None)
1469            data[self.key] = course_id
1470            ai = rpl.index('academics')
1471            data['faculty'] = rpl[ai +1]
1472            data['department'] = rpl[ai +2]
1473            self.modifyRecord(**data)
1474            return
1475        if event_type == "sys_del_object":
1476            self.deleteRecord(course_id)
1477            return
1478        if event_type == "sys_modify_object" and mt == 'Course':
1479            #from pdb import set_trace;set_trace()
1480            for field in self.schema():
1481                data[field] = getattr(object,field,None)
1482            course_id = object.aq_parent.getId()
1483            data[self.key] = course_id
1484            ai = rpl.index('academics')
1485            data['faculty'] = rpl[ai +1]
1486            data['department'] = rpl[ai +2]
1487            self.modifyRecord(**data)
1488    ###)
1489
1490
1491InitializeClass(CoursesCatalog)
1492###)
1493
1494class CourseResults(WAeUPTable): ###(
1495    security = ClassSecurityInfo()
1496
1497    meta_type = 'WAeUP Results Catalog'
1498    name = "course_results"
1499    key = "key" #student_id + level + course_id
1500    def __init__(self,name=None):
1501        if name ==  None:
1502            name = self.name
1503        WAeUPTable.__init__(self, name)
1504        self._queue = []
1505
1506    def addMultipleRecords(self, records): ###(
1507        """add many records"""
1508        existing_uids = []
1509        for data in records:
1510            uid = "%(student_id)s|%(level_id)s|%(course_id)s" % data
1511            data['%s' % self.key] = uid
1512            query = Eq(self.key, uid)
1513            res = self.course_results.evalAdvancedQuery(query)
1514            if len(res) > 0:
1515                rec = res[0]
1516                equal = True
1517                for attr in ('student_id','level_id','course_id'):
1518                    if getattr(rec,attr,'') != data[attr]:
1519                        equal = False
1520                        break
1521                if equal:
1522                    existing_uids += uid,
1523                    continue
1524            self.catalog_object(dict2ob(data), uid=uid)
1525        return existing_uids
1526    ###)
1527
1528    def deleteResultsHere(self,level_id,student_id): ###(
1529        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1530        course_results = self.course_results.evalAdvancedQuery(query)
1531        #import pdb;pdb.set_trace()
1532        for result in course_results:
1533            self.deleteRecord(result.key)
1534    ###)
1535
1536    def moveResultsHere(self,level,student_id): ###(
1537        #import pdb;pdb.set_trace()
1538        level_id = level.getId()
1539        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1540        course_results = self.course_results.evalAdvancedQuery(query)
1541        existing_courses = [cr.code for cr in course_results]
1542        to_delete = []
1543        for code,obj in level.objectItems():
1544            to_delete.append(code)
1545            carry_over = False
1546            if code.endswith('_co'):
1547                carry_over = True
1548                code  = code[:-3]
1549            if code in existing_courses:
1550                continue
1551            course_result_doc = obj.getContent()
1552            data = {}
1553            course_id = code
1554            for field in self.schema():
1555                data[field] = getattr(course_result_doc,field,'')
1556            data['key'] = key = "%(student_id)s|%(level_id)s|%(course_id)s" % vars()
1557            data['student_id'] = student_id
1558            data['level_id'] = level_id
1559            session_id = self.getLevelSession(level.getContent(),student_id,level_id)
1560            data['session_id'] = session_id
1561            #data['queue_status'] = OBJECT_CREATED
1562            data['code'] = course_id
1563            data['carry_over'] = carry_over
1564            self.catalog_object(dict2ob(data), uid=key)
1565        level.manage_delObjects(to_delete)
1566    ###)
1567
1568    def getCourses(self,student_id,level_id): ###(
1569        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1570        course_results = self.course_results.evalAdvancedQuery(query)
1571        carry_overs = []
1572        normal1 = []
1573        normal2 = []
1574        normal3 = []
1575        total_credits = 0
1576        gpa = 0
1577        for brain in course_results:
1578            d = {}
1579
1580            for field in self.schema():
1581                d[field] = getattr(brain,field,None)
1582                if repr(d[field]) == 'Missing.Value':
1583                    d[field] = ''
1584            d['weight'] = ''
1585            d['grade'] = ''
1586            d['score'] = ''
1587
1588            if str(brain.credits).isdigit():
1589                credits = int(brain.credits)
1590                total_credits += credits
1591                score = getattr(brain,'score',0)
1592                if score and str(score).isdigit() and int(score) > 0:
1593                    score = int(score)
1594                    grade,weight = self.getGradesFromScore(score,'')
1595                    gpa += weight * credits
1596                    d['weight'] = weight
1597                    d['grade'] = grade
1598                    d['score'] = score
1599
1600            #if str(brain.ca1).isdigit() and str(brain.ca2).isdigit() and str(brain.exam).isdigit():
1601            #    d['score_calc'] = int(brain.ca1) + int(brain.ca2) + int(brain.exam)
1602            #else:
1603            #    d['score_calc'] = ''
1604            try:
1605                d['score_calc'] = float(brain.ca1) + float(brain.ca2) + float(brain.exam)
1606            except:
1607                d['score_calc'] = ''
1608
1609            if d['score_calc']:
1610                grade = self.getGradesFromScore(d['score_calc'],level_id)
1611                d['grade'] = grade
1612
1613            d['coe'] = ''
1614            if brain.core_or_elective:
1615                d['coe'] = 'Core'
1616            elif brain.core_or_elective == False:
1617                d['coe'] = 'Elective'
1618            id = code = d['id'] = brain.code
1619            d['code'] = code
1620            res = self.courses_catalog.evalAdvancedQuery(Eq('code',code))
1621            if res:
1622                course = res[0]
1623                d['title'] = course.title
1624                # The courses_catalog contains strings and integers in its semester field.
1625                # Maybe this can be fixed by reindexing the catalog. The schema of course says 'CPS Int Field'.
1626                d['semester'] = str(course.semester)
1627            else:
1628                d['title'] = "Course has been removed from course list"
1629                d['semester'] = ''
1630            if brain.carry_over:
1631                d['coe'] = 'CO'
1632                carry_overs.append(d)
1633            else:
1634                if d['semester'] == '1':
1635                    normal1.append(d)
1636
1637                elif d['semester'] == '2':
1638                    normal2.append(d)
1639                else:
1640                    normal3.append(d)
1641        #normal.sort(cmp=lambda x,y: cmp("%(semester)s%(code)s" % x,
1642        #                                "%(semester)s%(code)s" % y))
1643        carry_overs.sort(cmp=lambda x,y: cmp("%(semester)s%(code)s" % x,
1644                                             "%(semester)s%(code)s" % y))
1645        return total_credits,gpa,carry_overs,normal1,normal2,normal3
1646    ###)
1647
1648   
1649    # for transcript only
1650    def getAllCourses(self,student_id): ###(
1651        query = Eq('student_id',student_id)
1652        course_results = self.course_results.evalAdvancedQuery(query)
1653        courses = []
1654        for brain in course_results:
1655            d = {}
1656
1657            for field in self.schema():
1658                d[field] = getattr(brain,field,'')
1659
1660            d['weight'] = ''
1661            d['grade'] = ''
1662            d['score'] = ''
1663
1664            if str(brain.credits).isdigit():
1665                credits = int(brain.credits)
1666                score = getattr(brain,'score',0)
1667                if score and str(score).isdigit() and int(score) > 0:
1668                    score = int(score)
1669                    grade,weight = self.getGradesFromScore(score,'')
1670                    d['weight'] = weight
1671                    d['grade'] = grade
1672                    d['score'] = score
1673            d['coe'] = ''
1674            if brain.core_or_elective:
1675                d['coe'] = 'Core'
1676            elif brain.core_or_elective == False:
1677                d['coe'] = 'Elective'
1678            id = code = d['id'] = brain.code
1679            d['code'] = code
1680            res = self.courses_catalog.evalAdvancedQuery(Eq('code',code))
1681            if res:
1682                course = res[0]
1683                d['title'] = course.title
1684                # The courses_catalog contains strings and integers in its semester field.
1685                # Maybe this can be fixed by reindexing the catalog. The schema of course says 'CPS Int Field'.
1686                d['semester'] = str(course.semester)
1687            else:
1688                d['title'] = "Course has been removed from course list"
1689                d['semester'] = ''
1690            if brain.carry_over:
1691                d['coe'] = 'CO'
1692            courses.append(d)
1693        return courses
1694    ###)
1695   
1696    def getYearGroupAverage(self,session_id,level_id): ###(
1697        query = Eq('session_id',session_id) & Eq('level_id',level_id)
1698        course_results = self.course_results.evalAdvancedQuery(query)
1699        yga1 = 0
1700        yg1 = []
1701        counter1 = 0
1702        yga2 = 0
1703        yg2 = []
1704        counter2 = 0
1705        yga3 = 0
1706        yg3 = []
1707        counter3 = 0       
1708        #import pdb;pdb.set_trace()
1709        for brain in course_results:
1710            try:
1711                om = float(brain.ca1) + float(brain.ca2) + float(brain.exam)
1712                if not om > 0:
1713                    continue
1714                code = brain.code               
1715                res = self.courses_catalog.evalAdvancedQuery(Eq('code',code))
1716                if res:
1717                    course = res[0]
1718                    # The courses_catalog contains strings and integers in its semester field.
1719                    # Maybe this can be fixed by reindexing the catalog. The schema of course says 'CPS Int Field'.
1720                    semester = str(course.semester)
1721                else:
1722                    semester = ''
1723                if semester == '1':
1724                    counter1 += 1
1725                    yga1 += om
1726                    yg1.append(om)
1727                elif semester == '2':
1728                    counter2 += 1
1729                    yga2 += om     
1730                    yg2.append(om)   
1731                if semester == '3':
1732                    counter3 += 1
1733                    yga3 += om
1734                    yg3.append(om)
1735            except:
1736                continue               
1737        if counter1:
1738            yga1 /= counter1
1739            yga1 = '%.2f' % yga1   
1740        if counter2:
1741            yga2 /= counter2
1742            yga2 = '%.2f' % yga2   
1743        if counter3:
1744            yga3 /= counter3
1745            yga3 = '%.2f' % yga3                                   
1746        return yga1, yga2, yga3, counter1, counter2, counter3, yg1, yg2, yg3
1747    ###)
1748   
1749   
1750    #security.declarePublic("calculateCoursePosition")
1751    def calculateCoursePosition(self,session_id,level_id,code,score,semester=None):
1752        #"""calculate Course Position"""
1753        query = Eq('session_id',session_id) & Eq('level_id',level_id) & Eq('code',code)
1754        course_results = self.course_results.evalAdvancedQuery(query)
1755        ygc = []
1756        #import pdb;pdb.set_trace() 
1757        for brain in course_results:
1758            try:
1759                if not float(brain.ca1) + float(brain.ca2) + float(brain.exam) > 0:
1760                    continue
1761                #code = brain.code   
1762                if semester:
1763                    res = self.courses_catalog.evalAdvancedQuery(Eq('code',code))
1764                    if res:
1765                        course = res[0]
1766                        # The courses_catalog contains strings and integers in its semester field.
1767                        # Maybe this can be fixed by reindexing the catalog. The schema of course says 'CPS Int Field'.
1768                        semester_from_course = str(course.semester)
1769                    else:
1770                        continue
1771                    if semester != semester_from_course:
1772                        continue
1773                ygc.append(float(brain.ca1) + float(brain.ca2) + float(brain.exam))
1774            except:
1775                continue     
1776        ygc.sort(reverse=True)
1777        if not len(ygc):
1778            return 'no result'
1779        #import pdb;pdb.set_trace()       
1780        for pos in range(len(ygc)):
1781            if ygc[pos] <= float(score):
1782                break
1783        output = {}   
1784        output['pos'] =  '%d of %d' % (pos+1,len(ygc))
1785        output['ygc'] = ygc
1786        return output
1787       
1788    security.declareProtected(ModifyPortalContent,"calculateAllCoursePositions")
1789    def calculateAllCoursePositions(self,session_id=None):
1790        """calculate All Course Positions"""
1791        logger = logging.getLogger('WAeUPTables.CourseResults.calculateAllCoursePositions')
1792        member = self.portal_membership.getAuthenticatedMember()
1793        logger.info('%s starts recalculation of positions in session %s' % (member,session_id))
1794        if session_id:
1795            query = Eq('session_id',session_id)
1796        else:
1797            return 'no session_id provided'
1798        course_results = self.course_results.evalAdvancedQuery(query)
1799        for brain in course_results:
1800            try:
1801                if not float(brain.ca1) + float(brain.ca2) + float(brain.exam) > 0:
1802                    data = {}
1803                    data[self.key] = brain.key
1804                    data['pic'] = ''
1805                    self.modifyRecord(**data)                   
1806                    continue
1807                res = self.courses_catalog.evalAdvancedQuery(Eq('code',brain.code))
1808                if res:
1809                    course = res[0]
1810                    semester_from_course = str(course.semester)
1811                else:
1812                    continue                   
1813                score = float(brain.ca1) + float(brain.ca2) + float(brain.exam)
1814                pic = self.calculateCoursePosition(session_id,brain.level_id,brain.code,score,semester_from_course)['pos']
1815                data = {}
1816                data[self.key] = brain.key
1817                data['pic'] = pic
1818                self.modifyRecord(**data)
1819            except:
1820                data = {}
1821                data[self.key] = brain.key
1822                data['pic'] = ''
1823                self.modifyRecord(**data)
1824                continue       
1825        logger.info('recalculation finished')             
1826        return 'ready'   
1827   
1828    def exportRemoveAllCourses(self,student_id,export=False,remove=False): ###(
1829        ""
1830        query = Eq('student_id',student_id)
1831        cr_catalog = self.course_results
1832        course_results = cr_catalog.evalAdvancedQuery(query)
1833        courses = []
1834        fields = self.schema()
1835        format = '"%(' + ')s","%('.join(fields) + ')s"'
1836        for brain in course_results:
1837            d = {}
1838            for field in fields:
1839                d[field] = getattr(brain,field,'')
1840            courses.append(format % d)
1841               
1842        if export:
1843            export_file = "%s/export/course_results_removed.csv" % (i_home)
1844            if not os.path.exists(export_file): 
1845                file_handler = open(export_file,"a")
1846                headline = ','.join(fields)
1847                file_handler.write(headline +'\n')
1848            else:
1849                file_handler = open(export_file,"a")
1850            for line in courses:
1851                file_handler.write(line +'\n')
1852
1853        if remove:
1854            for brain in course_results:
1855                key = getattr(brain,'key','')
1856                cr_catalog.deleteRecord(key)
1857       
1858        return courses
1859    ###)   
1860   
1861   
1862
1863InitializeClass(CourseResults)
1864###)
1865
1866class OnlinePaymentsImport(WAeUPTable): ###(
1867
1868    meta_type = 'WAeUP Online Payment Transactions'
1869    name = "online_payments_import"
1870    key = "order_id"
1871    def __init__(self,name=None):
1872        if name ==  None:
1873            name = self.name
1874        WAeUPTable.__init__(self, name)
1875
1876
1877InitializeClass(OnlinePaymentsImport)
1878###)
1879
1880class ReturningImport(WAeUPTable): ###(
1881
1882    meta_type = 'Returning Import Table'
1883    name = "returning_import"
1884    key = "matric_no"
1885    def __init__(self,name=None):
1886        if name ==  None:
1887            name = self.name
1888        WAeUPTable.__init__(self, name)
1889
1890
1891InitializeClass(ReturningImport)
1892###)
1893
1894class ResultsImport(WAeUPTable): ###(
1895
1896    meta_type = 'Results Import Table'
1897    name = "results_import"
1898    key = "key"
1899    def __init__(self,name=None):
1900        if name ==  None:
1901            name = self.name
1902        WAeUPTable.__init__(self, name)
1903
1904
1905InitializeClass(ResultsImport)
1906
1907###)
1908
1909class PaymentsCatalog(WAeUPTable): ###(
1910    security = ClassSecurityInfo()
1911
1912    meta_type = 'WAeUP Payments Catalog'
1913    name = "payments_catalog"
1914    key = "order_id"
1915    def __init__(self,name=None):
1916        if name ==  None:
1917            name = self.name
1918        WAeUPTable.__init__(self, name)
1919
1920
1921    security.declarePrivate('notify_event_listener') ###(
1922    def notify_event_listener(self,event_type,object,infos):
1923        "listen for events"
1924        if not infos.has_key('rpath'):
1925            return
1926        pt = getattr(object,'portal_type',None)
1927        mt = getattr(object,'meta_type',None)
1928        data = {}
1929        if pt != 'Payment':
1930            return
1931        if event_type == 'sys_del_object' and mt == 'CPS Proxy Folder':
1932            self.deleteRecord(object.getContent().order_id)
1933        if mt == 'CPS Proxy Folder':
1934            return # is handled only for the real object
1935        if event_type not in ('sys_modify_object'):
1936            return
1937        for field in self.schema():
1938            data[field] = getattr(object,field,'')
1939        rpl = infos['rpath'].split('/')
1940        #import pdb;pdb.set_trace()
1941        student_id = rpl[-4]
1942        data['student_id'] = student_id
1943        modified = False
1944        try:
1945            self.modifyRecord(**data)
1946            modified = True
1947        except KeyError:
1948            #logger = logging.getLogger('WAeUPTables.PaymentsCatalog.%s' % self.__name__)
1949            #logger.info("could not modify entry for %(student_id)s with %(order_id)s" % data)
1950            pass
1951        if not modified:
1952            try:
1953                self.addRecord(**data)
1954            except:
1955                logger = logging.getLogger('WAeUPTables.PaymentsCatalog.notify_event_listener')
1956                logger.info("could not add or modify entry for %(student_id)s with %(order_id)s" % data)
1957        ###)
1958
1959
1960    def exportRemoveAllPayments(self,student_id,export=False,remove=False): ###(
1961        ""
1962        query = Eq('student_id',student_id)
1963        pm_catalog = self.payments_catalog
1964        payments = pm_catalog.evalAdvancedQuery(query)
1965        payments_dic = []
1966        fields = self.schema()
1967        format = '"%(' + ')s","%('.join(fields) + ')s"'
1968        for brain in payments:
1969            d = {}
1970            for field in fields:
1971                d[field] = getattr(brain,field,'')
1972            payments_dic.append(format % d)
1973               
1974        if export:
1975            export_file = "%s/export/payments_removed.csv" % (i_home)
1976            if not os.path.exists(export_file): 
1977                file_handler = open(export_file,"a")
1978                headline = ','.join(fields)
1979                file_handler.write(headline +'\n')
1980            else:
1981                file_handler = open(export_file,"a")
1982            for line in payments_dic:
1983                file_handler.write(line +'\n')
1984
1985        if remove:
1986            for brain in payments:
1987                order_id = getattr(brain,'order_id','')
1988                pm_catalog.deleteRecord(order_id)
1989       
1990        return payments_dic
1991    ###)   
1992
1993InitializeClass(PaymentsCatalog)
1994
1995###)
1996
1997class RemovedStudentIds(WAeUPTable): ###(
1998
1999    meta_type = 'WAeUP Removed StudentIds'
2000    name = "removed_student_ids"
2001    key = "id"
2002    def __init__(self,name=None):
2003        if name ==  None:
2004            name = self.name
2005        WAeUPTable.__init__(self, name)
2006
2007
2008InitializeClass(RemovedStudentIds)
2009
2010###)
2011
2012# BBB:
2013AccomodationTable = AccommodationTable
Note: See TracBrowser for help on using the repository browser.