source: WAeUP_SRP/trunk/WAeUPTables.py @ 1705

Last change on this file since 1705 was 1705, checked in by joachim, 18 years ago

New indexes and fields

  • Property svn:keywords set to Id
File size: 22.2 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 1705 2007-04-24 18:18:05Z joachim $
20
21from zope.interface import implements
22from Globals import InitializeClass
23from Products.ZCatalog.ZCatalog import ZCatalog
24from Products.ZCatalog.ProgressHandler import ZLogHandler
25from AccessControl import ClassSecurityInfo
26from Products.CMFCore.permissions import ModifyPortalContent
27import urllib
28import DateTime,time
29import csv,re
30import logging
31import Globals
32p_home = Globals.package_home(globals())
33i_home = Globals.INSTANCE_HOME
34from Products.AdvancedQuery import Eq, Between, Le,In
35
36from interfaces import IWAeUPTable
37
38class AttributeHolder(object):
39    pass
40
41def dict2ob(dict):
42    ob = AttributeHolder()
43    for key, value in dict.items():
44        setattr(ob, key, value)
45    return ob
46
47
48class WAeUPTable(ZCatalog): ###(
49
50    implements(IWAeUPTable)
51    security = ClassSecurityInfo()
52
53    def refreshCatalog(self, clear=0, pghandler=None):
54        """ don't refresh for a normal table """
55
56        if self.REQUEST and self.REQUEST.RESPONSE:
57            self.REQUEST.RESPONSE.redirect(
58              URL1 +
59              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20refresh%20not%20implemented')
60
61    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None):
62        """ clears the whole enchilada """
63        #self._catalog.clear()
64
65        if REQUEST and RESPONSE:
66            RESPONSE.redirect(
67              URL1 +
68              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Clearing%20disabled')
69
70    def addRecord(self, **data):
71        # The uid is the same as "bed".
72        uid = data[self.key]
73        res = self.searchResults({"%s" % self.key : uid})
74        if len(res) > 0:
75            raise ValueError("More than one record with uid %s" % uid)
76        self.catalog_object(dict2ob(data), uid=uid)
77        return uid
78
79    def deleteRecord(self, uid):
80        self.uncatalog_object(uid)
81
82    def searchAndSetRecord(self, **data):
83        raise NotImplemented
84
85    def modifyRecord(self, **data):
86        #records = self.searchResults(uid=uid)
87        uid = data[self.key]
88        records = self.searchResults({"%s" % self.key : uid})
89        if len(records) > 1:
90            # Can not happen, but anyway...
91            raise ValueError("More than one record with uid %s" % uid)
92        if len(records) == 0:
93            raise KeyError("No record for uid %s" % uid)
94        record = records[0]
95        record_data = {}
96        for field in self.schema() + self.indexes():
97            record_data[field] = getattr(record, field)
98        # Add the updated data:
99        record_data.update(data)
100        self.catalog_object(dict2ob(record_data), uid)
101
102    def reindexIndex(self, name, REQUEST,pghandler=None):
103        if isinstance(name, str):
104            name = (name,)
105        paths = self._catalog.uids.items()
106        i = 0
107        #import pdb;pdb.set_trace()
108        for p,rid in paths:
109            i += 1
110            metadata = self.getMetadataForRID(rid)
111            record_data = {}
112            for field in name:
113                record_data[field] = metadata.get(field)
114            uid = metadata.get(self.key)
115            self.catalog_object(dict2ob(record_data), uid, idxs=name,
116                                update_metadata=0)
117
118    security.declareProtected(ModifyPortalContent,"exportAllRecords")
119    def exportAllRecords(self):
120        "export a WAeUPTable"
121        #import pdb;pdb.set_trace()
122        fields = [field for field in self.schema()]
123        format = ','.join(['"%%(%s)s"' % fn for fn in fields])
124        csv = []
125        csv.append(','.join(['"%s"' % fn for fn in fields]))
126        for uid in self._catalog.uids:
127            records = self.searchResults({"%s" % self.key : uid})
128            if len(records) > 1:
129                # Can not happen, but anyway...
130                raise ValueError("More than one record with uid %s" % uid)
131            if len(records) == 0:
132                raise KeyError("No record for uid %s" % uid)
133            rec = records[0]
134            csv.append(format % rec)
135        current = DateTime.DateTime().strftime("%d-%m-%y_%H_%M_%S")
136        open("%s/import/%s-%s.csv" % (i_home,self.getId(),current),"w+").write('\n'.join(csv))
137
138###)
139
140class AccommodationTable(WAeUPTable): ###(
141
142    meta_type = 'WAeUP Accommodation Tool'
143    name = "accommodation"
144    key = "bed"
145    def __init__(self):
146        WAeUPTable.__init__(self, 'portal_accommodation')
147
148    def searchAndReserveBed(self, student_id,bed_type):
149        records = self.searchResults({'student' : student_id})
150        if len(records) > 0:
151            return -1,"Student with Id %s already booked bed %s." % (student_id,records[0].bed)
152
153        records = [r for r in self.searchResults({'bed_type' : bed_type}) if not r.student]
154        #import pdb;pdb.set_trace()
155        if len(records) == 0:
156            return -2,"No bed available"
157        rec = records[0]
158        self.modifyRecord(bed=rec.bed,student=student_id)
159        s_logger = logging.getLogger('WAeUPTables.AccommodationTable.searchAndReserveBed')
160        s_logger.info('%s reserved bed %s' % (student_id,rec.bed))
161        return 1,rec.bed
162
163
164InitializeClass(AccommodationTable)
165
166###)
167
168class PinTable(WAeUPTable): ###(
169    from ZODB.POSException import ConflictError
170    meta_type = 'WAeUP Pin Tool'
171    name = "pins"
172    key = 'pin'
173    def __init__(self):
174        WAeUPTable.__init__(self, 'portal_pins')
175
176
177    def searchAndSetRecord(self, uid, student_id,prefix):
178        #records = self.searchResults(uid=uid)
179        records = self.searchResults(student = student_id)
180        #import pdb;pdb.set_trace()
181        if len(records) > 0:
182            for r in records:
183                if r.pin != uid and r.prefix_batch.startswith(prefix):
184                    return -2
185        records = self.searchResults({"%s" % self.key : uid})
186        if len(records) > 1:
187            # Can not happen, but anyway...
188            raise ValueError("More than one record with uid %s" % uid)
189        if len(records) == 0:
190            return -1
191        record = records[0]
192        if record.student == "":
193            record_data = {}
194            for field in self.schema() + self.indexes():
195                record_data[field] = getattr(record, field)
196            # Add the updated data:
197            record_data['student'] = student_id
198            try:
199                self.catalog_object(dict2ob(record_data), uid)
200                return 1
201            except ConflictError:
202                return 2
203        if record.student.upper() != student_id.upper():
204            return 0
205        if record.student.upper() == student_id.upper():
206            return 2
207        return -3
208
209InitializeClass(PinTable)
210
211###)
212
213class PumeResultsTable(WAeUPTable): ###(
214
215    meta_type = 'WAeUP PumeResults Tool'
216    name = "pumeresults"
217    key = "jamb_reg_no"
218    def __init__(self):
219        WAeUPTable.__init__(self, 'portal_pumeresults')
220
221
222InitializeClass(PumeResultsTable)
223
224###)
225
226class StudentsCatalog(WAeUPTable): ###(
227    security = ClassSecurityInfo()
228
229    meta_type = 'WAeUP Students Catalog'
230    name = "students_catalog"
231    key = "id"
232    affected_types = {   ###(
233                      'StudentApplication':
234                             ('jamb_reg_no',
235                              'entry_mode',
236                              'entry_session',
237                              ),
238                      'StudentClearance':
239                             ('matric_no',
240                              ),
241                         'StudentPersonal':
242                             ('name',
243                              'sex',
244                              'email',
245                              'phone',
246                              ),
247                         'StudentStudyCourse':
248                             ('course',
249                              'faculty',
250                              'department',
251                              'level',
252                              'mode',
253                              'session',
254                              'verdict',
255                              ),
256                         }
257    ###)
258
259    def __init__(self):
260        WAeUPTable.__init__(self, 'students_catalog')
261        return
262
263    def manage_catalogClear(self, REQUEST=None, RESPONSE=None, URL1=None):
264        """ clears the whole enchilada """
265        self._catalog.clear()
266
267        if REQUEST and RESPONSE:
268            RESPONSE.redirect(
269              URL1 +
270              '/manage_catalogAdvanced?manage_tabs_message=Catalog%20Cleared')
271
272    def manage_catalogReindex(self, REQUEST, RESPONSE, URL1): ###(
273        """ clear the catalog, then re-index everything """
274
275        elapse = time.time()
276        c_elapse = time.clock()
277
278        pgthreshold = self._getProgressThreshold()
279        handler = (pgthreshold > 0) and ZLogHandler(pgthreshold) or None
280        self.refreshCatalog(clear=1, pghandler=handler)
281
282        elapse = time.time() - elapse
283        c_elapse = time.clock() - c_elapse
284
285        RESPONSE.redirect(
286            URL1 +
287            '/manage_catalogAdvanced?manage_tabs_message=' +
288            urllib.quote('Catalog Updated \n'
289                         'Total time: %s\n'
290                         'Total CPU time: %s' % (`elapse`, `c_elapse`)))
291    ###)
292
293    def get_from_doc_department(self,doc): ###(
294        "return the students department"
295        if doc is None:
296            return None
297        certificate_res = self.portal_catalog(id = doc.study_course)
298        if len(certificate_res) != 1:
299            return None
300        return certificate_res[0].getPath().split('/')[-3]
301
302    def get_from_doc_faculty(self,doc):
303        "return the students faculty"
304        if doc is None:
305            return None
306        certificate_res = self.portal_catalog(id = doc.study_course)
307        if len(certificate_res) != 1:
308            return None
309        return certificate_res[0].getPath().split('/')[-4]
310
311    def get_from_doc_level(self,doc):
312        "return the students level"
313        if doc is None:
314            return None
315        return getattr(doc,'current_level',None)
316
317    def get_from_doc_mode(self,doc):
318        "return the students mode"
319        if doc is None:
320            return None
321        cm = getattr(doc,'current_mode',None)
322        return cm
323       
324
325    def get_from_doc_session(self,doc):
326        "return the students current_session"
327        if doc is None:
328            return None
329        return getattr(doc,'current_session',None)
330
331    def get_from_doc_entry_session(self,doc):
332        "return the students entry_session"
333        if doc is None:
334            return None
335        es = getattr(doc,'entry_session',None)
336        if len(es) == 2:
337            return es
338        try:
339            digit = int(doc.jamb_reg_no[0])
340        except:
341            return "xx"
342        if digit < 8:
343            return "0%c" % doc.jamb_reg_no[0]
344        return "9%c" % doc.jamb_reg_no[0]
345
346    def get_from_doc_session(self,doc):
347        "return the students session"
348        if doc is None:
349            return None
350        return getattr(doc,'current_session',None)
351
352    def get_from_doc_course(self,doc):
353        "return the students study_course"
354        if doc is None:
355            return None
356        return getattr(doc,'study_course',None)
357
358    def get_from_doc_name(self,doc):
359        "return the students name from the personal"
360        if doc is None:
361            return None
362        return "%s %s %s" % (doc.firstname,doc.middlename,doc.lastname)
363
364    def get_from_doc_verdict(self,doc):
365        "return the students study_course"
366        if doc is None:
367            return None
368        return getattr(doc,'current_verdict',None)
369    ###)
370
371    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
372        if isinstance(name, str):
373            name = (name,)
374        reindextypes = {}
375        reindex_special = []
376        #import pdb;pdb.set_trace()
377        for n in name:
378            if n in ("review_state","registered_courses"):
379                reindex_special.append(n)
380            else:
381                for pt in self.affected_types.keys():
382                    if n in self.affected_types[pt]:
383                        if reindextypes.has_key(pt):
384                            reindextypes[pt].append(n)
385                        else:
386                            reindextypes[pt]= [n]
387                        break
388        students = self.portal_catalog(portal_type="Student")
389        aq_portal = self.portal_catalog.evalAdvancedQuery
390        num_objects = len(students)
391        if pghandler:
392            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
393        noattr = set(('StudentClearance','StudentPersonal')) & set(reindextypes.keys())
394        for i in xrange(num_objects):
395            if pghandler: pghandler.report(i)
396            student_brain = students[i]
397            data = {}
398            modified = False
399            sid = data['id'] = student_brain.getId
400            if reindex_special and 'review_state' in reindex_special:
401                modified = True
402                data['review_state'] = student_brain.review_state
403            if reindextypes:
404                query = Eq('path',student_brain.getPath())
405                sub_brains = aq_portal(query)
406                modified = True
407                spath = student_brain.getPath()
408                if len(sub_brains) > 1:
409                    for sub_brain in sub_brains:
410                        if not sub_brain.portal_type in reindextypes.keys():
411                            continue
412                        doc = sub_brain.getObject().getContent()
413                        for field in self.affected_types[sub_brain.portal_type]:
414                            if hasattr(self,'get_from_doc_%s' % field):
415                                data[field] = getattr(self,'get_from_doc_%s' % field)(doc)
416                            else:
417                                data[field] = getattr(doc,field)
418                elif len(sub_brains) == 1 and noattr:
419                    import_res = self.returning_import(id = sid)
420                    if not import_res:
421                        continue
422                    import_record = import_res[0]
423                    data['matric_no'] = import_record.matric_no
424                    data['sex'] = import_record.Sex == 'F'
425                    data['name'] = "%s %s %s" % (import_record.Firstname,
426                                                 import_record.Middlename,
427                                                 import_record.Lastname)
428                    data['matric_no'] = import_record.Entryregno
429            if reindex_special and 'registered_courses' in reindex_special:
430                query = Eq('id','study_course') & Eq('path',student_brain.getPath())
431                brains = aq_portal(query)
432                while brains:
433                    study_course_path = brains[0].getPath()
434                    level_brains = self.portal_catalog(path = study_course_path,
435                                                       portal_type = "StudentStudyLevel")
436                    if not level_brains:
437                        break
438                    modified = True
439                    level_ids = [l.getId for l in level_brains]
440                    level_ids.sort()
441                    for l in level_brains:
442                        if l.getId == level_ids[-1]:
443                            level_path = l.getPath()
444                            break
445                    result_brains = self.portal_catalog(path = level_path,
446                                                        portal_type = "StudentCourseResult")
447                    course_ids = [cr.getId for cr in result_brains]
448                    courses = []
449                    for c in course_ids:
450                        if c.endswith('_co'):
451                            courses.append(c[:-3])
452                        else:
453                            courses.append(c)
454                    data['registered_courses'] = courses
455                    break
456            if modified:
457                self.modifyRecord(**data)
458        if pghandler: pghandler.finish()
459    ###)
460
461    def refreshCatalog(self, clear=0, pghandler=None): ###(
462        """ re-index everything we can find """
463        students_folder = self.portal_url.getPortalObject().campus.students
464
465        cat = self._catalog
466        paths = self._catalog.uids.items()
467        if clear:
468            paths = tuple(paths)
469            cat.clear()
470        students = self.portal_catalog(portal_type="Student")
471        num_objects = len(students)
472        if pghandler:
473            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
474        for i in xrange(num_objects):
475            if pghandler: pghandler.report(i)
476            student_brain = students[i]
477            spath = student_brain.getPath()
478            student_obj = student_brain.getObject()
479            data = {}
480            sid = data['id'] = student_brain.getId
481            data['review_state'] = student_brain.review_state
482            sub_brains = self.portal_catalog(path = spath)
483            if len(sub_brains) > 1:
484                for sub_brain in sub_brains:
485                    if not sub_brain.portal_type in self.affected_types.keys():
486                        continue
487                    doc = sub_brain.getObject().getContent()
488                    for field in self.affected_types[sub_brain.portal_type]:
489                        if hasattr(self,'get_from_doc_%s' % field):
490                            data[field] = getattr(self,'get_from_doc_%s' % field)(doc)
491                        else:
492                            data[field] = getattr(doc,field)
493            elif len(sub_brains) == 1:
494                #import pdb;pdb.set_trace()
495                import_res = self.returning_import(id = sid)
496                if not import_res:
497                    continue
498                import_record = import_res[0]
499                data['matric_no'] = import_record.matric_no
500                data['sex'] = import_record.Sex == 'F'
501                data['name'] = "%s %s %s" % (import_record.Firstname,
502                                             import_record.Middlename,
503                                             import_record.Lastname)
504                data['matric_no'] = import_record.Entryregno
505            study_course = getattr(student_obj,'study_course',None)
506            current_level = data.get('level',None)
507            data['registered_courses'] = []
508            if study_course and current_level and current_level in study_course.objectIds():
509                level_obj = getattr(study_course,current_level)
510                courses = []
511                for c in level_obj.objectIds():
512                    if c.endswith('_co'):
513                        courses.append(c[:-3])
514                    else:
515                        courses.append(c)
516                data['registered_courses'] = courses
517            self.addRecord(**data)
518        if pghandler: pghandler.finish()
519    ###)
520
521
522    security.declarePrivate('notify_event_listener') ###(
523    def notify_event_listener(self,event_type,object,infos):
524        "listen for events"
525        pt = getattr(object,'portal_type',None)
526        mt = getattr(object,'meta_type',None)
527        students_catalog = self.students_catalog
528        data = {}
529        if pt == 'Student' and\
530           mt == 'CPS Proxy Folder' and\
531           event_type.startswith('workflow'):
532            data['id'] = object.getId()
533            data['review_state'] = self.portal_workflow.getInfoFor(object,'review_state',None)
534            #from pdb import set_trace;set_trace()
535            students_catalog.modifyRecord(**data)
536            return
537        if pt not in self.affected_types.keys():
538            return
539        if not infos.has_key('rpath'):
540            return
541        rpl = infos['rpath'].split('/')
542        if pt == 'Student' and event_type == "sys_add_object":
543            student_id = object.id
544            try:
545                self.addRecord(id = student_id)
546            except ValueError:
547                pass
548            return
549        elif pt == 'CourseResult' and mt == 'CPS Proxy Folder':
550            from pdb import set_trace;set_trace()
551           
552        if event_type not in ('sys_modify_object'):
553            from pdb import set_trace;set_trace()
554            return
555        if mt == 'CPS Proxy Folder':
556            return
557        for field in self.affected_types[pt]:
558            if hasattr(self,'get_from_doc_%s' % field):
559                data[field] = getattr(self,'get_from_doc_%s' % field)(object)
560            else:
561                data[field] = getattr(object,field)
562        data['id'] = rpl[2]
563        students_catalog.modifyRecord(**data)
564    ###)
565
566
567InitializeClass(StudentsCatalog)
568
569###)
570
571class CoursesCatalog(WAeUPTable): ###(
572
573    meta_type = 'WAeUP Courses Catalog'
574    name = "students_catalog"
575    key = "code"
576    def __init__(self):
577        WAeUPTable.__init__(self, 'courses_catalog')
578
579
580InitializeClass(CoursesCatalog)
581###)
582
583class OnlinePaymentsImport(WAeUPTable): ###(
584
585    meta_type = 'WAeUP Online Payment Transactions'
586    name = "online_payments_import"
587    key = "order_id"
588    def __init__(self):
589        WAeUPTable.__init__(self, self.name)
590
591
592InitializeClass(CoursesCatalog)
593###)
594
595class ReturningImport(WAeUPTable): ###(
596
597    meta_type = 'Returning Import Table'
598    name = "returning_import"
599    key = "matric_no"
600    def __init__(self):
601        WAeUPTable.__init__(self, 'returning_import')
602
603
604InitializeClass(ReturningImport)
605###)
606
607class ResultsImport(WAeUPTable): ###(
608
609    meta_type = 'Results Import Table'
610    name = "results_import"
611    key = "key"
612    def __init__(self):
613        WAeUPTable.__init__(self, 'results_import')
614
615
616InitializeClass(ResultsImport)
617
618###)
619
620class PaymentsCatalog(WAeUPTable): ###(
621
622    meta_type = 'WAeUP Payments Catalog'
623    name = "students_catalog"
624    key = "id"
625    def __init__(self):
626        WAeUPTable.__init__(self, 'payments_catalog')
627
628
629InitializeClass(PaymentsCatalog)
630
631###)
632
633# BBB:
634AccomodationTable = AccommodationTable
Note: See TracBrowser for help on using the repository browser.