source: WAeUP_SRP/trunk/WAeUPTables.py @ 2097

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

add applicants_catalog

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