source: main/waeup.sirp/trunk/src/waeup/sirp/university/catalog.py @ 7387

Last change on this file since 7387 was 7211, checked in by uli, 13 years ago

Try to fix #65. Manually remove each course/certificate from a
container instead of using the (very fast) clear method. The latter
does not trigger IObjectRemoved events for courses and certificates.
Henrik, does it work better in UI now?
See https://trac.waeup.org/ticket/65

  • Property svn:keywords set to Id
File size: 6.8 KB
Line 
1## $Id: catalog.py 7211 2011-11-26 16:01:30Z uli $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
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 02111-1307 USA
17##
18"""Catalog and searching components for academics stuff.
19"""
20import grok
21from hurry.query import Eq, Text
22from hurry.query.query import Query
23from zope.catalog.interfaces import ICatalog
24from zope.component import getUtility
25from zope.component.interfaces import ComponentLookupError
26from zope.index.text.parsetree import ParseError
27from zope.intid import IIntIds
28#from waeup.sirp.catalog import QueryResultItem
29from waeup.sirp.interfaces import IUniversity, IQueryResultItem
30from waeup.sirp.university.interfaces import (
31    ICourse, ICertificateCourse, IDepartment,
32    ICertificate,
33    )
34
35class CourseIndexes(grok.Indexes):
36    """This catalog is needed for building sources.
37    """
38    grok.site(IUniversity)
39    grok.name('courses_catalog')
40    grok.context(ICourse)
41
42    code = grok.index.Field(attribute='code')
43    title = grok.index.Text(attribute='title')
44
45class CertificatesIndexes(grok.Indexes):
46    """This catalog is needed for building sources.
47    """
48    grok.site(IUniversity)
49    grok.name('certificates_catalog')
50    grok.context(ICertificate)
51
52    code = grok.index.Field(attribute='code')
53    application_category = grok.index.Field(attribute='application_category')
54    title = grok.index.Text(attribute='title')
55
56class CertificateCoursesIndexes(grok.Indexes):
57    """This catalog is needed for automatic removal of certificate courses
58    and later for selection course tickets in the students section.
59    """
60    grok.site(IUniversity)
61    grok.name('certcourses_catalog')
62    grok.context(ICertificateCourse)
63
64    course_code = grok.index.Field(attribute='getCourseCode')
65    level = grok.index.Field(attribute='level')
66
67@grok.subscribe(ICourse, grok.IObjectAddedEvent)
68def handle_course_added(obj, event):
69    """Index an added course with the local catalog.
70
71    Courses are not indexed automatically, as they are not a
72    dictionary subitem of the accompanied site object
73    (`IUniversity`). I.e. one cannot get them by asking for
74    ``app['FACCODE']['DEPTCODE']['COURSECODE']`` but one has to ask for
75    ``app.faculties['FACCODE']['DEPTCODE'].courses['COURSECODE']``.
76
77    Once, a course is indexed we can leave the further handling to
78    the default component architechture. At least removals will
79    be handled correctly then (and the course unindexed).
80    """
81    try:
82        cat = getUtility(ICatalog, name='courses_catalog')
83    except ComponentLookupError:
84        # catalog not available. This might happen during tests.
85        return
86    intids = getUtility(IIntIds)
87    index = cat['code']
88    index.index_doc(intids.getId(obj), obj)
89
90@grok.subscribe(ICourse, grok.IObjectAddedEvent)
91def handle_certificate_added(obj, event):
92    """Index an added certificate with the local catalog.
93
94    See handleCourseAdd.
95    """
96    try:
97      cat = getUtility(ICatalog, name='certificates_catalog')
98    except ComponentLookupError:
99      # catalog not available. This might happen during tests.
100      return
101    intids = getUtility(IIntIds)
102    index = cat['code']
103    index.index_doc(intids.getId(obj), obj)
104
105@grok.subscribe(ICertificateCourse, grok.IObjectAddedEvent)
106def handlecertificatecourse_added(obj, event):
107    """Index an added certificatecourse with the local catalog.
108
109    See handleCourseAdd.
110    """
111    try:
112      cat = getUtility(ICatalog, name='certcourses_catalog')
113    except ComponentLookupError:
114      # catalog not available. This might happen during tests.
115      return
116    intids = getUtility(IIntIds)
117    index = cat['course_code']
118    index.index_doc(intids.getId(obj), obj)
119
120@grok.subscribe(IDepartment, grok.IObjectRemovedEvent)
121def handle_department_removed(obj, event):
122    """Clear courses and certificates when a department is killed.
123    """
124    # We cannot use the 'clear()' method of respective subcontainers
125    # (courses, certificates), because that would not trigger
126    # IObjectRemoved events.
127    for subobj_name in ['courses', 'certificates']:
128        key_list = list(getattr(obj, subobj_name, []).keys())
129        for key in key_list:
130            del getattr(obj, subobj_name)[key]
131    return
132
133class CoursesQueryResultItem(object):
134    grok.implements(IQueryResultItem)
135
136    def __init__(self, context, view):
137        self.context = context
138        self.url = view.url(context)
139        self.title = context.title
140        self.code = context.code
141        self.type = 'Course'
142
143class CertificatesQueryResultItem(object):
144    grok.implements(IQueryResultItem)
145
146    def __init__(self, context, view):
147        self.context = context
148        self.url = view.url(context)
149        self.title = context.title
150        self.code = context.code
151        self.type = 'Certificate'
152
153class CertificateCoursesQueryResultItem(object):
154    grok.implements(IQueryResultItem)
155
156    def __init__(self, context, view):
157        self.context = context
158        self.url = view.url(context)
159        self.title = context.course.title
160        self.code = context.getCourseCode
161        self.type = 'Course Referrer'
162
163def search(query=None, view=None):
164    if not query:
165        view.flash('Empty search string.')
166        return
167
168    hitlist = []
169    try:
170        results = Query().searchResults(
171            Eq(('courses_catalog', 'code'), query))
172        for result in results:
173            hitlist.append(CoursesQueryResultItem(result, view=view))
174        results = Query().searchResults(
175            Text(('courses_catalog', 'title'), query))
176        for result in results:
177            hitlist.append(CoursesQueryResultItem(result, view=view))
178        results = Query().searchResults(
179            Eq(('certificates_catalog', 'code'), query))
180        for result in results:
181            hitlist.append(CertificatesQueryResultItem(result, view=view))
182        results = Query().searchResults(
183            Text(('certificates_catalog', 'title'), query))
184        for result in results:
185            hitlist.append(CertificatesQueryResultItem(result, view=view))
186        results = Query().searchResults(
187            Eq(('certcourses_catalog', 'course_code'), query))
188        for result in results:
189            hitlist.append(CertificateCoursesQueryResultItem(result, view=view))
190    except ParseError:
191        view.flash('Search string not allowed.')
192        return
193    return hitlist
Note: See TracBrowser for help on using the repository browser.