source: WAeUP_SRP/base/WAeUPTables.py @ 2380

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

data structure and layout for pde screening customized

  • Property svn:keywords set to Id
File size: 61.8 KB
RevLine 
[966]1#-*- mode: python; mode: fold -*-
[363]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 2368 2007-10-16 20:35:31Z henrik $
20
21from zope.interface import implements
22from Globals import InitializeClass
23from Products.ZCatalog.ZCatalog import ZCatalog
[1620]24from Products.ZCatalog.ProgressHandler import ZLogHandler
[780]25from AccessControl import ClassSecurityInfo
26from Products.CMFCore.permissions import ModifyPortalContent
[2094]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
[1700]33import urllib
[1620]34import DateTime,time
[780]35import csv,re
36import logging
37import Globals
38p_home = Globals.package_home(globals())
39i_home = Globals.INSTANCE_HOME
40
[2084]41ADDING_SHEDULED = "adding_sheduled"
42OBJECT_CREATED = "object_created"
43
[363]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
[1146]55class WAeUPTable(ZCatalog): ###(
[834]56
[363]57    implements(IWAeUPTable)
[780]58    security = ClassSecurityInfo()
[2094]59    meta_type = None
[2099]60
[2094]61    def __init__(self,name=None):
62        if name ==  None:
63            name = self.name
64        ZCatalog.__init__(self,name)
[2099]65
[2094]66    def refreshCatalog(self, clear=0, pghandler=None): ###(
[1620]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
[2094]74###)
75
76    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None): ###(
[1620]77        """ clears the whole enchilada """
[1986]78
[1916]79        #if REQUEST and RESPONSE:
80        #    RESPONSE.redirect(
81        #      URL1 +
82        #      '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Clearing%20disabled')
[1620]83
[1916]84        self._catalog.clear()
[1620]85        if REQUEST and RESPONSE:
86            RESPONSE.redirect(
87              URL1 +
[1916]88              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20cleared')
[1620]89
[2094]90###)
91
[2189]92    def record2dict(self,fields,record):
93        d = {}
94        for key in fields:
95            v = getattr(record, key, None)
[2192]96            if key == 'sex':
97                if v:
98                    v = 'F'
99                else:
100                    v = 'M'
101                d[key] = v
102            elif v:
[2189]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
[2191]109
[2094]110    def addRecord(self, **data): ###(
[502]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
[834]118
[2094]119###)
120
[363]121    def deleteRecord(self, uid):
122        self.uncatalog_object(uid)
[834]123
[502]124    def searchAndSetRecord(self, **data):
125        raise NotImplemented
126
[2094]127    def modifyRecord(self, record=None, **data): ###(
[502]128        #records = self.searchResults(uid=uid)
129        uid = data[self.key]
[2069]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]
[363]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
[2094]145###)
146
147    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
[1062]148        if isinstance(name, str):
[2094]149            name =  (name,)
[1062]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)
[1082]162
[2094]163###)
164
165    security.declareProtected(ModifyPortalContent,"exportAllRecords") ###(
[780]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))
[2094]184
185###)
186
[2189]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
[2185]260    security.declarePrivate("_import_old") ###(
261    def _import_old(self,filename,schema,layout, mode,logger):
[2094]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
[2112]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
[2094]290        if schema is None:
291            em = 'No schema specified'
292            logger.error(em)
[2112]293            return d
[2094]294        if layout is None:
295            em = 'No layout specified'
296            logger.error(em)
[2112]297            return d
[2094]298        validators = {}
299        for widget in layout.keys():
[2112]300            try:
301                validators[widget] = layout[widget].validate
302            except AttributeError:
303                logger.info('%s has no validate attribute' % widget)
304                return d
[2094]305        # if mode == 'edit':
306        #     importer = self.importEdit
307        # elif mode == 'add':
308        #     importer = self.importAdd
309        # else:
310        #     importer = None
311        try:
[2185]312            items = csv.DictReader(open(import_fn,"rb"),
313                                   dialect="excel",
314                                   skipinitialspace=True)
[2094]315        except:
316            em = 'Error reading %s.csv' % filename
317            logger.error(em)
318            return d
[2185]319        #import pdb;pdb.set_trace()
[2094]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')]
[2185]325                attrs = csv.reader(open("%s/import/%s.csv" % (i_home,filename),"rb"),
326                                   dialect="excel",
327                                   skipinitialspace=True).next()
[2094]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
[1935]380    ###)
[2185]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
[2094]461###)
[834]462
[1146]463class AccommodationTable(WAeUPTable): ###(
[834]464
[404]465    meta_type = 'WAeUP Accommodation Tool'
[2094]466    name = "portal_accommodation"
[502]467    key = "bed"
[2094]468    def __init__(self,name=None):
469        if name ==  None:
470            name = self.name
471        WAeUPTable.__init__(self, name)
[363]472
[635]473    def searchAndReserveBed(self, student_id,bed_type):
474        records = self.searchResults({'student' : student_id})
475        if len(records) > 0:
[1293]476            return -1,"Student with Id %s already booked bed %s." % (student_id,records[0].bed)
[834]477
[673]478        records = [r for r in self.searchResults({'bed_type' : bed_type}) if not r.student]
[686]479        #import pdb;pdb.set_trace()
[635]480        if len(records) == 0:
[1306]481            return -2,"No bed available"
[635]482        rec = records[0]
483        self.modifyRecord(bed=rec.bed,student=student_id)
[1571]484        s_logger = logging.getLogger('WAeUPTables.AccommodationTable.searchAndReserveBed')
485        s_logger.info('%s reserved bed %s' % (student_id,rec.bed))
[635]486        return 1,rec.bed
[363]487
[834]488
[404]489InitializeClass(AccommodationTable)
[411]490
[1146]491###)
492
493class PinTable(WAeUPTable): ###(
[1030]494    from ZODB.POSException import ConflictError
[440]495    meta_type = 'WAeUP Pin Tool'
[2094]496    name = "portal_pins"
[502]497    key = 'pin'
[2094]498    def __init__(self,name=None):
499        if name ==  None:
500            name = self.name
501        WAeUPTable.__init__(self, name)
[1082]502
503
[710]504    def searchAndSetRecord(self, uid, student_id,prefix):
[2191]505
506        # The following line must be activated after resetting the
507        # the portal_pins table. This is to avoid duplicate entries
[2184]508        # and disable duplicate payments.
[2191]509
[2184]510        #student_id = student_id.upper()
511
[710]512        records = self.searchResults(student = student_id)
[1776]513        if len(records) > 0 and prefix in ('CLR','APP'):
[710]514            for r in records:
[834]515                if r.pin != uid and r.prefix_batch.startswith(prefix):
[710]516                    return -2
[502]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:
[635]529            record_data['student'] = student_id
[1030]530            try:
531                self.catalog_object(dict2ob(record_data), uid)
532                return 1
533            except ConflictError:
534                return 2
[990]535        if record.student.upper() != student_id.upper():
[502]536            return 0
[997]537        if record.student.upper() == student_id.upper():
[502]538            return 2
[997]539        return -3
[440]540
541InitializeClass(PinTable)
542
[1146]543###)
[966]544
[1146]545class PumeResultsTable(WAeUPTable): ###(
546
[966]547    meta_type = 'WAeUP PumeResults Tool'
[2094]548    name = "portal_pumeresults"
[966]549    key = "jamb_reg_no"
[2094]550    def __init__(self,name=None):
551        if name ==  None:
552            name = self.name
553        WAeUPTable.__init__(self, name)
[966]554
555
556InitializeClass(PumeResultsTable)
557
[1146]558###)
[971]559
[2094]560class ApplicantsCatalog(WAeUPTable): ###(
561
[2113]562    meta_type = 'WAeUP Applicants Catalog'
[2094]563    name = "applicants_catalog"
564    key = "reg_no"
565    security = ClassSecurityInfo()
[2189]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                   )
[2094]598    def __init__(self,name=None):
599        if name ==  None:
600            name = self.name
601        WAeUPTable.__init__(self, name)
602
[2189]603
[2191]604
[2185]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)
[2191]648            return
[2185]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
[2191]680                outlist[0] = d
[2185]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
[2094]686    security.declareProtected(ModifyPortalContent,"importCSV")###(
687    def importCSV(self,filename="JAMB_data",
688                  schema_id="application",
[2368]689                  layout_id="application_pde",
[2094]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
[2099]704        logger = logging.getLogger('WAeUPTables.ApplicantsCatalog.importCSV')
[2185]705        d = self._import_old(filename,schema,layout,mode,logger)
[2094]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
[1146]724class StudentsCatalog(WAeUPTable): ###(
[1620]725    security = ClassSecurityInfo()
[1146]726
[971]727    meta_type = 'WAeUP Students Catalog'
728    name = "students_catalog"
729    key = "id"
[1700]730    affected_types = {   ###(
[1749]731                      'StudentApplication':
[2069]732                      {'id': 'application',
733                       'fields':
734                       ('jamb_reg_no',
735                        'entry_mode',
736                        #'entry_level',
737                        'entry_session',
738                       )
739                      },
[1700]740                      'StudentClearance':
[2069]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                     }
[1700]771    ###)
[1625]772
[2094]773    def __init__(self,name=None):
774        if name ==  None:
775            name = self.name
776        WAeUPTable.__init__(self, name)
[1620]777        return
[1625]778
[1700]779    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None):
780        """ clears the whole enchilada """
781        self._catalog.clear()
[971]782
[1700]783        if REQUEST and RESPONSE:
784            RESPONSE.redirect(
785              URL1 +
786              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Cleared')
[971]787
[1700]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
[2084]809    def fill_certificates_dict(self): ###(
[2078]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
[2084]822    ###)
823
[2078]824    def get_from_doc_department(self,doc,cached_data={}): ###(
[1620]825        "return the students department"
[1700]826        if doc is None:
[1620]827            return None
[2078]828        if cached_data.has_key(doc.study_course):
829            return cached_data[doc.study_course]['department']
[1700]830        certificate_res = self.portal_catalog(id = doc.study_course)
[1620]831        if len(certificate_res) != 1:
832            return None
833        return certificate_res[0].getPath().split('/')[-3]
834
[2078]835    def get_from_doc_faculty(self,doc,cached_data={}):
[1700]836        "return the students faculty"
837        if doc is None:
[1620]838            return None
[2078]839        if cached_data.has_key(doc.study_course):
840            return cached_data[doc.study_course]['faculty']
[1700]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]
[1620]845
[2099]846    def get_from_doc_end_level(self,doc,cached_data={}):
[2069]847        "return the students end_level"
848        if doc is None:
849            return None
[2078]850        if cached_data.has_key(doc.study_course):
851            return cached_data[doc.study_course]['end_level']
[2069]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
[2078]857    def get_from_doc_level(self,doc,cached_data={}):
[1700]858        "return the students level"
859        if doc is None:
[1620]860            return None
[1700]861        return getattr(doc,'current_level',None)
[1620]862
[2078]863    def get_from_doc_mode(self,doc,cached_data={}):
[1705]864        "return the students mode"
[1700]865        if doc is None:
[1620]866            return None
[1705]867        cm = getattr(doc,'current_mode',None)
868        return cm
[1625]869
[1749]870
[2078]871    def get_from_doc_session(self,doc,cached_data={}):
[1705]872        "return the students current_session"
873        if doc is None:
874            return None
875        return getattr(doc,'current_session',None)
876
[2078]877    def get_from_doc_entry_session(self,doc,cached_data={}):
[1700]878        "return the students entry_session"
879        if doc is None:
[1620]880            return None
[1705]881        es = getattr(doc,'entry_session',None)
[1729]882        if es is not None and len(es) == 2:
[1705]883            return es
[1700]884        try:
885            digit = int(doc.jamb_reg_no[0])
886        except:
[1986]887            return "-1"
[1700]888        if digit < 8:
889            return "0%c" % doc.jamb_reg_no[0]
890        return "9%c" % doc.jamb_reg_no[0]
891
[2078]892    def get_from_doc_course(self,doc,cached_data={}):
[1620]893        "return the students study_course"
[1700]894        if doc is None:
[1620]895            return None
[1700]896        return getattr(doc,'study_course',None)
[1620]897
[2078]898    def get_from_doc_name(self,doc,cached_data={}):
[1620]899        "return the students name from the personal"
[1700]900        if doc is None:
[1620]901            return None
902        return "%s %s %s" % (doc.firstname,doc.middlename,doc.lastname)
903
[2078]904    def get_from_doc_verdict(self,doc,cached_data={}):
[1700]905        "return the students study_course"
906        if doc is None:
[1620]907            return None
[1700]908        return getattr(doc,'current_verdict',None)
[1702]909    ###)
[1620]910
[1702]911    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
912        if isinstance(name, str):
913            name = (name,)
[1749]914        reindextypes = {}
[1702]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():
[1707]921                    if n in self.affected_types[pt]['fields']:
[1702]922                        if reindextypes.has_key(pt):
923                            reindextypes[pt].append(n)
924                        else:
925                            reindextypes[pt]= [n]
926                        break
[2078]927        cached_data = {}
928        if set(name).intersection(set(('faculty','department','end_level'))):
929            cached_data = self.fill_certificates_dict()
[1702]930        students = self.portal_catalog(portal_type="Student")
[1954]931        if hasattr(self,'portal_catalog_real'):
932            aq_portal = self.portal_catalog_real.evalAdvancedQuery
933        else:
934            aq_portal = self.portal_catalog.evalAdvancedQuery
[1702]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())
[2084]939        #import pdb;pdb.set_trace()
[1702]940        for i in xrange(num_objects):
941            if pghandler: pghandler.report(i)
942            student_brain = students[i]
[1707]943            student_object = student_brain.getObject()
[2084]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()
[1702]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
[1707]956            sub_objects = False
957            for pt in reindextypes.keys():
[1702]958                modified = True
[1707]959                try:
960                    doc = getattr(student_object,self.affected_types[pt]['id']).getContent()
[2084]961                    #doc = sub_brains[pt].getObject().getContent()
962                    # path = "%s/%s" % (student_path,self.affected_types[pt]['id'])
963                    # doc = self.unrestrictedTraverse(path).getContent()
[1707]964                    sub_objects = True
965                except:
966                    continue
[2084]967                for field in set(name).intersection(self.affected_types[pt]['fields']):
[1707]968                    if hasattr(self,'get_from_doc_%s' % field):
[2078]969                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc,
970                                                                              cached_data=cached_data)
[1707]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)
[1815]983                data['jamb_reg_no'] = import_record.Entryregno
[1702]984            if reindex_special and 'registered_courses' in reindex_special:
[1954]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
[1702]1002            if modified:
1003                self.modifyRecord(**data)
1004        if pghandler: pghandler.finish()
1005    ###)
[1620]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:
[1724]1011            self._catalog.clear()
[1700]1012        students = self.portal_catalog(portal_type="Student")
1013        num_objects = len(students)
[2078]1014        cached_data = self.fill_certificates_dict()
[1620]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)
[1700]1019            student_brain = students[i]
1020            spath = student_brain.getPath()
[1727]1021            student_object = student_brain.getObject()
[1620]1022            data = {}
[1700]1023            sid = data['id'] = student_brain.getId
1024            data['review_state'] = student_brain.review_state
[1707]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:
[1727]1032                    #from pdb import set_trace;set_trace()
[1707]1033                    continue
1034                for field in self.affected_types[pt]['fields']:
1035                    if hasattr(self,'get_from_doc_%s' % field):
[2078]1036                        data[field] = getattr(self,'get_from_doc_%s' % field)(doc,
1037                                                                              cached_data=cached_data)
[1707]1038                    else:
[1727]1039                        data[field] = getattr(doc,field,None)
1040            if not sub_objects:
[1700]1041                import_res = self.returning_import(id = sid)
1042                if not import_res:
[1620]1043                    continue
[1700]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)
[1815]1050                data['jamb_reg_no'] = import_record.Entryregno
[1727]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)
[1749]1057                    courses = []
[1727]1058                    for c in level_obj.objectIds():
1059                        if c.endswith('_co'):
1060                            courses.append(c[:-3])
1061                        else:
1062                            courses.append(c)
[1749]1063                    data['registered_courses'] = courses
[1700]1064            self.addRecord(**data)
[1620]1065        if pghandler: pghandler.finish()
1066    ###)
1067
[1700]1068    security.declarePrivate('notify_event_listener') ###(
[1620]1069    def notify_event_listener(self,event_type,object,infos):
1070        "listen for events"
[1716]1071        if not infos.has_key('rpath'):
1072            return
[1702]1073        pt = getattr(object,'portal_type',None)
1074        mt = getattr(object,'meta_type',None)
[1954]1075        students_catalog = self
[1702]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
[1700]1084        rpl = infos['rpath'].split('/')
[1731]1085        if pt == 'Student' and mt == 'CPS Proxy Folder'\
1086           and event_type == "sys_add_object":
[1700]1087            student_id = object.id
1088            try:
1089                self.addRecord(id = student_id)
1090            except ValueError:
1091                pass
1092            return
[1716]1093        elif pt == 'StudentCourseResult' and mt == 'CPS Proxy Folder':
1094            if event_type not in ("sys_add_object","sys_del_object"):
1095                return
[2033]1096            level_session = getattr(object.aq_parent.getContent(),'session','unknown')
1097            if level_session not in (self.getSessionId()[-2:],'2006/2007'):
1098                return
[1716]1099            course_id = object.getId()
[1954]1100            if course_id.endswith('_co'):
1101                course_id = course_id[:-3]
[1716]1102            student_id = object.absolute_url_path().split('/')[-4]
[1954]1103            res = students_catalog(id = student_id)
[1716]1104            if not res:
1105                return
1106            student_rec = res[0]
[2039]1107            registered_courses = getattr(student_rec,'registered_courses',None)
1108            if not registered_courses:
[1967]1109                registered_courses = []
[2039]1110            #import pdb;pdb.set_trace()
[1971]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
[2039]1116            elif registered_courses and event_type == "sys_del_object":
1117                removed = False
[1954]1118                while course_id in registered_courses:
[2039]1119                    removed = True
[1954]1120                    registered_courses.remove(course_id)
[2039]1121                if not removed:
1122                    return
[1716]1123            data['id'] = student_id
1124            data['registered_courses'] = registered_courses
[2069]1125            self.modifyRecord(record = student_rec, **data)
[1971]1126            return
[1716]1127        if pt not in self.affected_types.keys():
[1700]1128            return
[1716]1129        if event_type not in ('sys_modify_object'):
1130            return
[1700]1131        if mt == 'CPS Proxy Folder':
1132            return
[1716]1133        for field in self.affected_types[pt]['fields']:
[1700]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]
[1716]1139        self.modifyRecord(**data)
[1700]1140    ###)
[1620]1141
[1625]1142
[971]1143InitializeClass(StudentsCatalog)
1144
[1146]1145###)
1146
1147class CoursesCatalog(WAeUPTable): ###(
[1716]1148    security = ClassSecurityInfo()
[1146]1149
1150    meta_type = 'WAeUP Courses Catalog'
[2094]1151    name =  "courses_catalog"
[1146]1152    key = "code"
[2094]1153    def __init__(self,name=None):
1154        if name ==  None:
1155            name =  self.name
1156        WAeUPTable.__init__(self, name)
[1146]1157
[1716]1158    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
1159        """ clear the catalog, then re-index everything """
[1146]1160
[1716]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 """
[1724]1205        if clear:
1206            self._catalog.clear()
[1716]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)
[1724]1211        #from pdb import set_trace;set_trace()
[1716]1212        for i in xrange(num_objects):
1213            if pghandler: pghandler.report(i)
1214            course_brain = courses[i]
[1724]1215            course_doc = course_brain.getObject().getContent()
[1716]1216            pl = course_brain.getPath().split('/')
1217            data = {}
[1724]1218            for field in self.schema():
[1749]1219                data[field] = getattr(course_doc,field,None)
[1716]1220            data[self.key] = course_brain.getId
[1724]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)
[1716]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
[1724]1246        if event_type == "sys_add_object" and mt == 'CPS Proxy Folder':
[1716]1247            try:
1248                self.addRecord(**data)
1249            except ValueError:
[1724]1250                return
1251            course_id = object.getId()
1252            doc = object.getContent()
1253            if doc is None:
1254                return
1255            for field in self.schema():
[1749]1256                data[field] = getattr(doc,field,None)
[1724]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
[1716]1263        if event_type == "sys_del_object":
1264            self.deleteRecord(course_id)
[1724]1265            return
[1716]1266        if event_type == "sys_modify_object" and mt == 'Course':
[1724]1267            #from pdb import set_trace;set_trace()
[1716]1268            for field in self.schema():
[1749]1269                data[field] = getattr(object,field,None)
[1716]1270            course_id = object.aq_parent.getId()
1271            data[self.key] = course_id
[1724]1272            ai = rpl.index('academics')
1273            data['faculty'] = rpl[ai +1]
1274            data['department'] = rpl[ai +2]
[1716]1275            self.modifyRecord(**data)
1276    ###)
1277
1278
[1146]1279InitializeClass(CoursesCatalog)
[1151]1280###)
[1146]1281
[2084]1282class CourseResults(WAeUPTable): ###(
[2069]1283    security = ClassSecurityInfo()
1284
1285    meta_type = 'WAeUP Results Catalog'
1286    name = "course_results"
[2084]1287    key = "key" #student_id + level + course_id
[2094]1288    def __init__(self,name=None):
1289        if name ==  None:
1290            name = self.name
1291        WAeUPTable.__init__(self, name)
[2084]1292        self._queue = []
[2099]1293
[2084]1294    def addRecord(self, **data): ###(
[2094]1295        """add one record"""
[2099]1296
[2084]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    ###)
[2099]1312
[2094]1313    # def _p_resolveConflict(self, oldstate, committed, newstate):
1314    #     # Apply the changes made in going from old to newstate to
1315    #     # committed
[2099]1316
[2094]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.
[2069]1320
[2094]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
[2084]1359    def deleteRecord(self, uid): ###(
1360        self.uncatalog_object(uid)
1361        if uid in self._queue:
1362            self._queue.remove(uid)
1363    ###)
1364
[2094]1365    def updateCourseResults(self,student_id,level_id,portal_catalog_results,course_results): ###(
[2099]1366        # query = Eq('path',"%s/campus/students/%s/study_course/%s" %
[2094]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)
[2099]1372        # level_results = [r for r in course_results
[2094]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()
[2099]1384            data['student_id'] = student_id
1385            data['level_id'] = level_id
[2094]1386            data['queue_status'] = OBJECT_CREATED
[2099]1387            data['code'] = course_id
[2094]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
[2084]1393    def getCourses(self,student_id,level_id): ###(
[2094]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")
[2084]1402        query = Eq('student_id',student_id) & Eq('level_id', level_id)
[2094]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)
[2084]1409        carry_overs = []
1410        normal = []
[2099]1411        credits = 0
[2094]1412        for brain in course_results:
[2084]1413            d = {}
[2099]1414            credits += int(brain.credits)
[2084]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'
[2094]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'
[2084]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    ###)
[2099]1450
[2084]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
[2069]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
[2084]1502        self.refreshCatalog(clear=1, pghandler=handler)
[2069]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
[2084]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):
[2099]1526            if pghandler:
[2084]1527                pghandler.report(i)
1528            course_result_brain = course_results[i]
1529            path_list = course_result_brain.getPath().split('/')
[2094]1530            course_result_doc = course_result_brain.getObject().getContent()
[2084]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()
[2099]1538            data['student_id'] = student_id
1539            data['level_id'] = level_id
[2084]1540            data['queue_status'] = OBJECT_CREATED
1541            self.catalog_object(dict2ob(data), uid=key)
1542        if pghandler: pghandler.finish()
1543    ###)
[2069]1544
[2084]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
[2094]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)
[2084]1580    ###)
1581
1582InitializeClass(CourseResults)
[2069]1583###)
1584
[1625]1585class OnlinePaymentsImport(WAeUPTable): ###(
[1620]1586
1587    meta_type = 'WAeUP Online Payment Transactions'
[1625]1588    name = "online_payments_import"
[1620]1589    key = "order_id"
[2094]1590    def __init__(self,name=None):
1591        if name ==  None:
1592            name = self.name
1593        WAeUPTable.__init__(self, name)
[1620]1594
1595
[2069]1596InitializeClass(OnlinePaymentsImport)
[1620]1597###)
1598
[1151]1599class ReturningImport(WAeUPTable): ###(
[1146]1600
[1151]1601    meta_type = 'Returning Import Table'
1602    name = "returning_import"
[1146]1603    key = "matric_no"
[2094]1604    def __init__(self,name=None):
1605        if name ==  None:
1606            name = self.name
1607        WAeUPTable.__init__(self, name)
[1146]1608
1609
[1151]1610InitializeClass(ReturningImport)
1611###)
[1146]1612
1613class ResultsImport(WAeUPTable): ###(
1614
1615    meta_type = 'Results Import Table'
1616    name = "results_import"
1617    key = "key"
[2094]1618    def __init__(self,name=None):
1619        if name ==  None:
1620            name = self.name
1621        WAeUPTable.__init__(self, name)
[1146]1622
1623
1624InitializeClass(ResultsImport)
1625
1626###)
1627
1628class PaymentsCatalog(WAeUPTable): ###(
1629
1630    meta_type = 'WAeUP Payments Catalog'
1631    name = "students_catalog"
1632    key = "id"
[2094]1633    def __init__(self,name=None):
1634        if name ==  None:
1635            name = self.name
1636        WAeUPTable.__init__(self, name)
[1146]1637
1638
1639InitializeClass(PaymentsCatalog)
1640
1641###)
1642
[414]1643# BBB:
1644AccomodationTable = AccommodationTable
Note: See TracBrowser for help on using the repository browser.