source: WAeUP_SRP/base/WAeUPTables.py @ 2801

Last change on this file since 2801 was 2781, checked in by Henrik Bettermann, 17 years ago

fixed

  • Property svn:keywords set to Id
File size: 54.2 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 2781 2007-11-27 00:12:57Z 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
36import logging
37import Globals
38p_home = Globals.package_home(globals())
39i_home = Globals.INSTANCE_HOME
40
41ADDING_SHEDULED = "adding_sheduled"
42OBJECT_CREATED = "object_created"
43
44from interfaces import IWAeUPTable
45
46class AttributeHolder(object):
47    pass
48
49def dict2ob(dict):
50    ob = AttributeHolder()
51    for key, value in dict.items():
52        setattr(ob, key, value)
53    return ob
54
55class WAeUPTable(ZCatalog): ###(
56
57    implements(IWAeUPTable)
58    security = ClassSecurityInfo()
59    meta_type = None
60
61    def __init__(self,name=None):
62        if name ==  None:
63            name = self.name
64        ZCatalog.__init__(self,name)
65
66    def refreshCatalog(self, clear=0, pghandler=None): ###(
67        """ don't refresh for a normal table """
68
69        if self.REQUEST and self.REQUEST.RESPONSE:
70            self.REQUEST.RESPONSE.redirect(
71              URL1 +
72              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20refresh%20not%20implemented')
73
74###)
75
76    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None): ###(
77        """ clears the whole enchilada """
78
79        #if REQUEST and RESPONSE:
80        #    RESPONSE.redirect(
81        #      URL1 +
82        #      '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Clearing%20disabled')
83
84        self._catalog.clear()
85        if REQUEST and RESPONSE:
86            RESPONSE.redirect(
87              URL1 +
88              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20cleared')
89
90###)
91
92    def record2dict(self,fields,record): ###(
93        d = {}
94        for key in fields:
95            v = getattr(record, key, None)
96            if key == 'sex':
97                if v:
98                    v = 'F'
99                else:
100                    v = 'M'
101                d[key] = v
102            elif v:
103                if key == 'lga':
104                    v = self.portal_vocabularies.local_gov_areas.get(v)
105                elif key == 'aos':
106                    v = self.portal_vocabularies.aos.get(v)
107                d[key] = v
108            else:
109                d[key] = ''
110        return d
111
112###)
113
114    def addRecord(self, **data): ###(
115        # The uid is the same as "bed".
116        uid = data[self.key]
117        res = self.searchResults({"%s" % self.key : uid})
118        if len(res) > 0:
119            raise ValueError("More than one record with uid %s" % uid)
120        self.catalog_object(dict2ob(data), uid=uid)
121        return uid
122
123###)
124
125    def deleteRecord(self, uid):
126        self.uncatalog_object(uid)
127
128    def getRecordByKey(self,key):
129        if not key:
130            return None
131        res = self.evalAdvancedQuery(Eq(self.key,key))
132        if res:
133            return res[0]
134        return None
135
136    def searchAndSetRecord(self, **data):
137        raise NotImplemented
138
139    def modifyRecord(self, record=None, **data): ###(
140        #records = self.searchResults(uid=uid)
141        uid = data[self.key]
142        if record is None:
143            records = self.searchResults({"%s" % self.key : uid})
144            if len(records) > 1:
145                # Can not happen, but anyway...
146                raise ValueError("More than one record with uid %s" % uid)
147            if len(records) == 0:
148                raise KeyError("No record for uid %s" % uid)
149            record = records[0]
150        record_data = {}
151        for field in self.schema() + self.indexes():
152            record_data[field] = getattr(record, field)
153        # Add the updated data:
154        record_data.update(data)
155        self.catalog_object(dict2ob(record_data), uid)
156
157###)
158
159    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
160        if isinstance(name, str):
161            name =  (name,)
162        paths = self._catalog.uids.items()
163        i = 0
164        #import pdb;pdb.set_trace()
165        for p,rid in paths:
166            i += 1
167            metadata = self.getMetadataForRID(rid)
168            record_data = {}
169            for field in name:
170                record_data[field] = metadata.get(field)
171            uid = metadata.get(self.key)
172            self.catalog_object(dict2ob(record_data), uid, idxs=name,
173                                update_metadata=0)
174
175###)
176
177    security.declareProtected(ModifyPortalContent,"exportAllRecords") ###(
178    def exportAllRecords(self):
179        "export a WAeUPTable"
180        #import pdb;pdb.set_trace()
181        fields = [field for field in self.schema()]
182        format = ','.join(['"%%(%s)s"' % fn for fn in fields])
183        csv = []
184        csv.append(','.join(['"%s"' % fn for fn in fields]))
185        for uid in self._catalog.uids:
186            records = self.searchResults({"%s" % self.key : uid})
187            if len(records) > 1:
188                # Can not happen, but anyway...
189                raise ValueError("More than one record with uid %s" % uid)
190            if len(records) == 0:
191                raise KeyError("No record for uid %s" % uid)
192            rec = records[0]
193            csv.append(format % rec)
194        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
195        open("%s/import/%s-%s.csv" % (i_home,self.getId(),current),"w+").write('\n'.join(csv))
196
197###)
198
199    security.declareProtected(ModifyPortalContent,"dumpAll")###(
200    def dumpAll(self):
201        """dump all data in the table to a csv"""
202        member = self.portal_membership.getAuthenticatedMember()
203        logger = logging.getLogger('WAeUPTables.dump_%s' % self.__name__)
204        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
205        export_file = "%s/export/%s_%s.csv" % (i_home,self.__name__,current,)
206        print export_file
207        res_list = []
208        lines = []
209        if hasattr(self,"export_keys"):
210            fields = self.export_keys
211        else:
212            fields = []
213            for f in self.schema():
214                fields.append(f)
215        headline = ','.join(fields)
216        #open(export_file,"a").write(headline +'\n')
217        out = open(export_file,"wb")
218        out.write(headline +'\n')
219        out.close()
220        out = open(export_file,"a")
221        csv_writer = csv.DictWriter(out,fields,)
222        #format = '"%(' + ')s","%('.join(fields) + ')s"'
223        records = self()
224        nr2export = len(records)
225        logger.info('%s starts dumping, %s records to export' % (member,nr2export))
226        chunk = 2000
227        total = 0
228        start = DateTime.DateTime().timeTime()
229        start_chunk = DateTime.DateTime().timeTime()
230        for record in records:
231            not_all = False
232            d = self.record2dict(fields,record)
233            lines.append(d)
234            total += 1
235            if total and not total % chunk or total == len(records):
236                #open(export_file,"a").write('\n'.join(lines) +'\n')
237                csv_writer.writerows(lines)
238                anz = len(lines)
239                logger.info("wrote %(anz)d  total written %(total)d" % vars())
240                end_chunk = DateTime.DateTime().timeTime()
241                duration = end_chunk-start_chunk
242                per_record = duration/anz
243                till_now = end_chunk - start
244                avarage_per_record = till_now/total
245                estimated_end = DateTime.DateTime(start + avarage_per_record * nr2export)
246                estimated_end = estimated_end.strftime("%H:%M:%S")
247                logger.info('%(duration)4.1f, %(per_record)4.3f,end %(estimated_end)s' % vars())
248                start_chunk = DateTime.DateTime().timeTime()
249                lines = []
250        end = DateTime.DateTime().timeTime()
251        logger.info('total time %6.2f m' % ((end-start)/60))
252        import os
253        filename, extension = os.path.splitext(export_file)
254        from subprocess import call
255        msg = "wrote %(total)d records to %(export_file)s" % vars()
256        #try:
257        #    retcode = call('gzip %s' % (export_file),shell=True)
258        #    if retcode == 0:
259        #        msg = "wrote %(total)d records to %(export_file)s.gz" % vars()
260        #except OSError, e:
261        #    retcode = -99
262        #    logger.info("zip failed with %s" % e)
263        logger.info(msg)
264        args = {'portal_status_message': msg}
265        #url = self.REQUEST.get('URL1') + '?' + urlencode(args)
266        url = self.REQUEST.get('URL2')
267        return self.REQUEST.RESPONSE.redirect(url)
268    ###)
269
270    security.declarePrivate("_import_old") ###(
271    def _import_old(self,filename,schema,layout, mode,logger):
272        "import data from csv"
273        import transaction
274        import random
275        pm = self.portal_membership
276        member = pm.getAuthenticatedMember()
277        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
278        import_fn = "%s/import/%s.csv" % (i_home,filename)
279        imported_fn = "%s/import/%s_imported%s.csv" % (i_home,filename,current)
280        not_imported_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
281        start = True
282        tr_count = 1
283        total_imported = 0
284        total_not_imported = 0
285        total = 0
286        iname =  "%s" % filename
287        not_imported = []
288        imported = []
289        valid_records = []
290        invalid_records = []
291        d = {}
292        d['mode'] = mode
293        d['imported'] = total_imported
294        d['not_imported'] = total_not_imported
295        d['valid_records'] = valid_records
296        d['invalid_records'] = invalid_records
297        d['import_fn'] = import_fn
298        d['imported_fn'] = imported_fn
299        d['not_imported_fn'] = not_imported_fn
300        if schema is None:
301            em = 'No schema specified'
302            logger.error(em)
303            return d
304        if layout is None:
305            em = 'No layout specified'
306            logger.error(em)
307            return d
308        validators = {}
309        for widget in layout.keys():
310            try:
311                validators[widget] = layout[widget].validate
312            except AttributeError:
313                logger.info('%s has no validate attribute' % widget)
314                return d
315        # if mode == 'edit':
316        #     importer = self.importEdit
317        # elif mode == 'add':
318        #     importer = self.importAdd
319        # else:
320        #     importer = None
321        try:
322            items = csv.DictReader(open(import_fn,"rb"),
323                                   dialect="excel",
324                                   skipinitialspace=True)
325        except:
326            em = 'Error reading %s.csv' % filename
327            logger.error(em)
328            return d
329        #import pdb;pdb.set_trace()
330        for item in items:
331            if start:
332                start = False
333                logger.info('%s starts import from %s.csv' % (member,filename))
334                #import_keys = [k for k in item.keys() if not k.startswith('ignore')]
335                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb"),
336                                   dialect="excel",
337                                   skipinitialspace=True).next()
338                import_keys = [k for k in attrs if not (k.startswith('ignore') or k.isupper())]
339                diff2schema = set(import_keys).difference(set(schema.keys()))
340                diff2layout = set(import_keys).difference(set(layout.keys()))
341                if diff2layout:
342                    em = "not ignorable key(s) %s found in heading" % diff2layout
343                    logger.info(em)
344                    return d
345                s = ','.join(['"%s"' % fn for fn in import_keys])
346                open(not_imported_fn,"a").write(s + ',"Error"'+ '\n')
347                #s = '"id",' + s
348                open(imported_fn,"a").write(s + '\n')
349                format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
350                format_error = format + ',"%(Error)s"'
351                #format = '"%(id)s",'+ format
352                adapters = [MappingStorageAdapter(schema, item)]
353            dm = DataModel(item, adapters,context=self)
354            ds = DataStructure(data=item,datamodel=dm)
355            error_string = ""
356            #import pdb;pdb.set_trace()
357            for k in import_keys:
358                if not validators[k](ds,mode=mode):
359                    error_string += " %s : %s" % (k,ds.getError(k))
360            # if not error_string and importer:
361            #     item.update(dm)
362            #     item['id'],error = importer(item)
363            #     if error:
364            #         error_string += error
365            if error_string:
366                item['Error'] = error_string
367                invalid_records.append(dm)
368                not_imported.append(format_error % item)
369                total_not_imported += 1
370            else:
371                em = format % item
372                valid_records.append(dm)
373                imported.append(em)
374                #logger.info("%(total_imported)d of %(total)d %(em)s" % vars())
375                tr_count += 1
376                total_imported += 1
377            total += 1
378        if len(imported) > 0:
379            open(imported_fn,"a").write('\n'.join(imported))
380        if len(not_imported) > 0:
381            open(not_imported_fn,"a").write('\n'.join(not_imported))
382        #em = "Imported: %d, not imported: %d of total %d" % (total_imported,total_not_imported,total)
383        d['imported'] = total_imported
384        d['not_imported'] = total_not_imported
385        d['valid_records'] = valid_records
386        d['invalid_records'] = invalid_records
387        d['imported_fn'] = imported_fn
388        d['not_imported_fn'] = not_imported_fn
389        #logger.info(em)
390        return d
391    ###)
392
393    security.declarePrivate("_import") ###(
394    def _import_new(self,csv_items,schema, layout, mode,logger):
395        "import data from csv.Dictreader Instance"
396        start = True
397        tr_count = 1
398        total_imported = 0
399        total_not_imported = 0
400        total = 0
401        iname =  "%s" % filename
402        not_imported = []
403        valid_records = []
404        invalid_records = []
405        duplicate_records = []
406        d = {}
407        d['mode'] = mode
408        d['valid_records'] = valid_records
409        d['invalid_records'] = invalid_records
410        d['invalid_records'] = duplicate_records
411        # d['import_fn'] = import_fn
412        # d['imported_fn'] = imported_fn
413        # d['not_imported_fn'] = not_imported_fn
414        validators = {}
415        for widget in layout.keys():
416            try:
417                validators[widget] = layout[widget].validate
418            except AttributeError:
419                logger.info('%s has no validate attribute' % widget)
420                return d
421        for item in csv_items:
422            if start:
423                start = False
424                logger.info('%s starts import from %s.csv' % (member,filename))
425                #import_keys = [k for k in item.keys() if not k.startswith('ignore')]
426                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb")).next()
427                import_keys = [k for k in attrs if not (k.startswith('ignore') or k.isupper())]
428                diff2schema = set(import_keys).difference(set(schema.keys()))
429                diff2layout = set(import_keys).difference(set(layout.keys()))
430                if diff2layout:
431                    em = "not ignorable key(s) %s found in heading" % diff2layout
432                    logger.info(em)
433                    return d
434                # s = ','.join(['"%s"' % fn for fn in import_keys])
435                # open(not_imported_fn,"a").write(s + ',"Error"'+ '\n')
436                # #s = '"id",' + s
437                # open(imported_fn,"a").write(s + '\n')
438                # format = ','.join(['"%%(%s)s"' % fn for fn in import_keys])
439                # format_error = format + ',"%(Error)s"'
440                # #format = '"%(id)s",'+ format
441                adapters = [MappingStorageAdapter(schema, item)]
442            dm = DataModel(item, adapters,context=self)
443            ds = DataStructure(data=item,datamodel=dm)
444            error_string = ""
445            for k in import_keys:
446                if not validators[k](ds,mode=mode):
447                    error_string += " %s : %s" % (k,ds.getError(k))
448            if error_string:
449                item['Error'] = error_string
450                #invalid_records.append(dm)
451                invalid_records.append(item)
452                total_not_imported += 1
453            else:
454                em = format % item
455                valid_records.append(dm)
456                #logger.info("%(total_imported)d of %(total)d %(em)s" % vars())
457                tr_count += 1
458                total_imported += 1
459            total += 1
460        # if len(imported) > 0:
461        #     open(imported_fn,"a").write('\n'.join(imported))
462        # if len(not_imported) > 0:
463        #     open(not_imported_fn,"a").write('\n'.join(not_imported))
464        #em = "Imported: %d, not imported: %d of total %d" % (total_imported,total_not_imported,total)
465        d['imported'] = total_imported
466        d['not_imported'] = total_not_imported
467        d['valid_records'] = valid_records
468        d['invalid_records'] = invalid_records
469        return d
470    ###)
471
472    security.declarePublic("missingValue")###(
473    def missingValue(self):
474        from Missing import MV
475        return MV
476    ###)
477###)
478
479class AccommodationTable(WAeUPTable): ###(
480
481    meta_type = 'WAeUP Accommodation Tool'
482    name = "portal_accommodation"
483    key = "bed"
484    def __init__(self,name=None):
485        if name ==  None:
486            name = self.name
487        WAeUPTable.__init__(self, name)
488
489    def searchAndReserveBed(self, student_id,bed_type):
490        records = self.searchResults({'student' : student_id})
491        if len(records) > 0:
492            return -1,"Student with Id %s already booked bed %s." % (student_id,records[0].bed)
493
494        records = [r for r in self.searchResults({'bed_type' : bed_type}) if not r.student]
495        #import pdb;pdb.set_trace()
496        if len(records) == 0:
497            return -2,"No bed available"
498        rec = records[0]
499        self.modifyRecord(bed=rec.bed,student=student_id)
500        s_logger = logging.getLogger('WAeUPTables.AccommodationTable.searchAndReserveBed')
501        s_logger.info('%s reserved bed %s' % (student_id,rec.bed))
502        return 1,rec.bed
503
504
505InitializeClass(AccommodationTable)
506
507###)
508
509class PinTable(WAeUPTable): ###(
510    from ZODB.POSException import ConflictError
511    meta_type = 'WAeUP Pin Tool'
512    name = "portal_pins"
513    key = 'pin'
514    def __init__(self,name=None):
515        if name ==  None:
516            name = self.name
517        WAeUPTable.__init__(self, name)
518
519
520    def searchAndSetRecord(self, uid, student_id,prefix):
521
522        # The following line must be activated after resetting the
523        # the portal_pins table. This is to avoid duplicate entries
524        # and disable duplicate payments.
525
526        #student_id = student_id.upper()
527
528        #records = self.searchResults(student = student_id)
529        #if len(records) > 0 and prefix in ('CLR','APP'):
530        #    for r in records:
531        #        if r.pin != uid and r.prefix_batch.startswith(prefix):
532        #            return -2
533        records = self.searchResults({"%s" % self.key : uid})
534        if len(records) > 1:
535            # Can not happen, but anyway...
536            raise ValueError("More than one record with uid %s" % uid)
537        if len(records) == 0:
538            return -1,None
539        record = records[0]
540        if record.student == "":
541            record_data = {}
542            for field in self.schema() + self.indexes():
543                record_data[field] = getattr(record, field)
544            # Add the updated data:
545            record_data['student'] = student_id
546            try:
547                self.catalog_object(dict2ob(record_data), uid)
548                return 1,record
549            except ConflictError:
550                return 2,record
551        if record.student.upper() != student_id.upper():
552            return 0,record
553        if record.student.upper() == student_id.upper():
554            return 2,record
555        return -3,record
556InitializeClass(PinTable)
557###)
558
559class PumeResultsTable(WAeUPTable): ###(
560
561    meta_type = 'WAeUP PumeResults Tool'
562    name = "portal_pumeresults"
563    key = "jamb_reg_no"
564    def __init__(self,name=None):
565        if name ==  None:
566            name = self.name
567        WAeUPTable.__init__(self, name)
568
569
570InitializeClass(PumeResultsTable)
571
572###)
573
574class ApplicantsCatalog(WAeUPTable): ###(
575
576    meta_type = 'WAeUP Applicants Catalog'
577    name = "applicants_catalog"
578    key = "reg_no"
579    security = ClassSecurityInfo()
580    #export_keys = (
581    #               "reg_no",
582    #               "status",
583    #               "lastname",
584    #               "sex",
585    #               "date_of_birth",
586    #               "lga",
587    #               "email",
588    #               "phone",
589    #               "passport",
590    #               "entry_mode",
591    #               "pin",
592    #               "screening_type",
593    #               "registration_date",
594    #               "testdate",
595    #               "application_date",
596    #               "screening_date",
597    #               "faculty",
598    #               "department",
599    #               "course1",
600    #               "course2",
601    #               "course3",
602    #               "eng_score",
603    #               "subj1",
604    #               "subj1score",
605    #               "subj2",
606    #               "subj2score",
607    #               "subj3",
608    #               "subj3score",
609    #               "aggregate",
610    #               "course_admitted",
611    #               )
612
613    def __init__(self,name=None):
614        if name ==  None:
615            name = self.name
616        WAeUPTable.__init__(self, name)
617
618    security.declareProtected(ModifyPortalContent,"new_importCSV")###(
619    def new_importCSV(self,filename="JAMB_data",
620                  schema_id="application",
621                  layout_id="import_application",
622                  mode='add'):
623        """ import JAMB data """
624        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
625        pm = self.portal_membership
626        member = pm.getAuthenticatedMember()
627        logger = logging.getLogger('WAeUPTables.ApplicantsCatalog.importCSV')
628        lock_fn = "%s/import/%s_import_lock" % (i_home,filename)
629        import_fn = "%s/import/%s.csv" % (i_home,filename)
630        if mode not in ('add','edit'):
631            logger.info("invalid mode: %s" % mode)
632        if os.path.exists(lock_fn):
633            logger.info("import of %(import_fn)s already in progress" % vars())
634            return
635        lock_file = open(lock_fn,"w")
636        lock_file.write("%(current)s \n" % vars())
637        lock_file.close()
638        invalid_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
639        duplicate_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
640        stool = getToolByName(self, 'portal_schemas')
641        ltool = getToolByName(self, 'portal_layouts')
642        schema = stool._getOb(schema_id)
643        if schema is None:
644            em = 'No such schema %s' % schema_id
645            logger.error(em)
646            return
647        for postfix in ('_import',''):
648            layout_name = "%(layout_id)s%(postfix)s" % vars()
649            if hasattr(ltool,layout_name):
650                break
651        layout = ltool._getOb(layout_name)
652        if layout is None:
653            em = 'No such layout %s' % layout_id
654            logger.error(em)
655            return
656        try:
657            csv_file = csv.DictReader(open(import_fn,"rb"))
658        except:
659            em = 'Error reading %s.csv' % filename
660            logger.error(em)
661            return
662        d = self._import_new(csv_items,schema,layout,mode,logger)
663        imported = []
664        edited = []
665        duplicates = []
666        not_found = []
667        if len(d['valid_records']) > 0:
668            for record in d['valid_records']:
669                #import pdb;pdb.set_trace()
670                if mode == "add":
671                    try:
672                        self.addRecord(**dict(record.items()))
673                        imported.append(**dict(record.items()))
674                        logger.info("added %s" % record.items())
675                    except ValueError:
676                        dupplicate.append(**dict(record.items()))
677                        logger.info("duplicate %s" % record.items())
678                elif mode == "edit":
679                    try:
680                        self.modifyRecord(**dict(record.items()))
681                        edited.append(**dict(record.items()))
682                        logger.info("edited %s" % record.items())
683                    except KeyError:
684                        not_found.append(**dict(record.items()))
685                        logger.info("not found %s" % record.items())
686        invalid = d['invalid_records']
687        for itype in ("imported","edited","not_found","duplicate","invalid"):
688            outlist = locals[itype]
689            if len(outlist):
690                d = {}
691                for k in outlist[0].keys():
692                    d[k] = k
693                outlist[0] = d
694                outfile = open("file_name_%s" % itype,'w')
695                csv.DictWriter(outfile,outlist[0].keys()).writerows(outlist)
696                logger.info("wrote %(itype)s records to %(, written to %(not_imported_fn)s" % d)
697###)
698
699    security.declareProtected(ModifyPortalContent,"importCSV")###(
700    def importCSV(self,filename="JAMB_data",
701                  schema_id="application",
702                  layout_id="application_pce",
703                  mode='add'):
704        """ import JAMB data """
705        stool = getToolByName(self, 'portal_schemas')
706        ltool = getToolByName(self, 'portal_layouts')
707        schema = stool._getOb(schema_id)
708        if schema is None:
709            em = 'No such schema %s' % schema_id
710            logger.error(em)
711            return
712        layout = ltool._getOb(layout_id)
713        if layout is None:
714            em = 'No such layout %s' % layout_id
715            logger.error(em)
716            return
717        logger = logging.getLogger('WAeUPTables.ApplicantsCatalog.importCSV')
718        d = self._import_old(filename,schema,layout,mode,logger)
719        if len(d['valid_records']) > 0:
720            for record in d['valid_records']:
721                #import pdb;pdb.set_trace()
722                if mode == "add":
723                    self.addRecord(**dict(record.items()))
724                    logger.info("added %s" % record.items())
725                elif mode == "edit":
726                    self.modifyRecord(**dict(record.items()))
727                    logger.info("edited %s" % record.items())
728                else:
729                    logger.info("invalid mode: %s" % mode)
730        logger.info("%(mode)sed %(imported)d records, invalid written to %(not_imported_fn)s" % d)
731    ###)
732
733InitializeClass(ApplicantsCatalog)
734
735###)
736
737class StudentsCatalog(WAeUPTable): ###(
738    security = ClassSecurityInfo()
739
740    meta_type = 'WAeUP Students Catalog'
741    name = "students_catalog"
742    key = "id"
743    affected_types = {   ###(
744                      'StudentApplication':
745                      {'id': 'application',
746                       'fields':
747                       ('jamb_reg_no',
748                        'entry_mode',
749                        #'entry_level',
750                        'entry_session',
751                       )
752                      },
753                      'StudentClearance':
754                      {'id': 'clearance',
755                       'fields':
756                       ('matric_no',
757                        'lga',
758                       )
759                      },
760                      'StudentPersonal':
761                      {'id': 'personal',
762                       'fields':
763                       ('name',
764                        'sex',
765                        'perm_address',
766                        'email',
767                        'phone',
768                       )
769                      },
770                      'StudentStudyCourse':
771                      {'id': 'study_course',
772                       'fields':
773                       ('course', # study_course
774                        'faculty', # from certificate
775                        'department', # from certificate
776                        'end_level', # from certificate
777                        'level', # current_level
778                        'mode',  # current_mode
779                        'session', # current_session
780                        'verdict', # current_verdict
781                       )
782                      },
783                     }
784    ###)
785
786    def __init__(self,name=None):
787        if name ==  None:
788            name = self.name
789        WAeUPTable.__init__(self, name)
790        return
791
792    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None):
793        """ clears the whole enchilada """
794        self._catalog.clear()
795
796        if REQUEST and RESPONSE:
797            RESPONSE.redirect(
798              URL1 +
799              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Cleared')
800
801    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
802        """ clear the catalog, then re-index everything """
803
804        elapse = time.time()
805        c_elapse = time.clock()
806
807        pgthreshold = self._getProgressThreshold()
808        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
809        self.refreshCatalog(clear=1, pghandler=handler)
810
811        elapse = time.time() - elapse
812        c_elapse = time.clock() - c_elapse
813
814        RESPONSE.redirect(
815            URL1 +
816            '/manage_catalogAdvanced?manage_tabs_message=' +
817            urllib.quote('Catalog Updated \n'
818                         'Total time: %s\n'
819                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
820    ###)
821
822    def fill_certificates_dict(self): ###(
823        "return certificate data in  dict"
824        certificates_brains = self.portal_catalog(portal_type ='Certificate')
825        d = {}
826        for cb in certificates_brains:
827            certificate_doc = cb.getObject().getContent()
828            cb_path = cb.getPath().split('/')
829            ld = {}
830            ld['faculty'] = cb_path[-4]
831            ld['department'] = cb_path[-3]
832            ld['end_level'] = getattr(certificate_doc,'end_level','999')
833            d[cb.getId] = ld
834        return d
835    ###)
836
837    def get_from_doc_department(self,doc,cached_data={}): ###(
838        "return the students department"
839        if doc is None:
840            return None
841        if cached_data.has_key(doc.study_course):
842            return cached_data[doc.study_course]['department']
843        certificate_res = self.portal_catalog(id = doc.study_course)
844        if len(certificate_res) != 1:
845            return None
846        return certificate_res[0].getPath().split('/')[-3]
847
848    def get_from_doc_faculty(self,doc,cached_data={}):
849        "return the students faculty"
850        if doc is None:
851            return None
852        if cached_data.has_key(doc.study_course):
853            return cached_data[doc.study_course]['faculty']
854        certificate_res = self.portal_catalog(id = doc.study_course)
855        if len(certificate_res) != 1:
856            return None
857        return certificate_res[0].getPath().split('/')[-4]
858
859    def get_from_doc_end_level(self,doc,cached_data={}):
860        "return the students end_level"
861        if doc is None:
862            return None
863        if cached_data.has_key(doc.study_course):
864            return cached_data[doc.study_course]['end_level']
865        certificate_res = self.portal_catalog(id = doc.study_course)
866        if len(certificate_res) != 1:
867            return None
868        return getattr(certificate_res[0].getObject().getContent(),'end_level','unknown')
869
870    def get_from_doc_level(self,doc,cached_data={}):
871        "return the students level"
872        if doc is None:
873            return None
874        return getattr(doc,'current_level',None)
875
876    def get_from_doc_mode(self,doc,cached_data={}):
877        "return the students mode"
878        if doc is None:
879            return None
880        cm = getattr(doc,'current_mode',None)
881        return cm
882
883
884    def get_from_doc_session(self,doc,cached_data={}):
885        "return the students current_session"
886        if doc is None:
887            return None
888        return getattr(doc,'current_session',None)
889
890    def get_from_doc_entry_session(self,doc,cached_data={}):
891        "return the students entry_session"
892        if doc is None:
893            return None
894        es = getattr(doc,'entry_session',None)
895        if es is not None and len(es) == 2:
896            return es
897        try:
898            digit = int(doc.jamb_reg_no[0])
899        except:
900            return "-1"
901        if digit < 8:
902            return "0%c" % doc.jamb_reg_no[0]
903        return "9%c" % doc.jamb_reg_no[0]
904
905    def get_from_doc_course(self,doc,cached_data={}):
906        "return the students study_course"
907        if doc is None:
908            return None
909        return getattr(doc,'study_course',None)
910
911    def get_from_doc_name(self,doc,cached_data={}):
912        "return the students name from the personal"
913        if doc is None:
914            return None
915        return "%s %s %s" % (doc.firstname,doc.middlename,doc.lastname)
916
917    def get_from_doc_verdict(self,doc,cached_data={}):
918        "return the students study_course"
919        if doc is None:
920            return None
921        return getattr(doc,'current_verdict',None)
922    ###)
923
924    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
925        if isinstance(name, str):
926            name = (name,)
927        reindextypes = {}
928        reindex_special = []
929        for n in name:
930            if n in ("review_state","registered_courses"):
931                reindex_special.append(n)
932            else:
933                for pt in self.affected_types.keys():
934                    if n in self.affected_types[pt]['fields']:
935                        if reindextypes.has_key(pt):
936                            reindextypes[pt].append(n)
937                        else:
938                            reindextypes[pt]= [n]
939                        break
940        cached_data = {}
941        if set(name).intersection(set(('faculty','department','end_level'))):
942            cached_data = self.fill_certificates_dict()
943        students = self.portal_catalog(portal_type="Student")
944        if hasattr(self,'portal_catalog_real'):
945            aq_portal = self.portal_catalog_real.evalAdvancedQuery
946        else:
947            aq_portal = self.portal_catalog.evalAdvancedQuery
948        num_objects = len(students)
949        if pghandler:
950            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
951        noattr = set(('StudentClearance','StudentPersonal')) & set(reindextypes.keys())
952        #import pdb;pdb.set_trace()
953        for i in xrange(num_objects):
954            if pghandler: pghandler.report(i)
955            student_brain = students[i]
956            student_object = student_brain.getObject()
957            # query = Eq('path',student_brain.getPath())
958            # sub_brains_list = aq_portal(query)
959            # sub_brains = {}
960            # for sub_brain in sub_brains_list:
961            #     sub_brains[sub_brain.portal_type] = sub_brain
962            # student_path = student_brain.getPath()
963            data = {}
964            modified = False
965            sid = data['id'] = student_brain.getId
966            if reindex_special and 'review_state' in reindex_special:
967                modified = True
968                data['review_state'] = student_brain.review_state
969            sub_objects = False
970            for pt in reindextypes.keys():
971                modified = True
972                try:
973                    doc = getattr(student_object,self.affected_types[pt]['id']).getContent()
974                    #doc = sub_brains[pt].getObject().getContent()
975                    # path = "%s/%s" % (student_path,self.affected_types[pt]['id'])
976                    # doc = self.unrestrictedTraverse(path).getContent()
977                    sub_objects = True
978                except:
979                    continue
980                for field in set(name).intersection(self.affected_types[pt]['fields']):
981                    if hasattr(self,'get_from_doc_%s' % field):
982                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc,
983                                                                              cached_data=cached_data)
984                    else:
985                        data[field] = getattr(doc,field)
986            if not sub_objects and noattr:
987                import_res = self.returning_import(id = sid)
988                if not import_res:
989                    continue
990                import_record = import_res[0]
991                data['matric_no'] = import_record.matric_no
992                data['sex'] = import_record.Sex == 'F'
993                data['name'] = "%s %s %s" % (import_record.Firstname,
994                                             import_record.Middlename,
995                                             import_record.Lastname)
996                data['jamb_reg_no'] = import_record.Entryregno
997            #if reindex_special and 'registered_courses' in reindex_special:
998            #    try:
999            #        study_course = getattr(student_object,"study_course")
1000            #        level_ids = study_course.objectIds()
1001            #    except:
1002            #        continue
1003            #    if not level_ids:
1004            #        continue
1005            #    modified = True
1006            #    level_ids.sort()
1007            #    course_ids = getattr(study_course,level_ids[-1]).objectIds()
1008            #    courses = []
1009            #    for c in course_ids:
1010            #        if c.endswith('_co'):
1011            #            courses.append(c[:-3])
1012            #        else:
1013            #            courses.append(c)
1014            #    data['registered_courses'] = courses
1015            if modified:
1016                self.modifyRecord(**data)
1017        if pghandler: pghandler.finish()
1018    ###)
1019
1020    def refreshCatalog(self, clear=0, pghandler=None): ###(
1021        """ re-index everything we can find """
1022        students_folder = self.portal_url.getPortalObject().campus.students
1023        if clear:
1024            self._catalog.clear()
1025        students = self.portal_catalog(portal_type="Student")
1026        num_objects = len(students)
1027        cached_data = self.fill_certificates_dict()
1028        if pghandler:
1029            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1030        for i in xrange(num_objects):
1031            if pghandler: pghandler.report(i)
1032            student_brain = students[i]
1033            spath = student_brain.getPath()
1034            student_object = student_brain.getObject()
1035            data = {}
1036            sid = data['id'] = student_brain.getId
1037            data['review_state'] = student_brain.review_state
1038            sub_objects = False
1039            for pt in self.affected_types.keys():
1040                modified = True
1041                try:
1042                    doc = getattr(student_object,self.affected_types[pt]['id']).getContent()
1043                    sub_objects = True
1044                except:
1045                    #from pdb import set_trace;set_trace()
1046                    continue
1047                for field in self.affected_types[pt]['fields']:
1048                    if hasattr(self,'get_from_doc_%s' % field):
1049                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc,
1050                                                                              cached_data=cached_data)
1051                    else:
1052                        data[field] = getattr(doc,field,None)
1053            if not sub_objects:
1054                import_res = self.returning_import(id = sid)
1055                if not import_res:
1056                    continue
1057                import_record = import_res[0]
1058                data['matric_no'] = import_record.matric_no
1059                data['sex'] = import_record.Sex == 'F'
1060                data['name'] = "%s %s %s" % (import_record.Firstname,
1061                                             import_record.Middlename,
1062                                             import_record.Lastname)
1063                data['jamb_reg_no'] = import_record.Entryregno
1064            self.addRecord(**data)
1065        if pghandler: pghandler.finish()
1066    ###)
1067
1068    security.declarePrivate('notify_event_listener') ###(
1069    def notify_event_listener(self,event_type,object,infos):
1070        "listen for events"
1071        if not infos.has_key('rpath'):
1072            return
1073        pt = getattr(object,'portal_type',None)
1074        mt = getattr(object,'meta_type',None)
1075        students_catalog = self
1076        data = {}
1077        if pt == 'Student' and\
1078           mt == 'CPS Proxy Folder' and\
1079           event_type.startswith('workflow'):
1080            data['id'] = object.getId()
1081            data['review_state'] = self.portal_workflow.getInfoFor(object,'review_state',None)
1082            students_catalog.modifyRecord(**data)
1083            return
1084        rpl = infos['rpath'].split('/')
1085        if pt == 'Student' and mt == 'CPS Proxy Folder':
1086            student_id = object.id
1087            if event_type == "sys_add_object":
1088                try:
1089                    self.addRecord(id = student_id)
1090                except ValueError:
1091                    pass
1092                return
1093            elif event_type == 'sys_del_object':
1094                self.deleteRecord(student_id)
1095        if pt not in self.affected_types.keys():
1096            return
1097        if event_type not in ('sys_modify_object'):
1098            return
1099        if mt == 'CPS Proxy Folder':
1100            return
1101        for field in self.affected_types[pt]['fields']:
1102            if hasattr(self,'get_from_doc_%s' % field):
1103                data[field] = getattr(self,'get_from_doc_%s' % field)(object)
1104            else:
1105                data[field] = getattr(object,field)
1106        data['id'] = rpl[2]
1107        self.modifyRecord(**data)
1108    ###)
1109
1110
1111InitializeClass(StudentsCatalog)
1112
1113###)
1114
1115class CoursesCatalog(WAeUPTable): ###(
1116    security = ClassSecurityInfo()
1117
1118    meta_type = 'WAeUP Courses Catalog'
1119    name =  "courses_catalog"
1120    key = "code"
1121    def __init__(self,name=None):
1122        if name ==  None:
1123            name =  self.name
1124        WAeUPTable.__init__(self, name)
1125
1126    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
1127        """ clear the catalog, then re-index everything """
1128
1129        elapse = time.time()
1130        c_elapse = time.clock()
1131
1132        pgthreshold = self._getProgressThreshold()
1133        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
1134        self.refreshCatalog(clear=1, pghandler=handler)
1135
1136        elapse = time.time() - elapse
1137        c_elapse = time.clock() - c_elapse
1138
1139        RESPONSE.redirect(
1140            URL1 +
1141            '/manage_catalogAdvanced?manage_tabs_message=' +
1142            urllib.quote('Catalog Updated \n'
1143                         'Total time: %s\n'
1144                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
1145    ###)
1146
1147    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
1148        if isinstance(name, str):
1149            name = (name,)
1150        courses = self.portal_catalog(portal_type="Course")
1151        num_objects = len(courses)
1152        if pghandler:
1153            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1154        for i in xrange(num_objects):
1155            if pghandler: pghandler.report(i)
1156            course_brain = courses[i]
1157            course_object = course_brain.getObject()
1158            pl = course_brain.getPath().split('/')
1159            data = {}
1160            cid = data[self.key] = course_brain.getId
1161            data['faculty'] = pl[-4]
1162            data['department'] = pl[-3]
1163            doc = course_object.getContent()
1164            for field in name:
1165                if field not in (self.key,'faculty','department'):
1166                    data[field] = getattr(doc,field)
1167            self.modifyRecord(**data)
1168        if pghandler: pghandler.finish()
1169    ###)
1170
1171    def refreshCatalog(self, clear=0, pghandler=None): ###(
1172        """ re-index everything we can find """
1173        if clear:
1174            self._catalog.clear()
1175        courses = self.portal_catalog(portal_type="Course")
1176        num_objects = len(courses)
1177        if pghandler:
1178            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1179        #from pdb import set_trace;set_trace()
1180        for i in xrange(num_objects):
1181            if pghandler: pghandler.report(i)
1182            course_brain = courses[i]
1183            course_doc = course_brain.getObject().getContent()
1184            pl = course_brain.getPath().split('/')
1185            data = {}
1186            for field in self.schema():
1187                data[field] = getattr(course_doc,field,None)
1188            data[self.key] = course_brain.getId
1189            ai = pl.index('academics')
1190            data['faculty'] = pl[ai +1]
1191            data['department'] = pl[ai +2]
1192            if clear:
1193                self.addRecord(**data)
1194            else:
1195                self.modifyRecord(**data)
1196        if pghandler: pghandler.finish()
1197    ###)
1198
1199    security.declarePrivate('notify_event_listener') ###(
1200    def notify_event_listener(self,event_type,object,infos):
1201        "listen for events"
1202        if not infos.has_key('rpath'):
1203            return
1204        pt = getattr(object,'portal_type',None)
1205        mt = getattr(object,'meta_type',None)
1206        if pt != 'Course':
1207            return
1208        data = {}
1209        rpl = infos['rpath'].split('/')
1210        if event_type not in ("sys_add_object","sys_modify_object","sys_del_object"):
1211            return
1212        course_id = object.getId()
1213        data[self.key] = course_id
1214        if event_type == "sys_add_object" and mt == 'CPS Proxy Folder':
1215            try:
1216                self.addRecord(**data)
1217            except ValueError:
1218                return
1219            course_id = object.getId()
1220            doc = object.getContent()
1221            if doc is None:
1222                return
1223            for field in self.schema():
1224                data[field] = getattr(doc,field,None)
1225            data[self.key] = course_id
1226            ai = rpl.index('academics')
1227            data['faculty'] = rpl[ai +1]
1228            data['department'] = rpl[ai +2]
1229            self.modifyRecord(**data)
1230            return
1231        if event_type == "sys_del_object":
1232            self.deleteRecord(course_id)
1233            return
1234        if event_type == "sys_modify_object" and mt == 'Course':
1235            #from pdb import set_trace;set_trace()
1236            for field in self.schema():
1237                data[field] = getattr(object,field,None)
1238            course_id = object.aq_parent.getId()
1239            data[self.key] = course_id
1240            ai = rpl.index('academics')
1241            data['faculty'] = rpl[ai +1]
1242            data['department'] = rpl[ai +2]
1243            self.modifyRecord(**data)
1244    ###)
1245
1246
1247InitializeClass(CoursesCatalog)
1248###)
1249
1250class CourseResults(WAeUPTable): ###(
1251    security = ClassSecurityInfo()
1252
1253    meta_type = 'WAeUP Results Catalog'
1254    name = "course_results"
1255    key = "key" #student_id + level + course_id
1256    def __init__(self,name=None):
1257        if name ==  None:
1258            name = self.name
1259        WAeUPTable.__init__(self, name)
1260        self._queue = []
1261
1262    def addMultipleRecords(self, records): ###(
1263        """add many records"""
1264        added_keys = []
1265        for data in records:
1266            uid = key = "%(student_id)s|%(level_id)s|%(course_id)s" % data
1267            data['%s' % self.key] = uid
1268            res = self.searchResults({"%s" % self.key : uid})
1269            if len(res) > 0:
1270                raise ValueError("More than one record with uid %s" % uid)
1271            self.catalog_object(dict2ob(data), uid=uid)
1272        return uid
1273    ###)
1274
1275    def deleteResultsHere(self,level_id,student_id): ###(
1276        #import pdb;pdb.set_trace()
1277        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1278        course_results = self.course_results.evalAdvancedQuery(query)
1279        for result in course_results:
1280            self.deleteRecord(result.key)
1281    ###)
1282
1283    def moveResultsHere(self,level,student_id): ###(
1284        #import pdb;pdb.set_trace()
1285        level_id = level.getId()
1286        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1287        course_results = self.course_results.evalAdvancedQuery(query)
1288        existing_courses = [cr.code for cr in course_results]
1289        to_delete = []
1290        for code,obj in level.objectItems():
1291            to_delete.append(code)
1292            carry_over = False
1293            if code.endswith('_co'):
1294                carry_over = True
1295                code  = code[:-3]
1296            if code in existing_courses:
1297                continue
1298            course_result_doc = obj.getContent()
1299            data = {}
1300            course_id = code
1301            for field in self.schema():
1302                data[field] = getattr(course_result_doc,field,'')
1303            data['key'] = key = "%(student_id)s|%(level_id)s|%(course_id)s" % vars()
1304            data['student_id'] = student_id
1305            data['level_id'] = level_id
1306            session_id = self.getLevelSession(level.getContent(),student_id,level_id)
1307            data['session_id'] = session_id
1308            #data['queue_status'] = OBJECT_CREATED
1309            data['code'] = course_id
1310            data['carry_over'] = carry_over
1311            self.catalog_object(dict2ob(data), uid=key)
1312        level.manage_delObjects(to_delete)
1313    ###)
1314
1315    def getCourses(self,student_id,level_id): ###(
1316        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1317        course_results = self.course_results.evalAdvancedQuery(query)
1318        carry_overs = []
1319        normal1 = []
1320        normal2 = []
1321        normal3 = []
1322        total_credits = 0
1323        gpa = 0
1324        for brain in course_results:
1325            d = {}
1326
1327            for field in self.schema():
1328                d[field] = getattr(brain,field,'')
1329
1330            d['weight'] = ''
1331            d['grade'] = ''
1332            d['score'] = ''
1333
1334            if str(brain.credits).isdigit():
1335                credits = int(brain.credits)
1336                total_credits += credits
1337                score = getattr(brain,'score',0)
1338                if score and str(score).isdigit() and int(score) > 0:
1339                    score = int(score)
1340                    grade,weight = self.getGradesFromScore(score)
1341                    gpa += weight * credits
1342                    d['weight'] = weight
1343                    d['grade'] = grade
1344                    d['score'] = score
1345            d['coe'] = ''
1346            if brain.core_or_elective:
1347                d['coe'] = 'Core'
1348            elif brain.core_or_elective == False:
1349                d['coe'] = 'Elective'
1350            id = code = d['id'] = brain.code
1351            d['code'] = code
1352            course = self.courses_catalog.evalAdvancedQuery(Eq('code',code))[0]
1353            d['title'] = course.title
1354
1355            # The courses_catalog contains strings and integers in its semester field.
1356            # Maybe this can be fixed by reindexing the catalog. The schema of course says 'CPS Int Field'.
1357            d['semester'] = str(course.semester)
1358            if brain.carry_over:
1359                d['coe'] = 'CO'
1360                carry_overs.append(d)
1361            else:
1362                if d['semester'] == '1':
1363                    normal1.append(d)
1364
1365                elif d['semester'] == '2':
1366                    normal2.append(d)
1367                else:
1368                    normal3.append(d)
1369        #normal.sort(cmp=lambda x,y: cmp("%(semester)s%(code)s" % x,
1370        #                                "%(semester)s%(code)s" % y))
1371        carry_overs.sort(cmp=lambda x,y: cmp("%(semester)s%(code)s" % x,
1372                                             "%(semester)s%(code)s" % y))
1373        return total_credits,gpa,carry_overs,normal1,normal2,normal3
1374    ###)
1375
1376InitializeClass(CourseResults)
1377###)
1378
1379class OnlinePaymentsImport(WAeUPTable): ###(
1380
1381    meta_type = 'WAeUP Online Payment Transactions'
1382    name = "online_payments_import"
1383    key = "order_id"
1384    def __init__(self,name=None):
1385        if name ==  None:
1386            name = self.name
1387        WAeUPTable.__init__(self, name)
1388
1389
1390InitializeClass(OnlinePaymentsImport)
1391###)
1392
1393class ReturningImport(WAeUPTable): ###(
1394
1395    meta_type = 'Returning Import Table'
1396    name = "returning_import"
1397    key = "matric_no"
1398    def __init__(self,name=None):
1399        if name ==  None:
1400            name = self.name
1401        WAeUPTable.__init__(self, name)
1402
1403
1404InitializeClass(ReturningImport)
1405###)
1406
1407class ResultsImport(WAeUPTable): ###(
1408
1409    meta_type = 'Results Import Table'
1410    name = "results_import"
1411    key = "key"
1412    def __init__(self,name=None):
1413        if name ==  None:
1414            name = self.name
1415        WAeUPTable.__init__(self, name)
1416
1417
1418InitializeClass(ResultsImport)
1419
1420###)
1421
1422class PaymentsCatalog(WAeUPTable): ###(
1423    security = ClassSecurityInfo()
1424
1425    meta_type = 'WAeUP Payments Catalog'
1426    name = "students_catalog"
1427    key = "key"
1428    def __init__(self,name=None):
1429        if name ==  None:
1430            name = self.name
1431        WAeUPTable.__init__(self, name)
1432
1433    security.declarePrivate('notify_event_listener') ###(
1434    def notify_event_listener(self,event_type,object,infos):
1435        "listen for events"
1436        if not infos.has_key('rpath'):
1437            return
1438        pt = getattr(object,'portal_type',None)
1439        mt = getattr(object,'meta_type',None)
1440        data = {}
1441        if pt == 'Payment' and mt == 'CPS Proxy Folder':
1442            order_id = object.id
1443            if event_type == "sys_add_object":
1444                try:
1445                    self.addRecord(order_id = order_id)
1446                except ValueError:
1447                    pass
1448                return
1449            elif event_type == 'sys_del_object':
1450                self.deleteRecord(order_id)
1451        if pt != 'Payment' or mt == 'CPS Proxy Folder':
1452            return
1453        if event_type not in ('sys_modify_object'):
1454            return
1455        for field in self.schema():
1456            data[field] = getattr(object,field)
1457        rpl = infos['rpath'].split('/')
1458        student_id = rpl[-3]
1459        data['student_id'] = student_id
1460        self.modifyRecord(**data)
1461    ###)
1462
1463
1464InitializeClass(PaymentsCatalog)
1465
1466###)
1467
1468# BBB:
1469AccomodationTable = AccommodationTable
Note: See TracBrowser for help on using the repository browser.