source: main/waeup.kofa/trunk/src/waeup/kofa/university/catalog.py @ 11089

Last change on this file since 11089 was 9805, checked in by Henrik Bettermann, 12 years ago

Catch strange AttributeError?: "'module' object has no attribute 'index'" when importing the search function.

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