source: WAeUP_SRP/base/WAeUPTables.py @ 2425

Last change on this file since 2425 was 2396, checked in by joachim, 17 years ago

added remove_students

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