source: WAeUP_SRP/trunk/WAeUPTables.py @ 1704

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

added reindexIndex for students_catalog

  • Property svn:keywords set to Id
File size: 21.9 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 1702 2007-04-24 13:52:06Z 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                              'study_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_study_mode(self,doc):
318        "return the students study_mode"
319        if doc is None:
320            return None
321        return getattr(doc,'current_study_mode',None)
322
323    def get_from_doc_entry_session(self,doc):
324        "return the students entry_session"
325        if doc is None:
326            return None
327        try:
328            digit = int(doc.jamb_reg_no[0])
329        except:
330            return "xx"
331        if digit < 8:
332            return "0%c" % doc.jamb_reg_no[0]
333        return "9%c" % doc.jamb_reg_no[0]
334
335    def get_from_doc_session(self,doc):
336        "return the students session"
337        if doc is None:
338            return None
339        return getattr(doc,'current_session',None)
340
341    def get_from_doc_course(self,doc):
342        "return the students study_course"
343        if doc is None:
344            return None
345        return getattr(doc,'study_course',None)
346
347    def get_from_doc_name(self,doc):
348        "return the students name from the personal"
349        if doc is None:
350            return None
351        return "%s %s %s" % (doc.firstname,doc.middlename,doc.lastname)
352
353    def get_from_doc_verdict(self,doc):
354        "return the students study_course"
355        if doc is None:
356            return None
357        return getattr(doc,'current_verdict',None)
358    ###)
359
360    def reindexIndex(self, name, REQUEST,pghandler=None): ###(
361        if isinstance(name, str):
362            name = (name,)
363        reindextypes = {}
364        reindex_special = []
365        #import pdb;pdb.set_trace()
366        for n in name:
367            if n in ("review_state","registered_courses"):
368                reindex_special.append(n)
369            else:
370                for pt in self.affected_types.keys():
371                    if n in self.affected_types[pt]:
372                        if reindextypes.has_key(pt):
373                            reindextypes[pt].append(n)
374                        else:
375                            reindextypes[pt]= [n]
376                        break
377        students = self.portal_catalog(portal_type="Student")
378        aq_portal = self.portal_catalog.evalAdvancedQuery
379        num_objects = len(students)
380        if pghandler:
381            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
382        noattr = set(('StudentClearance','StudentPersonal')) & set(reindextypes.keys())
383        for i in xrange(num_objects):
384            if pghandler: pghandler.report(i)
385            student_brain = students[i]
386            data = {}
387            modified = False
388            sid = data['id'] = student_brain.getId
389            if reindex_special and 'review_state' in reindex_special:
390                modified = True
391                data['review_state'] = student_brain.review_state
392            if reindextypes:
393                query = Eq('path',student_brain.getPath())
394                sub_brains = aq_portal(query)
395                modified = True
396                spath = student_brain.getPath()
397                if len(sub_brains) > 1:
398                    for sub_brain in sub_brains:
399                        if not sub_brain.portal_type in reindextypes.keys():
400                            continue
401                        doc = sub_brain.getObject().getContent()
402                        for field in self.affected_types[sub_brain.portal_type]:
403                            if hasattr(self,'get_from_doc_%s' % field):
404                                data[field] = getattr(self,'get_from_doc_%s' % field)(doc)
405                            else:
406                                data[field] = getattr(doc,field)
407                elif len(sub_brains) == 1 and noattr:
408                    import_res = self.returning_import(id = sid)
409                    if not import_res:
410                        continue
411                    import_record = import_res[0]
412                    data['matric_no'] = import_record.matric_no
413                    data['sex'] = import_record.Sex == 'F'
414                    data['name'] = "%s %s %s" % (import_record.Firstname,
415                                                 import_record.Middlename,
416                                                 import_record.Lastname)
417                    data['matric_no'] = import_record.Entryregno
418            if reindex_special and 'registered_courses' in reindex_special:
419                query = Eq('id','study_course') & Eq('path',student_brain.getPath())
420                brains = aq_portal(query)
421                while brains:
422                    study_course_path = brains[0].getPath()
423                    level_brains = self.portal_catalog(path = study_course_path,
424                                                       portal_type = "StudentStudyLevel")
425                    if not level_brains:
426                        break
427                    modified = True
428                    level_ids = [l.getId for l in level_brains]
429                    level_ids.sort()
430                    for l in level_brains:
431                        if l.getId == level_ids[-1]:
432                            level_path = l.getPath()
433                            break
434                    result_brains = self.portal_catalog(path = level_path,
435                                                        portal_type = "StudentCourseResult")
436                    course_ids = [cr.getId for cr in result_brains]
437                    courses = []
438                    for c in course_ids:
439                        if c.endswith('_co'):
440                            courses.append(c[:-3])
441                        else:
442                            courses.append(c)
443                    data['registered_courses'] = courses
444                    break
445            if modified:
446                self.modifyRecord(**data)
447        if pghandler: pghandler.finish()
448    ###)
449
450    def refreshCatalog(self, clear=0, pghandler=None): ###(
451        """ re-index everything we can find """
452        students_folder = self.portal_url.getPortalObject().campus.students
453
454        cat = self._catalog
455        paths = self._catalog.uids.items()
456        if clear:
457            paths = tuple(paths)
458            cat.clear()
459        students = self.portal_catalog(portal_type="Student")
460        num_objects = len(students)
461        if pghandler:
462            pghandler.init('Refreshing catalog: %s' % self.absolute_url(1), num_objects)
463        for i in xrange(num_objects):
464            if pghandler: pghandler.report(i)
465            student_brain = students[i]
466            spath = student_brain.getPath()
467            student_obj = student_brain.getObject()
468            data = {}
469            sid = data['id'] = student_brain.getId
470            data['review_state'] = student_brain.review_state
471            sub_brains = self.portal_catalog(path = spath)
472            if len(sub_brains) > 1:
473                for sub_brain in sub_brains:
474                    if not sub_brain.portal_type in self.affected_types.keys():
475                        continue
476                    doc = sub_brain.getObject().getContent()
477                    for field in self.affected_types[sub_brain.portal_type]:
478                        if hasattr(self,'get_from_doc_%s' % field):
479                            data[field] = getattr(self,'get_from_doc_%s' % field)(doc)
480                        else:
481                            data[field] = getattr(doc,field)
482            elif len(sub_brains) == 1:
483                #import pdb;pdb.set_trace()
484                import_res = self.returning_import(id = sid)
485                if not import_res:
486                    continue
487                import_record = import_res[0]
488                data['matric_no'] = import_record.matric_no
489                data['sex'] = import_record.Sex == 'F'
490                data['name'] = "%s %s %s" % (import_record.Firstname,
491                                             import_record.Middlename,
492                                             import_record.Lastname)
493                data['matric_no'] = import_record.Entryregno
494            study_course = getattr(student_obj,'study_course',None)
495            current_level = data.get('level',None)
496            data['registered_courses'] = []
497            if study_course and current_level and current_level in study_course.objectIds():
498                level_obj = getattr(study_course,current_level)
499                courses = []
500                for c in level_obj.objectIds():
501                    if c.endswith('_co'):
502                        courses.append(c[:-3])
503                    else:
504                        courses.append(c)
505                data['registered_courses'] = courses
506            self.addRecord(**data)
507        if pghandler: pghandler.finish()
508    ###)
509
510
511    security.declarePrivate('notify_event_listener') ###(
512    def notify_event_listener(self,event_type,object,infos):
513        "listen for events"
514        pt = getattr(object,'portal_type',None)
515        mt = getattr(object,'meta_type',None)
516        students_catalog = self.students_catalog
517        data = {}
518        if pt == 'Student' and\
519           mt == 'CPS Proxy Folder' and\
520           event_type.startswith('workflow'):
521            data['id'] = object.getId()
522            data['review_state'] = self.portal_workflow.getInfoFor(object,'review_state',None)
523            #from pdb import set_trace;set_trace()
524            students_catalog.modifyRecord(**data)
525            return
526        if pt not in self.affected_types.keys():
527            return
528        if not infos.has_key('rpath'):
529            return
530        rpl = infos['rpath'].split('/')
531        if pt == 'Student' and event_type == "sys_add_object":
532            student_id = object.id
533            try:
534                self.addRecord(id = student_id)
535            except ValueError:
536                pass
537            return
538        elif pt == 'CourseResult' and mt == 'CPS Proxy Folder':
539            from pdb import set_trace;set_trace()
540           
541        if event_type not in ('sys_modify_object'):
542            from pdb import set_trace;set_trace()
543            return
544        if mt == 'CPS Proxy Folder':
545            return
546        for field in self.affected_types[pt]:
547            if hasattr(self,'get_from_doc_%s' % field):
548                data[field] = getattr(self,'get_from_doc_%s' % field)(object)
549            else:
550                data[field] = getattr(object,field)
551        data['id'] = rpl[2]
552        students_catalog.modifyRecord(**data)
553    ###)
554
555
556InitializeClass(StudentsCatalog)
557
558###)
559
560class CoursesCatalog(WAeUPTable): ###(
561
562    meta_type = 'WAeUP Courses Catalog'
563    name = "students_catalog"
564    key = "code"
565    def __init__(self):
566        WAeUPTable.__init__(self, 'courses_catalog')
567
568
569InitializeClass(CoursesCatalog)
570###)
571
572class OnlinePaymentsImport(WAeUPTable): ###(
573
574    meta_type = 'WAeUP Online Payment Transactions'
575    name = "online_payments_import"
576    key = "order_id"
577    def __init__(self):
578        WAeUPTable.__init__(self, self.name)
579
580
581InitializeClass(CoursesCatalog)
582###)
583
584class ReturningImport(WAeUPTable): ###(
585
586    meta_type = 'Returning Import Table'
587    name = "returning_import"
588    key = "matric_no"
589    def __init__(self):
590        WAeUPTable.__init__(self, 'returning_import')
591
592
593InitializeClass(ReturningImport)
594###)
595
596class ResultsImport(WAeUPTable): ###(
597
598    meta_type = 'Results Import Table'
599    name = "results_import"
600    key = "key"
601    def __init__(self):
602        WAeUPTable.__init__(self, 'results_import')
603
604
605InitializeClass(ResultsImport)
606
607###)
608
609class PaymentsCatalog(WAeUPTable): ###(
610
611    meta_type = 'WAeUP Payments Catalog'
612    name = "students_catalog"
613    key = "id"
614    def __init__(self):
615        WAeUPTable.__init__(self, 'payments_catalog')
616
617
618InitializeClass(PaymentsCatalog)
619
620###)
621
622# BBB:
623AccomodationTable = AccommodationTable
Note: See TracBrowser for help on using the repository browser.