source: WAeUP_SRP/trunk/WAeUPTables.py @ 2184

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

add only upper-case reg numbers to portal_pins table

see inline comments and also
comment 09/06/07 16:40:52 in ticket #328

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