source: WAeUP_SRP/base/WAeUPTables.py @ 2356

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

ok, this works

  • Property svn:keywords set to Id
File size: 61.8 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 2192 2007-09-11 12:48:10Z henrik $
20
21from zope.interface import implements
22from Globals import InitializeClass
23from Products.ZCatalog.ZCatalog import ZCatalog
24from Products.ZCatalog.ProgressHandler import ZLogHandler
25from AccessControl import ClassSecurityInfo
26from Products.CMFCore.permissions import ModifyPortalContent
27from Products.CMFCore.utils import getToolByName
28from Products.CMFCore.CatalogTool import CatalogTool
29from Products.CPSSchemas.StorageAdapter import MappingStorageAdapter
30from Products.CPSSchemas.DataStructure import DataStructure
31from Products.CPSSchemas.DataModel import DataModel
32from Products.AdvancedQuery import Eq, Between, Le,In
33import urllib
34import DateTime,time
35import csv,re
36import logging
37import Globals
38p_home = Globals.package_home(globals())
39i_home = Globals.INSTANCE_HOME
40
41ADDING_SHEDULED = "adding_sheduled"
42OBJECT_CREATED = "object_created"
43
44from interfaces import IWAeUPTable
45
46class AttributeHolder(object):
47    pass
48
49def dict2ob(dict):
50    ob = AttributeHolder()
51    for key, value in dict.items():
52        setattr(ob, key, value)
53    return ob
54
55class WAeUPTable(ZCatalog): ###(
56
57    implements(IWAeUPTable)
58    security = ClassSecurityInfo()
59    meta_type = None
60
61    def __init__(self,name=None):
62        if name ==  None:
63            name = self.name
64        ZCatalog.__init__(self,name)
65
66    def refreshCatalog(self, clear=0, pghandler=None): ###(
67        """ don't refresh for a normal table """
68
69        if self.REQUEST and self.REQUEST.RESPONSE:
70            self.REQUEST.RESPONSE.redirect(
71              URL1 +
72              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20refresh%20not%20implemented')
73
74###)
75
76    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None): ###(
77        """ clears the whole enchilada """
78
79        #if REQUEST and RESPONSE:
80        #    RESPONSE.redirect(
81        #      URL1 +
82        #      '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Clearing%20disabled')
83
84        self._catalog.clear()
85        if REQUEST and RESPONSE:
86            RESPONSE.redirect(
87              URL1 +
88              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20cleared')
89
90###)
91
92    def record2dict(self,fields,record):
93        d = {}
94        for key in fields:
95            v = getattr(record, key, None)
96            if key == 'sex':
97                if v:
98                    v = 'F'
99                else:
100                    v = 'M'
101                d[key] = v
102            elif v:
103                if key == 'lga':
104                    v = self.portal_vocabularies.local_gov_areas.get(v)
105                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###)
462
463class AccommodationTable(WAeUPTable): ###(
464
465    meta_type = 'WAeUP Accommodation Tool'
466    name = "portal_accommodation"
467    key = "bed"
468    def __init__(self,name=None):
469        if name ==  None:
470            name = self.name
471        WAeUPTable.__init__(self, name)
472
473    def searchAndReserveBed(self, student_id,bed_type):
474        records = self.searchResults({'student' : student_id})
475        if len(records) > 0:
476            return -1,"Student with Id %s already booked bed %s." % (student_id,records[0].bed)
477
478        records = [r for r in self.searchResults({'bed_type' : bed_type}) if not r.student]
479        #import pdb;pdb.set_trace()
480        if len(records) == 0:
481            return -2,"No bed available"
482        rec = records[0]
483        self.modifyRecord(bed=rec.bed,student=student_id)
484        s_logger = logging.getLogger('WAeUPTables.AccommodationTable.searchAndReserveBed')
485        s_logger.info('%s reserved bed %s' % (student_id,rec.bed))
486        return 1,rec.bed
487
488
489InitializeClass(AccommodationTable)
490
491###)
492
493class PinTable(WAeUPTable): ###(
494    from ZODB.POSException import ConflictError
495    meta_type = 'WAeUP Pin Tool'
496    name = "portal_pins"
497    key = 'pin'
498    def __init__(self,name=None):
499        if name ==  None:
500            name = self.name
501        WAeUPTable.__init__(self, name)
502
503
504    def searchAndSetRecord(self, uid, student_id,prefix):
505
506        # The following line must be activated after resetting the
507        # the portal_pins table. This is to avoid duplicate entries
508        # and disable duplicate payments.
509
510        #student_id = student_id.upper()
511
512        records = self.searchResults(student = student_id)
513        if len(records) > 0 and prefix in ('CLR','APP'):
514            for r in records:
515                if r.pin != uid and r.prefix_batch.startswith(prefix):
516                    return -2
517        records = self.searchResults({"%s" % self.key : uid})
518        if len(records) > 1:
519            # Can not happen, but anyway...
520            raise ValueError("More than one record with uid %s" % uid)
521        if len(records) == 0:
522            return -1
523        record = records[0]
524        if record.student == "":
525            record_data = {}
526            for field in self.schema() + self.indexes():
527                record_data[field] = getattr(record, field)
528            # Add the updated data:
529            record_data['student'] = student_id
530            try:
531                self.catalog_object(dict2ob(record_data), uid)
532                return 1
533            except ConflictError:
534                return 2
535        if record.student.upper() != student_id.upper():
536            return 0
537        if record.student.upper() == student_id.upper():
538            return 2
539        return -3
540
541InitializeClass(PinTable)
542
543###)
544
545class PumeResultsTable(WAeUPTable): ###(
546
547    meta_type = 'WAeUP PumeResults Tool'
548    name = "portal_pumeresults"
549    key = "jamb_reg_no"
550    def __init__(self,name=None):
551        if name ==  None:
552            name = self.name
553        WAeUPTable.__init__(self, name)
554
555
556InitializeClass(PumeResultsTable)
557
558###)
559
560class ApplicantsCatalog(WAeUPTable): ###(
561
562    meta_type = 'WAeUP Applicants Catalog'
563    name = "applicants_catalog"
564    key = "reg_no"
565    security = ClassSecurityInfo()
566    export_keys = (
567                   "reg_no",
568                   "status",
569                   "lastname",
570                   "sex",
571                   "date_of_birth",
572                   "lga",
573                   "email",
574                   "phone",
575                   "passport",
576                   "entry_mode",
577                   "pin",
578                   "screening_type",
579                   "registration_date",
580                   "testdate",
581                   "application_date",
582                   "screening_date",
583                   "faculty",
584                   "department",
585                   "course1",
586                   "course2",
587                   "course3",
588                   "eng_score",
589                   "subj1",
590                   "subj1score",
591                   "subj2",
592                   "subj2score",
593                   "subj3",
594                   "subj3score",
595                   "aggregate",
596                   "course_admitted",
597                   )
598    def __init__(self,name=None):
599        if name ==  None:
600            name = self.name
601        WAeUPTable.__init__(self, name)
602
603
604
605    security.declareProtected(ModifyPortalContent,"new_importCSV")###(
606    def new_importCSV(self,filename="JAMB_data",
607                  schema_id="application",
608                  layout_id="application_import",
609                  mode='add'):
610        """ import JAMB data """
611        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
612        pm = self.portal_membership
613        member = pm.getAuthenticatedMember()
614        logger = logging.getLogger('WAeUPTables.ApplicantsCatalog.importCSV')
615        lock_fn = "%s/import/%s_import_lock" % (i_home,filename)
616        import_fn = "%s/import/%s.csv" % (i_home,filename)
617        if mode not in ('add','edit'):
618            logger.info("invalid mode: %s" % mode)
619        if os.path.exists(lock_fn):
620            logger.info("import of %(import_fn)s already in progress" % vars())
621            return
622        lock_file = open(lock_fn,"w")
623        lock_file.write("%(current)s \n" % vars())
624        lock_file.close()
625        invalid_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
626        duplicate_fn = "%s/import/%s_not_imported%s.csv" % (i_home,filename,current)
627        stool = getToolByName(self, 'portal_schemas')
628        ltool = getToolByName(self, 'portal_layouts')
629        schema = stool._getOb(schema_id)
630        if schema is None:
631            em = 'No such schema %s' % schema_id
632            logger.error(em)
633            return
634        for postfix in ('_import',''):
635            layout_name = "%(layout_id)s%(postfix)s" % vars()
636            if hasattr(ltool,layout_name):
637                break
638        layout = ltool._getOb(layout_name)
639        if layout is None:
640            em = 'No such layout %s' % layout_id
641            logger.error(em)
642            return
643        try:
644            csv_file = csv.DictReader(open(import_fn,"rb"))
645        except:
646            em = 'Error reading %s.csv' % filename
647            logger.error(em)
648            return
649        d = self._import_new(csv_items,schema,layout,mode,logger)
650        imported = []
651        edited = []
652        duplicates = []
653        not_found = []
654        if len(d['valid_records']) > 0:
655            for record in d['valid_records']:
656                #import pdb;pdb.set_trace()
657                if mode == "add":
658                    try:
659                        self.addRecord(**dict(record.items()))
660                        imported.append(**dict(record.items()))
661                        logger.info("added %s" % record.items())
662                    except ValueError:
663                        dupplicate.append(**dict(record.items()))
664                        logger.info("duplicate %s" % record.items())
665                elif mode == "edit":
666                    try:
667                        self.modifyRecord(**dict(record.items()))
668                        edited.append(**dict(record.items()))
669                        logger.info("edited %s" % record.items())
670                    except KeyError:
671                        not_found.append(**dict(record.items()))
672                        logger.info("not found %s" % record.items())
673        invalid = d['invalid_records']
674        for itype in ("imported","edited","not_found","duplicate","invalid"):
675            outlist = locals[itype]
676            if len(outlist):
677                d = {}
678                for k in outlist[0].keys():
679                    d[k] = k
680                outlist[0] = d
681                outfile = open("file_name_%s" % itype,'w')
682                csv.DictWriter(outfile,outlist[0].keys()).writerows(outlist)
683                logger.info("wrote %(itype)s records to %(, written to %(not_imported_fn)s" % d)
684###)
685
686    security.declareProtected(ModifyPortalContent,"importCSV")###(
687    def importCSV(self,filename="JAMB_data",
688                  schema_id="application",
689                  layout_id="application",
690                  mode='add'):
691        """ import JAMB data """
692        stool = getToolByName(self, 'portal_schemas')
693        ltool = getToolByName(self, 'portal_layouts')
694        schema = stool._getOb(schema_id)
695        if schema is None:
696            em = 'No such schema %s' % schema_id
697            logger.error(em)
698            return
699        layout = ltool._getOb(layout_id)
700        if layout is None:
701            em = 'No such layout %s' % layout_id
702            logger.error(em)
703            return
704        logger = logging.getLogger('WAeUPTables.ApplicantsCatalog.importCSV')
705        d = self._import_old(filename,schema,layout,mode,logger)
706        if len(d['valid_records']) > 0:
707            for record in d['valid_records']:
708                #import pdb;pdb.set_trace()
709                if mode == "add":
710                    self.addRecord(**dict(record.items()))
711                    logger.info("added %s" % record.items())
712                elif mode == "edit":
713                    self.modifyRecord(**dict(record.items()))
714                    logger.info("edited %s" % record.items())
715                else:
716                    logger.info("invalid mode: %s" % mode)
717        logger.info("%(mode)sed %(imported)d records, invalid written to %(not_imported_fn)s" % d)
718###)
719
720InitializeClass(ApplicantsCatalog)
721
722###)
723
724class StudentsCatalog(WAeUPTable): ###(
725    security = ClassSecurityInfo()
726
727    meta_type = 'WAeUP Students Catalog'
728    name = "students_catalog"
729    key = "id"
730    affected_types = {   ###(
731                      'StudentApplication':
732                      {'id': 'application',
733                       'fields':
734                       ('jamb_reg_no',
735                        'entry_mode',
736                        #'entry_level',
737                        'entry_session',
738                       )
739                      },
740                      'StudentClearance':
741                      {'id': 'clearance',
742                       'fields':
743                       ('matric_no',
744                        'lga',
745                       )
746                      },
747                      'StudentPersonal':
748                      {'id': 'personal',
749                       'fields':
750                       ('name',
751                        'sex',
752                        'perm_address',
753                        'email',
754                        'phone',
755                       )
756                      },
757                      'StudentStudyCourse':
758                      {'id': 'study_course',
759                       'fields':
760                       ('course', # study_course
761                        'faculty', # from certificate
762                        'department', # from certificate
763                        'end_level', # from certificate
764                        'level', # current_level
765                        'mode',  # current_mode
766                        'session', # current_session
767                        'verdict', # current_verdict
768                       )
769                      },
770                     }
771    ###)
772
773    def __init__(self,name=None):
774        if name ==  None:
775            name = self.name
776        WAeUPTable.__init__(self, name)
777        return
778
779    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None):
780        """ clears the whole enchilada """
781        self._catalog.clear()
782
783        if REQUEST and RESPONSE:
784            RESPONSE.redirect(
785              URL1 +
786              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Cleared')
787
788    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
789        """ clear the catalog, then re-index everything """
790
791        elapse = time.time()
792        c_elapse = time.clock()
793
794        pgthreshold = self._getProgressThreshold()
795        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
796        self.refreshCatalog(clear=1, pghandler=handler)
797
798        elapse = time.time() - elapse
799        c_elapse = time.clock() - c_elapse
800
801        RESPONSE.redirect(
802            URL1 +
803            '/manage_catalogAdvanced?manage_tabs_message=' +
804            urllib.quote('Catalog Updated \n'
805                         'Total time: %s\n'
806                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
807    ###)
808
809    def fill_certificates_dict(self): ###(
810        "return certificate data in  dict"
811        certificates_brains = self.portal_catalog(portal_type ='Certificate')
812        d = {}
813        for cb in certificates_brains:
814            certificate_doc = cb.getObject().getContent()
815            cb_path = cb.getPath().split('/')
816            ld = {}
817            ld['faculty'] = cb_path[-4]
818            ld['department'] = cb_path[-3]
819            ld['end_level'] = getattr(certificate_doc,'end_level','999')
820            d[cb.getId] = ld
821        return d
822    ###)
823
824    def get_from_doc_department(self,doc,cached_data={}): ###(
825        "return the students department"
826        if doc is None:
827            return None
828        if cached_data.has_key(doc.study_course):
829            return cached_data[doc.study_course]['department']
830        certificate_res = self.portal_catalog(id = doc.study_course)
831        if len(certificate_res) != 1:
832            return None
833        return certificate_res[0].getPath().split('/')[-3]
834
835    def get_from_doc_faculty(self,doc,cached_data={}):
836        "return the students faculty"
837        if doc is None:
838            return None
839        if cached_data.has_key(doc.study_course):
840            return cached_data[doc.study_course]['faculty']
841        certificate_res = self.portal_catalog(id = doc.study_course)
842        if len(certificate_res) != 1:
843            return None
844        return certificate_res[0].getPath().split('/')[-4]
845
846    def get_from_doc_end_level(self,doc,cached_data={}):
847        "return the students end_level"
848        if doc is None:
849            return None
850        if cached_data.has_key(doc.study_course):
851            return cached_data[doc.study_course]['end_level']
852        certificate_res = self.portal_catalog(id = doc.study_course)
853        if len(certificate_res) != 1:
854            return None
855        return getattr(certificate_res[0].getObject().getContent(),'end_level','unknown')
856
857    def get_from_doc_level(self,doc,cached_data={}):
858        "return the students level"
859        if doc is None:
860            return None
861        return getattr(doc,'current_level',None)
862
863    def get_from_doc_mode(self,doc,cached_data={}):
864        "return the students mode"
865        if doc is None:
866            return None
867        cm = getattr(doc,'current_mode',None)
868        return cm
869
870
871    def get_from_doc_session(self,doc,cached_data={}):
872        "return the students current_session"
873        if doc is None:
874            return None
875        return getattr(doc,'current_session',None)
876
877    def get_from_doc_entry_session(self,doc,cached_data={}):
878        "return the students entry_session"
879        if doc is None:
880            return None
881        es = getattr(doc,'entry_session',None)
882        if es is not None and len(es) == 2:
883            return es
884        try:
885            digit = int(doc.jamb_reg_no[0])
886        except:
887            return "-1"
888        if digit < 8:
889            return "0%c" % doc.jamb_reg_no[0]
890        return "9%c" % doc.jamb_reg_no[0]
891
892    def get_from_doc_course(self,doc,cached_data={}):
893        "return the students study_course"
894        if doc is None:
895            return None
896        return getattr(doc,'study_course',None)
897
898    def get_from_doc_name(self,doc,cached_data={}):
899        "return the students name from the personal"
900        if doc is None:
901            return None
902        return "%s %s %s" % (doc.firstname,doc.middlename,doc.lastname)
903
904    def get_from_doc_verdict(self,doc,cached_data={}):
905        "return the students study_course"
906        if doc is None:
907            return None
908        return getattr(doc,'current_verdict',None)
909    ###)
910
911    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
912        if isinstance(name, str):
913            name = (name,)
914        reindextypes = {}
915        reindex_special = []
916        for n in name:
917            if n in ("review_state","registered_courses"):
918                reindex_special.append(n)
919            else:
920                for pt in self.affected_types.keys():
921                    if n in self.affected_types[pt]['fields']:
922                        if reindextypes.has_key(pt):
923                            reindextypes[pt].append(n)
924                        else:
925                            reindextypes[pt]= [n]
926                        break
927        cached_data = {}
928        if set(name).intersection(set(('faculty','department','end_level'))):
929            cached_data = self.fill_certificates_dict()
930        students = self.portal_catalog(portal_type="Student")
931        if hasattr(self,'portal_catalog_real'):
932            aq_portal = self.portal_catalog_real.evalAdvancedQuery
933        else:
934            aq_portal = self.portal_catalog.evalAdvancedQuery
935        num_objects = len(students)
936        if pghandler:
937            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
938        noattr = set(('StudentClearance','StudentPersonal')) & set(reindextypes.keys())
939        #import pdb;pdb.set_trace()
940        for i in xrange(num_objects):
941            if pghandler: pghandler.report(i)
942            student_brain = students[i]
943            student_object = student_brain.getObject()
944            # query = Eq('path',student_brain.getPath())
945            # sub_brains_list = aq_portal(query)
946            # sub_brains = {}
947            # for sub_brain in sub_brains_list:
948            #     sub_brains[sub_brain.portal_type] = sub_brain
949            # student_path = student_brain.getPath()
950            data = {}
951            modified = False
952            sid = data['id'] = student_brain.getId
953            if reindex_special and 'review_state' in reindex_special:
954                modified = True
955                data['review_state'] = student_brain.review_state
956            sub_objects = False
957            for pt in reindextypes.keys():
958                modified = True
959                try:
960                    doc = getattr(student_object,self.affected_types[pt]['id']).getContent()
961                    #doc = sub_brains[pt].getObject().getContent()
962                    # path = "%s/%s" % (student_path,self.affected_types[pt]['id'])
963                    # doc = self.unrestrictedTraverse(path).getContent()
964                    sub_objects = True
965                except:
966                    continue
967                for field in set(name).intersection(self.affected_types[pt]['fields']):
968                    if hasattr(self,'get_from_doc_%s' % field):
969                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc,
970                                                                              cached_data=cached_data)
971                    else:
972                        data[field] = getattr(doc,field)
973            if not sub_objects and noattr:
974                import_res = self.returning_import(id = sid)
975                if not import_res:
976                    continue
977                import_record = import_res[0]
978                data['matric_no'] = import_record.matric_no
979                data['sex'] = import_record.Sex == 'F'
980                data['name'] = "%s %s %s" % (import_record.Firstname,
981                                             import_record.Middlename,
982                                             import_record.Lastname)
983                data['jamb_reg_no'] = import_record.Entryregno
984            if reindex_special and 'registered_courses' in reindex_special:
985                try:
986                    study_course = getattr(student_object,"study_course")
987                    level_ids = study_course.objectIds()
988                except:
989                    continue
990                if not level_ids:
991                    continue
992                modified = True
993                level_ids.sort()
994                course_ids = getattr(study_course,level_ids[-1]).objectIds()
995                courses = []
996                for c in course_ids:
997                    if c.endswith('_co'):
998                        courses.append(c[:-3])
999                    else:
1000                        courses.append(c)
1001                data['registered_courses'] = courses
1002            if modified:
1003                self.modifyRecord(**data)
1004        if pghandler: pghandler.finish()
1005    ###)
1006
1007    def refreshCatalog(self, clear=0, pghandler=None): ###(
1008        """ re-index everything we can find """
1009        students_folder = self.portal_url.getPortalObject().campus.students
1010        if clear:
1011            self._catalog.clear()
1012        students = self.portal_catalog(portal_type="Student")
1013        num_objects = len(students)
1014        cached_data = self.fill_certificates_dict()
1015        if pghandler:
1016            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1017        for i in xrange(num_objects):
1018            if pghandler: pghandler.report(i)
1019            student_brain = students[i]
1020            spath = student_brain.getPath()
1021            student_object = student_brain.getObject()
1022            data = {}
1023            sid = data['id'] = student_brain.getId
1024            data['review_state'] = student_brain.review_state
1025            sub_objects = False
1026            for pt in self.affected_types.keys():
1027                modified = True
1028                try:
1029                    doc = getattr(student_object,self.affected_types[pt]['id']).getContent()
1030                    sub_objects = True
1031                except:
1032                    #from pdb import set_trace;set_trace()
1033                    continue
1034                for field in self.affected_types[pt]['fields']:
1035                    if hasattr(self,'get_from_doc_%s' % field):
1036                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc,
1037                                                                              cached_data=cached_data)
1038                    else:
1039                        data[field] = getattr(doc,field,None)
1040            if not sub_objects:
1041                import_res = self.returning_import(id = sid)
1042                if not import_res:
1043                    continue
1044                import_record = import_res[0]
1045                data['matric_no'] = import_record.matric_no
1046                data['sex'] = import_record.Sex == 'F'
1047                data['name'] = "%s %s %s" % (import_record.Firstname,
1048                                             import_record.Middlename,
1049                                             import_record.Lastname)
1050                data['jamb_reg_no'] = import_record.Entryregno
1051            else:
1052                study_course = getattr(student_object,'study_course',None)
1053                current_level = data.get('level',None)
1054                data['registered_courses'] = []
1055                if study_course and current_level and current_level in study_course.objectIds():
1056                    level_obj = getattr(study_course,current_level)
1057                    courses = []
1058                    for c in level_obj.objectIds():
1059                        if c.endswith('_co'):
1060                            courses.append(c[:-3])
1061                        else:
1062                            courses.append(c)
1063                    data['registered_courses'] = courses
1064            self.addRecord(**data)
1065        if pghandler: pghandler.finish()
1066    ###)
1067
1068    security.declarePrivate('notify_event_listener') ###(
1069    def notify_event_listener(self,event_type,object,infos):
1070        "listen for events"
1071        if not infos.has_key('rpath'):
1072            return
1073        pt = getattr(object,'portal_type',None)
1074        mt = getattr(object,'meta_type',None)
1075        students_catalog = self
1076        data = {}
1077        if pt == 'Student' and\
1078           mt == 'CPS Proxy Folder' and\
1079           event_type.startswith('workflow'):
1080            data['id'] = object.getId()
1081            data['review_state'] = self.portal_workflow.getInfoFor(object,'review_state',None)
1082            students_catalog.modifyRecord(**data)
1083            return
1084        rpl = infos['rpath'].split('/')
1085        if pt == 'Student' and mt == 'CPS Proxy Folder'\
1086           and event_type == "sys_add_object":
1087            student_id = object.id
1088            try:
1089                self.addRecord(id = student_id)
1090            except ValueError:
1091                pass
1092            return
1093        elif pt == 'StudentCourseResult' and mt == 'CPS Proxy Folder':
1094            if event_type not in ("sys_add_object","sys_del_object"):
1095                return
1096            level_session = getattr(object.aq_parent.getContent(),'session','unknown')
1097            if level_session not in (self.getSessionId()[-2:],'2006/2007'):
1098                return
1099            course_id = object.getId()
1100            if course_id.endswith('_co'):
1101                course_id = course_id[:-3]
1102            student_id = object.absolute_url_path().split('/')[-4]
1103            res = students_catalog(id = student_id)
1104            if not res:
1105                return
1106            student_rec = res[0]
1107            registered_courses = getattr(student_rec,'registered_courses',None)
1108            if not registered_courses:
1109                registered_courses = []
1110            #import pdb;pdb.set_trace()
1111            if event_type == "sys_add_object":
1112                if course_id not in registered_courses:
1113                    registered_courses.append(course_id)
1114                else:
1115                    return
1116            elif registered_courses and event_type == "sys_del_object":
1117                removed = False
1118                while course_id in registered_courses:
1119                    removed = True
1120                    registered_courses.remove(course_id)
1121                if not removed:
1122                    return
1123            data['id'] = student_id
1124            data['registered_courses'] = registered_courses
1125            self.modifyRecord(record = student_rec, **data)
1126            return
1127        if pt not in self.affected_types.keys():
1128            return
1129        if event_type not in ('sys_modify_object'):
1130            return
1131        if mt == 'CPS Proxy Folder':
1132            return
1133        for field in self.affected_types[pt]['fields']:
1134            if hasattr(self,'get_from_doc_%s' % field):
1135                data[field] = getattr(self,'get_from_doc_%s' % field)(object)
1136            else:
1137                data[field] = getattr(object,field)
1138        data['id'] = rpl[2]
1139        self.modifyRecord(**data)
1140    ###)
1141
1142
1143InitializeClass(StudentsCatalog)
1144
1145###)
1146
1147class CoursesCatalog(WAeUPTable): ###(
1148    security = ClassSecurityInfo()
1149
1150    meta_type = 'WAeUP Courses Catalog'
1151    name =  "courses_catalog"
1152    key = "code"
1153    def __init__(self,name=None):
1154        if name ==  None:
1155            name =  self.name
1156        WAeUPTable.__init__(self, name)
1157
1158    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
1159        """ clear the catalog, then re-index everything """
1160
1161        elapse = time.time()
1162        c_elapse = time.clock()
1163
1164        pgthreshold = self._getProgressThreshold()
1165        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
1166        self.refreshCatalog(clear=1, pghandler=handler)
1167
1168        elapse = time.time() - elapse
1169        c_elapse = time.clock() - c_elapse
1170
1171        RESPONSE.redirect(
1172            URL1 +
1173            '/manage_catalogAdvanced?manage_tabs_message=' +
1174            urllib.quote('Catalog Updated \n'
1175                         'Total time: %s\n'
1176                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
1177    ###)
1178
1179    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
1180        if isinstance(name, str):
1181            name = (name,)
1182        courses = self.portal_catalog(portal_type="Course")
1183        num_objects = len(courses)
1184        if pghandler:
1185            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1186        for i in xrange(num_objects):
1187            if pghandler: pghandler.report(i)
1188            course_brain = courses[i]
1189            course_object = course_brain.getObject()
1190            pl = course_brain.getPath().split('/')
1191            data = {}
1192            cid = data[self.key] = course_brain.getId
1193            data['faculty'] = pl[-4]
1194            data['department'] = pl[-3]
1195            doc = course_object.getContent()
1196            for field in name:
1197                if field not in (self.key,'faculty','department'):
1198                    data[field] = getattr(doc,field)
1199            self.modifyRecord(**data)
1200        if pghandler: pghandler.finish()
1201    ###)
1202
1203    def refreshCatalog(self, clear=0, pghandler=None): ###(
1204        """ re-index everything we can find """
1205        if clear:
1206            self._catalog.clear()
1207        courses = self.portal_catalog(portal_type="Course")
1208        num_objects = len(courses)
1209        if pghandler:
1210            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1211        #from pdb import set_trace;set_trace()
1212        for i in xrange(num_objects):
1213            if pghandler: pghandler.report(i)
1214            course_brain = courses[i]
1215            course_doc = course_brain.getObject().getContent()
1216            pl = course_brain.getPath().split('/')
1217            data = {}
1218            for field in self.schema():
1219                data[field] = getattr(course_doc,field,None)
1220            data[self.key] = course_brain.getId
1221            ai = pl.index('academics')
1222            data['faculty'] = pl[ai +1]
1223            data['department'] = pl[ai +2]
1224            if clear:
1225                self.addRecord(**data)
1226            else:
1227                self.modifyRecord(**data)
1228        if pghandler: pghandler.finish()
1229    ###)
1230
1231    security.declarePrivate('notify_event_listener') ###(
1232    def notify_event_listener(self,event_type,object,infos):
1233        "listen for events"
1234        if not infos.has_key('rpath'):
1235            return
1236        pt = getattr(object,'portal_type',None)
1237        mt = getattr(object,'meta_type',None)
1238        if pt != 'Course':
1239            return
1240        data = {}
1241        rpl = infos['rpath'].split('/')
1242        if event_type not in ("sys_add_object","sys_modify_object","sys_del_object"):
1243            return
1244        course_id = object.getId()
1245        data[self.key] = course_id
1246        if event_type == "sys_add_object" and mt == 'CPS Proxy Folder':
1247            try:
1248                self.addRecord(**data)
1249            except ValueError:
1250                return
1251            course_id = object.getId()
1252            doc = object.getContent()
1253            if doc is None:
1254                return
1255            for field in self.schema():
1256                data[field] = getattr(doc,field,None)
1257            data[self.key] = course_id
1258            ai = rpl.index('academics')
1259            data['faculty'] = rpl[ai +1]
1260            data['department'] = rpl[ai +2]
1261            self.modifyRecord(**data)
1262            return
1263        if event_type == "sys_del_object":
1264            self.deleteRecord(course_id)
1265            return
1266        if event_type == "sys_modify_object" and mt == 'Course':
1267            #from pdb import set_trace;set_trace()
1268            for field in self.schema():
1269                data[field] = getattr(object,field,None)
1270            course_id = object.aq_parent.getId()
1271            data[self.key] = course_id
1272            ai = rpl.index('academics')
1273            data['faculty'] = rpl[ai +1]
1274            data['department'] = rpl[ai +2]
1275            self.modifyRecord(**data)
1276    ###)
1277
1278
1279InitializeClass(CoursesCatalog)
1280###)
1281
1282class CourseResults(WAeUPTable): ###(
1283    security = ClassSecurityInfo()
1284
1285    meta_type = 'WAeUP Results Catalog'
1286    name = "course_results"
1287    key = "key" #student_id + level + course_id
1288    def __init__(self,name=None):
1289        if name ==  None:
1290            name = self.name
1291        WAeUPTable.__init__(self, name)
1292        self._queue = []
1293
1294    def addRecord(self, **data): ###(
1295        """add one record"""
1296
1297        uid = key = "%(student_id)s|%(level_id)s|%(course_id)s" % data
1298        if key in self._queue:
1299            return uid
1300        data['queue_status'] = ADDING_SHEDULED
1301        data['%s' % self.key] = uid
1302        res = self.searchResults({"%s" % self.key : uid})
1303        if len(res) > 0:
1304            raise ValueError("More than one record with uid %s" % uid)
1305        self.catalog_object(dict2ob(data), uid=uid)
1306        if not hasattr(self,'_queue'):
1307            self._queue = []
1308        self._queue.append(key)
1309        self._p_changed = 1
1310        return uid
1311    ###)
1312
1313    # def _p_resolveConflict(self, oldstate, committed, newstate):
1314    #     # Apply the changes made in going from old to newstate to
1315    #     # committed
1316
1317    #     # Note that in the case of undo, the olddata is the data for
1318    #     # the transaction being undone and newdata is the data for the
1319    #     # transaction previous to the undone transaction.
1320
1321    #     # Find the conflict policy on the new state to make sure changes
1322    #     # to it will be applied
1323
1324    #     # Committed is always the currently committed data.
1325    #     import pdb;pdb.set_trace()
1326    #     oldstate_data  =  oldstate['_queue']
1327    #     committed_data = committed['_queue']
1328    #     newstate_data  =  newstate['_queue']
1329
1330    #     # Merge newstate changes into committed
1331    #     for uid, new in newstate_data.items():
1332
1333    #         # Decide if this is a change
1334    #         old = oldstate_data.get(uid)
1335    #         current = committed_data.get(uid)
1336
1337
1338    def addMultipleRecords(self, records): ###(
1339        """add many records"""
1340        added_keys = []
1341        for data in records:
1342            uid = key = "%(student_id)s|%(level_id)s|%(course_id)s" % data
1343            added_keys.append(key)
1344            if key in self._queue:
1345                return uid
1346            data['queue_status'] = ADDING_SHEDULED
1347            data['%s' % self.key] = uid
1348            res = self.searchResults({"%s" % self.key : uid})
1349            if len(res) > 0:
1350                raise ValueError("More than one record with uid %s" % uid)
1351            self.catalog_object(dict2ob(data), uid=uid)
1352        if not hasattr(self,'_queue'):
1353            self._queue = added_keys
1354        self._queue.extend(added_keys)
1355        self._p_changed = 1
1356        return uid
1357    ###)
1358
1359    def deleteRecord(self, uid): ###(
1360        self.uncatalog_object(uid)
1361        if uid in self._queue:
1362            self._queue.remove(uid)
1363    ###)
1364
1365    def updateCourseResults(self,student_id,level_id,portal_catalog_results,course_results): ###(
1366        # query = Eq('path',"%s/campus/students/%s/study_course/%s" %
1367        #            (self.portal_url.getPortalPath(),
1368        #             student_id,
1369        #             level_id)) &\
1370        #             Eq('portal_type', "StudentCourseResult")
1371        # level_results = self.portal_catalog_real.evalAdvancedQuery(query)
1372        # level_results = [r for r in course_results
1373        #                  if level_id in r.relative_path.split('/')]
1374        course_results_ids = [cr.getId for cr in course_results]
1375        for r in portal_catalog_results:
1376            if r.getId in course_results_ids:
1377                continue
1378            course_result_doc = r.getObject().getContent()
1379            data = {}
1380            course_id = r.getId
1381            for field in self.schema():
1382                data[field] = getattr(course_result_doc,field,'')
1383            data['key'] = key = "%(student_id)s|%(level_id)s|%(course_id)s" % vars()
1384            data['student_id'] = student_id
1385            data['level_id'] = level_id
1386            data['queue_status'] = OBJECT_CREATED
1387            data['code'] = course_id
1388            self.catalog_object(dict2ob(data), uid=key)
1389        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1390        return self.course_results.evalAdvancedQuery(query)
1391    ###)
1392
1393    def getCourses(self,student_id,level_id): ###(
1394        level_path = "%s/campus/students/%s/study_course/%s" % (self.portal_url.getPortalPath(),
1395                                                                student_id,
1396                                                                level_id)
1397        # portal_catalog_query = Eq('path',level_path) &\
1398        #                        Eq('portal_type', "StudentCourseResult")
1399        # portal_catalog_results = self.portal_catalog_real.evalAdvancedQuery(portal_catalog_query)
1400        portal_catalog_results = self.portal_catalog(path = level_path,
1401                                                     portal_type = "StudentCourseResult")
1402        query = Eq('student_id',student_id) & Eq('level_id', level_id)
1403        course_results = self.course_results.evalAdvancedQuery(query)
1404        if len(course_results) != len(portal_catalog_results):
1405            course_results = self.updateCourseResults(student_id,
1406                                                      level_id,
1407                                                      portal_catalog_results,
1408                                                      course_results)
1409        carry_overs = []
1410        normal = []
1411        credits = 0
1412        for brain in course_results:
1413            d = {}
1414            credits += int(brain.credits)
1415            for field in self.schema():
1416                d[field] = getattr(brain,field,'')
1417            d['sheduled'] = brain.queue_status == ADDING_SHEDULED
1418            d['coe'] = 'Elective'
1419            if brain.core_or_elective:
1420                d['coe'] = 'Core'
1421            id = code = d['id'] = brain.code
1422            is_carry_over = False
1423            if code.endswith('_co'):
1424                is_carry_over = True
1425                code = code[:-3]
1426            d['code'] = code
1427            d['title'] = self.courses_catalog.evalAdvancedQuery(Eq('code',code))[0].title
1428            if is_carry_over:
1429                d['coe'] = 'Core'
1430                carry_overs.append(d)
1431            else:
1432                normal.append(d)
1433        normal.sort(cmp=lambda x,y: cmp(x['semester'], y['semester']))
1434        carry_overs.sort(cmp=lambda x,y: cmp(x['semester'], y['semester']))
1435        return credits,carry_overs,normal
1436    ###)
1437
1438    def addObject(self,record): ###(
1439        key = record.key
1440        student_id,level_id,course_id = key.split('|')
1441        level = getattr(getattr(self.portal_url.getPortalObject().campus.students,student_id).study_course,level_id)
1442        cr_id = level.invokeFactory('StudentCourseResult', course_id)
1443        course_result = getattr(level,cr_id)
1444        self.portal_workflow.doActionFor(course_result,'open')
1445        d = {}
1446        for field in self.schema():
1447            d[field] = getattr(record,field,'')
1448        course_result.getContent().edit(mapping=d)
1449    ###)
1450
1451    security.declareProtected(ModifyPortalContent,"process_queue") ###(
1452    def process_queue(self,limit=None):
1453        """adds objects and removes them from the queue.
1454        If limit is specified, at most (limit) events are removed.
1455        """
1456        if not hasattr(self,'_queue'):
1457            return 0
1458        queue= self._queue
1459        if not limit or len(queue) <= limit:
1460            keys = self._queue[:]
1461        else:
1462            keys = queue[:limit]
1463        if not keys:
1464            records = self.evalAdvancedQuery(Eq('queue_status',ADDING_SHEDULED))
1465        else:
1466            records = self.evalAdvancedQuery(In("%s" % self.key,keys))
1467        for record in records:
1468            if record.queue_status == OBJECT_CREATED:
1469                continue
1470            self.addObject(record)
1471            data = {}
1472            data['key'] = record.key
1473            data['queue_status'] = OBJECT_CREATED
1474            self.modifyRecord(**data)
1475        count = 0
1476        for key in keys:
1477            count +=1
1478            if key in self._queue:
1479                self._queue.remove(key)
1480        self._p_changed = 1
1481        return count,len(self._queue)
1482    ###)
1483
1484    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None): ###(
1485        """ clears the whole enchilada """
1486        self._catalog.clear()
1487
1488        if REQUEST and RESPONSE:
1489            RESPONSE.redirect(
1490              URL1 +
1491              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Cleared')
1492    ###)
1493
1494    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
1495        """ clear the catalog, then re-index everything """
1496
1497        elapse = time.time()
1498        c_elapse = time.clock()
1499
1500        pgthreshold = self._getProgressThreshold()
1501        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
1502        self.refreshCatalog(clear=1, pghandler=handler)
1503
1504        elapse = time.time() - elapse
1505        c_elapse = time.clock() - c_elapse
1506
1507        RESPONSE.redirect(
1508            URL1 +
1509            '/manage_catalogAdvanced?manage_tabs_message=' +
1510            urllib.quote('Catalog Updated \n'
1511                         'Total time: %s\n'
1512                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
1513    ###)
1514
1515    def refreshCatalog(self, clear=1, pghandler=None): ###(
1516        """ re-index everything we can find """
1517        students_folder = self.portal_url.getPortalObject().campus.students
1518        if clear:
1519            self._catalog.clear()
1520        course_results = self.portal_catalog(portal_type="StudentCourseResult")
1521        num_objects = len(course_results)
1522        if pghandler:
1523            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
1524        #import pdb;pdb.set_trace()
1525        for i in xrange(num_objects):
1526            if pghandler:
1527                pghandler.report(i)
1528            course_result_brain = course_results[i]
1529            path_list = course_result_brain.getPath().split('/')
1530            course_result_doc = course_result_brain.getObject().getContent()
1531            data = {}
1532            level_id = path_list[-2]
1533            course_id = path_list[-1]
1534            student_id = path_list[-4]
1535            for field in self.schema():
1536                data[field] = getattr(course_result_doc,field,'')
1537            data['key'] = key = "%(student_id)s|%(level_id)s|%(course_id)s" % vars()
1538            data['student_id'] = student_id
1539            data['level_id'] = level_id
1540            data['queue_status'] = OBJECT_CREATED
1541            self.catalog_object(dict2ob(data), uid=key)
1542        if pghandler: pghandler.finish()
1543    ###)
1544
1545    security.declarePrivate('notify_event_listener') ###(
1546    def notify_event_listener(self,event_type,object,infos):
1547        "listen for events"
1548        if not infos.has_key('rpath'):
1549            return
1550        pt = getattr(object,'portal_type',None)
1551        mt = getattr(object,'meta_type',None)
1552        data = {}
1553        rpl = infos['rpath'].split('/')
1554        if mt == 'CPS Proxy Folder':
1555            return
1556        if pt == 'StudentCourseResult' and event_type == "sys_modify_object":
1557            data["%s" % self.key] = uid = "%s|%s|%s" % (rpl[-5],rpl[-3],rpl[-2])
1558            records = self.searchResults({"%s" % self.key : uid})
1559            if len(records) > 1:
1560                # Can not happen, but anyway...
1561                raise ValueError("More than one record with uid %s" % uid)
1562            if len(records) == 0:
1563                raise KeyError("No record for uid %s" % uid)
1564            record = records[0]
1565            for field in ('core_or_elective','score'):
1566                value = getattr(object,field,None)
1567                data[field] = value
1568            try:
1569                self.modifyRecord(record,**data)
1570            except KeyError:
1571                pass
1572        if pt == 'StudentStudyLevel' and event_type == "sys_del_object":
1573            #import pdb;pdb.set_trace()
1574            student_id = rpl[-3]
1575            level_id = rpl[-1]
1576            res = self.searchResults(student_id = student_id,
1577                                     level_id = level_id)
1578            for cr in res:
1579                self.deleteRecord(cr.key)
1580    ###)
1581
1582InitializeClass(CourseResults)
1583###)
1584
1585class OnlinePaymentsImport(WAeUPTable): ###(
1586
1587    meta_type = 'WAeUP Online Payment Transactions'
1588    name = "online_payments_import"
1589    key = "order_id"
1590    def __init__(self,name=None):
1591        if name ==  None:
1592            name = self.name
1593        WAeUPTable.__init__(self, name)
1594
1595
1596InitializeClass(OnlinePaymentsImport)
1597###)
1598
1599class ReturningImport(WAeUPTable): ###(
1600
1601    meta_type = 'Returning Import Table'
1602    name = "returning_import"
1603    key = "matric_no"
1604    def __init__(self,name=None):
1605        if name ==  None:
1606            name = self.name
1607        WAeUPTable.__init__(self, name)
1608
1609
1610InitializeClass(ReturningImport)
1611###)
1612
1613class ResultsImport(WAeUPTable): ###(
1614
1615    meta_type = 'Results Import Table'
1616    name = "results_import"
1617    key = "key"
1618    def __init__(self,name=None):
1619        if name ==  None:
1620            name = self.name
1621        WAeUPTable.__init__(self, name)
1622
1623
1624InitializeClass(ResultsImport)
1625
1626###)
1627
1628class PaymentsCatalog(WAeUPTable): ###(
1629
1630    meta_type = 'WAeUP Payments Catalog'
1631    name = "students_catalog"
1632    key = "id"
1633    def __init__(self,name=None):
1634        if name ==  None:
1635            name = self.name
1636        WAeUPTable.__init__(self, name)
1637
1638
1639InitializeClass(PaymentsCatalog)
1640
1641###)
1642
1643# BBB:
1644AccomodationTable = AccommodationTable
Note: See TracBrowser for help on using the repository browser.