source: WAeUP_SRP/trunk/WAeUPTables.py @ 2134

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

select widgets for courses

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