1 | """Catalog and searching components for academics stuff. |
---|
2 | """ |
---|
3 | import grok |
---|
4 | from hurry.query import Eq, Text |
---|
5 | from hurry.query.query import Query |
---|
6 | from zope.catalog.interfaces import ICatalog |
---|
7 | from zope.component import getUtility |
---|
8 | from zope.component.interfaces import ComponentLookupError |
---|
9 | from zope.index.text.parsetree import ParseError |
---|
10 | from zope.intid import IIntIds |
---|
11 | #from waeup.sirp.catalog import QueryResultItem |
---|
12 | from waeup.sirp.interfaces import IUniversity, IQueryResultItem |
---|
13 | from waeup.sirp.university.interfaces import ( |
---|
14 | ICourse, ICertificateCourse, IDepartment, |
---|
15 | ICertificate, |
---|
16 | ) |
---|
17 | |
---|
18 | class CourseIndexes(grok.Indexes): |
---|
19 | """This catalog is needed for building sources. |
---|
20 | """ |
---|
21 | grok.site(IUniversity) |
---|
22 | grok.name('courses_catalog') |
---|
23 | grok.context(ICourse) |
---|
24 | |
---|
25 | code = grok.index.Field(attribute='code') |
---|
26 | title = grok.index.Text(attribute='title') |
---|
27 | |
---|
28 | class CertificatesIndexes(grok.Indexes): |
---|
29 | """This catalog is needed for building sources. |
---|
30 | """ |
---|
31 | grok.site(IUniversity) |
---|
32 | grok.name('certificates_catalog') |
---|
33 | grok.context(ICertificate) |
---|
34 | |
---|
35 | code = grok.index.Field(attribute='code') |
---|
36 | application_category = grok.index.Field(attribute='application_category') |
---|
37 | title = grok.index.Text(attribute='title') |
---|
38 | |
---|
39 | class CertificateCoursesIndexes(grok.Indexes): |
---|
40 | """This catalog is needed for automatic removal of certificate courses |
---|
41 | and later for selection course tickets in the students section. |
---|
42 | """ |
---|
43 | grok.site(IUniversity) |
---|
44 | grok.name('certcourses_catalog') |
---|
45 | grok.context(ICertificateCourse) |
---|
46 | |
---|
47 | course_code = grok.index.Field(attribute='getCourseCode') |
---|
48 | level = grok.index.Field(attribute='level') |
---|
49 | |
---|
50 | @grok.subscribe(ICourse, grok.IObjectAddedEvent) |
---|
51 | def handle_course_added(obj, event): |
---|
52 | """Index an added course with the local catalog. |
---|
53 | |
---|
54 | Courses are not indexed automatically, as they are not a |
---|
55 | dictionary subitem of the accompanied site object |
---|
56 | (`IUniversity`). I.e. one cannot get them by asking for |
---|
57 | ``app['FACCODE']['DEPTCODE']['COURSECODE']`` but one has to ask for |
---|
58 | ``app.faculties['FACCODE']['DEPTCODE'].courses['COURSECODE']``. |
---|
59 | |
---|
60 | Once, a course is indexed we can leave the further handling to |
---|
61 | the default component architechture. At least removals will |
---|
62 | be handled correctly then (and the course unindexed). |
---|
63 | """ |
---|
64 | try: |
---|
65 | cat = getUtility(ICatalog, name='courses_catalog') |
---|
66 | except ComponentLookupError: |
---|
67 | # catalog not available. This might happen during tests. |
---|
68 | return |
---|
69 | intids = getUtility(IIntIds) |
---|
70 | index = cat['code'] |
---|
71 | index.index_doc(intids.getId(obj), obj) |
---|
72 | |
---|
73 | @grok.subscribe(ICourse, grok.IObjectAddedEvent) |
---|
74 | def handle_certificate_added(obj, event): |
---|
75 | """Index an added certificate with the local catalog. |
---|
76 | |
---|
77 | See handleCourseAdd. |
---|
78 | """ |
---|
79 | try: |
---|
80 | cat = getUtility(ICatalog, name='certificates_catalog') |
---|
81 | except ComponentLookupError: |
---|
82 | # catalog not available. This might happen during tests. |
---|
83 | return |
---|
84 | intids = getUtility(IIntIds) |
---|
85 | index = cat['code'] |
---|
86 | index.index_doc(intids.getId(obj), obj) |
---|
87 | |
---|
88 | @grok.subscribe(ICertificateCourse, grok.IObjectAddedEvent) |
---|
89 | def handlecertificatecourse_added(obj, event): |
---|
90 | """Index an added certificatecourse with the local catalog. |
---|
91 | |
---|
92 | See handleCourseAdd. |
---|
93 | """ |
---|
94 | try: |
---|
95 | cat = getUtility(ICatalog, name='certcourses_catalog') |
---|
96 | except ComponentLookupError: |
---|
97 | # catalog not available. This might happen during tests. |
---|
98 | return |
---|
99 | intids = getUtility(IIntIds) |
---|
100 | index = cat['course_code'] |
---|
101 | index.index_doc(intids.getId(obj), obj) |
---|
102 | |
---|
103 | @grok.subscribe(IDepartment, grok.IObjectRemovedEvent) |
---|
104 | def handle_department_removed(obj, event): |
---|
105 | """Clear courses and certificates when a department is killed. |
---|
106 | """ |
---|
107 | obj.courses.clear() |
---|
108 | obj.certificates.clear() |
---|
109 | return |
---|
110 | |
---|
111 | class CoursesQueryResultItem(object): |
---|
112 | grok.implements(IQueryResultItem) |
---|
113 | |
---|
114 | def __init__(self, context, view): |
---|
115 | self.context = context |
---|
116 | self.url = view.url(context) |
---|
117 | self.title = context.title |
---|
118 | self.code = context.code |
---|
119 | self.type = 'Course' |
---|
120 | |
---|
121 | class CertificatesQueryResultItem(object): |
---|
122 | grok.implements(IQueryResultItem) |
---|
123 | |
---|
124 | def __init__(self, context, view): |
---|
125 | self.context = context |
---|
126 | self.url = view.url(context) |
---|
127 | self.title = context.title |
---|
128 | self.code = context.code |
---|
129 | self.type = 'Certificate' |
---|
130 | |
---|
131 | class CertificateCoursesQueryResultItem(object): |
---|
132 | grok.implements(IQueryResultItem) |
---|
133 | |
---|
134 | def __init__(self, context, view): |
---|
135 | self.context = context |
---|
136 | self.url = view.url(context) |
---|
137 | self.title = context.course.title |
---|
138 | self.code = context.getCourseCode |
---|
139 | self.type = 'Course Referrer' |
---|
140 | |
---|
141 | def search(query=None, view=None): |
---|
142 | if not query: |
---|
143 | view.flash('Empty search string.') |
---|
144 | return |
---|
145 | |
---|
146 | hitlist = [] |
---|
147 | try: |
---|
148 | results = Query().searchResults( |
---|
149 | Eq(('courses_catalog', 'code'), query)) |
---|
150 | for result in results: |
---|
151 | hitlist.append(CoursesQueryResultItem(result, view=view)) |
---|
152 | results = Query().searchResults( |
---|
153 | Text(('courses_catalog', 'title'), query)) |
---|
154 | for result in results: |
---|
155 | hitlist.append(CoursesQueryResultItem(result, view=view)) |
---|
156 | results = Query().searchResults( |
---|
157 | Eq(('certificates_catalog', 'code'), query)) |
---|
158 | for result in results: |
---|
159 | hitlist.append(CertificatesQueryResultItem(result, view=view)) |
---|
160 | results = Query().searchResults( |
---|
161 | Text(('certificates_catalog', 'title'), query)) |
---|
162 | for result in results: |
---|
163 | hitlist.append(CertificatesQueryResultItem(result, view=view)) |
---|
164 | results = Query().searchResults( |
---|
165 | Eq(('certcourses_catalog', 'course_code'), query)) |
---|
166 | for result in results: |
---|
167 | hitlist.append(CertificateCoursesQueryResultItem(result, view=view)) |
---|
168 | except ParseError: |
---|
169 | view.flash('Search string not allowed.') |
---|
170 | return |
---|
171 | return hitlist |
---|