Merge lp:~justas.sadzevicius/schooltool/catalogs into lp:schooltool/1.7
- catalogs
- Merge into 1.7
Proposed by
Justas Sadzevičius
Status: | Merged |
---|---|
Merged at revision: | 2683 |
Proposed branch: | lp:~justas.sadzevicius/schooltool/catalogs |
Merge into: | lp:schooltool/1.7 |
Diff against target: |
2330 lines (+1430/-174) 37 files modified
src/schooltool/app/app.py (+5/-6) src/schooltool/app/browser/app.py (+3/-4) src/schooltool/app/browser/cal.py (+4/-5) src/schooltool/app/browser/csvimport.py (+1/-2) src/schooltool/app/browser/overlay.py (+3/-3) src/schooltool/app/catalog.py (+216/-0) src/schooltool/app/catalog.zcml (+22/-0) src/schooltool/app/configure.zcml (+3/-1) src/schooltool/app/interfaces.py (+40/-2) src/schooltool/app/main.py (+12/-0) src/schooltool/app/security.py (+7/-6) src/schooltool/app/security.txt (+5/-2) src/schooltool/app/tests/catalog-integration.txt (+105/-0) src/schooltool/app/tests/test_app.py (+3/-5) src/schooltool/app/tests/test_catalog.py (+749/-0) src/schooltool/app/tests/test_security.py (+5/-2) src/schooltool/basicperson/configure.zcml (+0/-5) src/schooltool/basicperson/overrides.zcml (+4/-1) src/schooltool/basicperson/person.py (+9/-21) src/schooltool/commendation/browser.py (+2/-1) src/schooltool/commendation/commendation.py (+2/-1) src/schooltool/contact/configure.zcml (+3/-4) src/schooltool/contact/contact.py (+12/-25) src/schooltool/generations/__init__.py (+2/-2) src/schooltool/generations/evolve33.py (+0/-7) src/schooltool/generations/evolve35.py (+59/-0) src/schooltool/generations/tests/__init__.py (+2/-0) src/schooltool/generations/tests/test_evolve33.py (+1/-24) src/schooltool/generations/tests/test_evolve35.py (+123/-0) src/schooltool/person/configure.zcml (+4/-6) src/schooltool/person/person.py (+13/-23) src/schooltool/resource/types.py (+2/-2) src/schooltool/skin/skin.py (+1/-3) src/schooltool/term/browser/emergency.py (+2/-2) src/schooltool/timetable/__init__.py (+3/-6) src/schooltool/timetable/browser/__init__.py (+1/-2) src/schooltool/utility/utility.py (+2/-1) |
To merge this branch: | bzr merge lp:~justas.sadzevicius/schooltool/catalogs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Justas Sadzevičius | Pending | ||
Review via email: mp+26289@code.launchpad.net |
Commit message
Description of the change
New-style catalogs should not fall to ST in Lucid, so... merge to 1.5?
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/schooltool/app/app.py' |
2 | --- src/schooltool/app/app.py 2010-02-09 16:54:13 +0000 |
3 | +++ src/schooltool/app/app.py 2010-05-28 11:09:37 +0000 |
4 | @@ -28,11 +28,9 @@ |
5 | from persistent.dict import PersistentDict |
6 | |
7 | from zope.location.location import LocationProxy |
8 | -from zope.component import adapter |
9 | -from zope.component import adapts |
10 | +from zope.component import adapter, adapts |
11 | from zope.i18n import translate |
12 | -from zope.interface import implementer |
13 | -from zope.interface import implements, implementsOnly |
14 | +from zope.interface import implementer, implements, implementsOnly |
15 | |
16 | from zope.annotation.interfaces import IAttributeAnnotatable, IAnnotations |
17 | from zope.app.applicationcontrol.interfaces import IApplicationControl |
18 | @@ -86,6 +84,8 @@ |
19 | title = property(_title) |
20 | |
21 | |
22 | +@adapter(None) |
23 | +@implementer(ISchoolToolApplication) |
24 | def getSchoolToolApplication(ignore=None): |
25 | """Return the nearest ISchoolToolApplication. |
26 | |
27 | @@ -99,8 +99,7 @@ |
28 | candidate = getSite() |
29 | if ISchoolToolApplication.providedBy(candidate): |
30 | return candidate |
31 | - else: |
32 | - raise ValueError("can't get a SchoolToolApplication") |
33 | + return None |
34 | |
35 | |
36 | class SimpleNameChooser(NameChooser): |
37 | |
38 | === modified file 'src/schooltool/app/browser/app.py' |
39 | --- src/schooltool/app/browser/app.py 2010-01-19 16:41:42 +0000 |
40 | +++ src/schooltool/app/browser/app.py 2010-05-28 11:09:37 +0000 |
41 | @@ -45,7 +45,6 @@ |
42 | from schooltool.calendar.icalendar import convert_calendar_to_ical |
43 | from schooltool.common import SchoolToolMessage as _ |
44 | from schooltool.app.browser.interfaces import IManageMenuViewletManager |
45 | -from schooltool.app.app import getSchoolToolApplication |
46 | from schooltool.app.interfaces import ISchoolToolAuthenticationPlugin |
47 | from schooltool.app.interfaces import ISchoolToolApplication |
48 | from schooltool.app.interfaces import IApplicationPreferences |
49 | @@ -63,7 +62,7 @@ |
50 | """A view for the main application.""" |
51 | |
52 | def update(self): |
53 | - prefs = IApplicationPreferences(getSchoolToolApplication()) |
54 | + prefs = IApplicationPreferences(ISchoolToolApplication(None)) |
55 | if prefs.frontPageCalendar: |
56 | url = absoluteURL(ISchoolToolCalendar(self.context), |
57 | self.request) |
58 | @@ -275,7 +274,7 @@ |
59 | def __init__(self, context, request): |
60 | BrowserView.__init__(self, context, request) |
61 | |
62 | - app = getSchoolToolApplication() |
63 | + app = ISchoolToolApplication(None) |
64 | prefs = self.schema(app) |
65 | initial = {} |
66 | for field in self.schema: |
67 | @@ -292,7 +291,7 @@ |
68 | except WidgetsError: |
69 | return # Errors will be displayed next to widgets |
70 | |
71 | - app = getSchoolToolApplication() |
72 | + app = ISchoolToolApplication(None) |
73 | prefs = self.schema(app) |
74 | for field in self.schema: |
75 | if field in data: # skip non-fields |
76 | |
77 | === modified file 'src/schooltool/app/browser/cal.py' |
78 | --- src/schooltool/app/browser/cal.py 2010-04-12 10:49:40 +0000 |
79 | +++ src/schooltool/app/browser/cal.py 2010-05-28 11:09:37 +0000 |
80 | @@ -77,7 +77,6 @@ |
81 | from schooltool.app.browser.interfaces import IEventForDisplay |
82 | from schooltool.app.browser.interfaces import IHaveEventLegend |
83 | from schooltool.app.interfaces import ISchoolToolCalendarEvent |
84 | -from schooltool.app.app import getSchoolToolApplication |
85 | from schooltool.app.interfaces import ISchoolToolCalendar |
86 | from schooltool.app.interfaces import IHaveCalendar |
87 | from schooltool.table.batch import IterableBatch |
88 | @@ -934,7 +933,7 @@ |
89 | timetable_template = ViewPageTemplateFile("templates/cal_weekly_timetable.pt") |
90 | |
91 | def __call__(self): |
92 | - app = getSchoolToolApplication() |
93 | + app = ISchoolToolApplication(None) |
94 | # XXX ttshemas were removed from app in evolve28. |
95 | # timetable_template can be killed now (maybe?). |
96 | # All related code should go to land of no return. |
97 | @@ -2477,7 +2476,7 @@ |
98 | |
99 | elif "BOOK" in self.request: # and not self.update_status: |
100 | self.update_status = '' |
101 | - sb = getSchoolToolApplication() |
102 | + sb = ISchoolToolApplication(None) |
103 | for res_id, resource in sb["resources"].items(): |
104 | if 'add_item.%s' % res_id in self.request: |
105 | booked = self.hasBooked(resource) |
106 | @@ -2488,7 +2487,7 @@ |
107 | |
108 | elif "UNBOOK" in self.request: |
109 | self.update_status = '' |
110 | - sb = getSchoolToolApplication() |
111 | + sb = ISchoolToolApplication(None) |
112 | for res_id, resource in sb["resources"].items(): |
113 | if 'remove_item.%s' % res_id in self.request: |
114 | booked = self.hasBooked(resource) |
115 | @@ -2502,7 +2501,7 @@ |
116 | @property |
117 | def availableResources(self): |
118 | """Gives us a list of all bookable resources.""" |
119 | - sb = getSchoolToolApplication() |
120 | + sb = ISchoolToolApplication(None) |
121 | calendar_owner = removeSecurityProxy(self.context.__parent__.__parent__) |
122 | def isBookable(resource): |
123 | if resource is calendar_owner: |
124 | |
125 | === modified file 'src/schooltool/app/browser/csvimport.py' |
126 | --- src/schooltool/app/browser/csvimport.py 2010-01-19 12:09:22 +0000 |
127 | +++ src/schooltool/app/browser/csvimport.py 2010-05-28 11:09:37 +0000 |
128 | @@ -27,7 +27,6 @@ |
129 | |
130 | from schooltool.schoolyear.interfaces import ISchoolYear |
131 | from schooltool.common import SchoolToolMessage as _ |
132 | -from schooltool.app.app import getSchoolToolApplication |
133 | from schooltool.app.app import SimpleNameChooser |
134 | from schooltool.app.interfaces import ISchoolToolApplication |
135 | from schooltool.course.interfaces import ICourseContainer |
136 | @@ -260,7 +259,7 @@ |
137 | def __init__(self, container, charset=None): |
138 | # XXX It appears that our security declarations are inadequate, |
139 | # because things break without this removeSecurityProxy. |
140 | - self.app = getSchoolToolApplication() |
141 | + self.app = ISchoolToolApplication(None) |
142 | self.sections = removeSecurityProxy(container) |
143 | self.persons = self.app['persons'] |
144 | self.errors = TimetableImportErrorCollection() |
145 | |
146 | === modified file 'src/schooltool/app/browser/overlay.py' |
147 | --- src/schooltool/app/browser/overlay.py 2009-11-30 18:56:52 +0000 |
148 | +++ src/schooltool/app/browser/overlay.py 2010-05-28 11:09:37 +0000 |
149 | @@ -31,8 +31,8 @@ |
150 | from zope.viewlet.viewlet import ViewletBase |
151 | |
152 | from schooltool.common import SchoolToolMessage as _ |
153 | -from schooltool.app.app import getSchoolToolApplication |
154 | from schooltool.app.interfaces import ISchoolToolCalendar |
155 | +from schooltool.app.interfaces import ISchoolToolApplication |
156 | from schooltool.app.interfaces import IShowTimetables |
157 | from schooltool.person.interfaces import IPerson |
158 | |
159 | @@ -159,7 +159,7 @@ |
160 | user = removeSecurityProxy(IPerson(self.request.principal, None)) |
161 | if user is None: |
162 | return [] |
163 | - app = getSchoolToolApplication() |
164 | + app = ISchoolToolApplication(None) |
165 | |
166 | result = [] |
167 | for obj in app[container].values(): |
168 | @@ -179,7 +179,7 @@ |
169 | """ |
170 | user = IPerson(self.request.principal, None) |
171 | if user: |
172 | - app = getSchoolToolApplication() |
173 | + app = ISchoolToolApplication(None) |
174 | calendar = ISchoolToolCalendar(app) |
175 | if canAccess(calendar, '__iter__'): |
176 | return {'title': app.title, |
177 | |
178 | === added file 'src/schooltool/app/catalog.py' |
179 | --- src/schooltool/app/catalog.py 1970-01-01 00:00:00 +0000 |
180 | +++ src/schooltool/app/catalog.py 2010-05-28 11:09:37 +0000 |
181 | @@ -0,0 +1,216 @@ |
182 | +# |
183 | +# SchoolTool - common information systems platform for school administration |
184 | +# Copyright (c) 2010 Shuttleworth Foundation |
185 | +# |
186 | +# This program is free software; you can redistribute it and/or modify |
187 | +# it under the terms of the GNU General Public License as published by |
188 | +# the Free Software Foundation; either version 2 of the License, or |
189 | +# (at your option) any later version. |
190 | +# |
191 | +# This program is distributed in the hope that it will be useful, |
192 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
193 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
194 | +# GNU General Public License for more details. |
195 | +# |
196 | +# You should have received a copy of the GNU General Public License |
197 | +# along with this program; if not, write to the Free Software |
198 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
199 | +# |
200 | +""" |
201 | +SchoolTool catalogs. |
202 | +""" |
203 | +from zope.interface import implementer, implements, implementsOnly |
204 | +from zope.intid.interfaces import IIntIds, IIntIdAddedEvent, IIntIdRemovedEvent |
205 | +from zope.component import adapter, queryUtility, getUtility |
206 | +from zope.container import btree |
207 | +from zope.container.contained import Contained |
208 | +from zope.lifecycleevent import IObjectModifiedEvent |
209 | + |
210 | +from zc.catalog import extentcatalog |
211 | +from zc.catalog import catalogindex |
212 | + |
213 | +from schooltool.app.interfaces import ISchoolToolApplication |
214 | +from schooltool.app.interfaces import ICatalogStartUp |
215 | +from schooltool.app.interfaces import ICatalogs |
216 | +from schooltool.app.interfaces import IVersionedCatalog |
217 | +from schooltool.app.app import ActionBase |
218 | +from schooltool.table.catalog import FilterImplementing |
219 | + |
220 | + |
221 | +APP_CATALOGS_KEY = 'schooltool.app.catalog:Catalogs' |
222 | + |
223 | + |
224 | +class CatalogStartupBase(ActionBase): |
225 | + implementsOnly(ICatalogStartUp) |
226 | + |
227 | + |
228 | +class Catalogs(btree.BTreeContainer): |
229 | + implements(ICatalogs) |
230 | + |
231 | + |
232 | +@adapter(ISchoolToolApplication) |
233 | +@implementer(ICatalogs) |
234 | +def getAppCatalogs(app): |
235 | + if APP_CATALOGS_KEY not in app: |
236 | + app[APP_CATALOGS_KEY] = Catalogs() |
237 | + return app[APP_CATALOGS_KEY] |
238 | + |
239 | + |
240 | +class VersionedCatalog(Contained): |
241 | + implements(IVersionedCatalog) |
242 | + |
243 | + expired = False |
244 | + version = 0 |
245 | + catalog = None |
246 | + |
247 | + def __init__(self, catalog, version): |
248 | + self.catalog = catalog |
249 | + self.catalog.__parent__ = self |
250 | + self.catalog.__name__ = 'catalog' |
251 | + self.version = version |
252 | + |
253 | + def __repr__(self): |
254 | + return '<%s v. %r>: %s' % ( |
255 | + self.__class__.__name__, self.version, self.catalog) |
256 | + |
257 | + |
258 | +class PrepareCatalogContainer(CatalogStartupBase): |
259 | + |
260 | + def __call__(self): |
261 | + catalogs = ICatalogs(self.app) |
262 | + for entry in catalogs.values(): |
263 | + entry.expired = True |
264 | + |
265 | + |
266 | +class ExpiredCatalogCleanup(CatalogStartupBase): |
267 | + |
268 | + def __call__(self): |
269 | + catalogs = ICatalogs(self.app) |
270 | + for key in list(catalogs): |
271 | + if catalogs[key].expired: |
272 | + del catalogs[key] |
273 | + |
274 | + |
275 | +class CatalogFactory(CatalogStartupBase): |
276 | + |
277 | + after = ('prepare-catalog-container', ) |
278 | + before = ('expired-catalog-cleanup', ) |
279 | + |
280 | + version = u'' |
281 | + |
282 | + @classmethod |
283 | + def key(cls): |
284 | + return u'catalog:%s.%s' % (cls.__module__, cls.__name__) |
285 | + |
286 | + @classmethod |
287 | + def get(cls, ignored=None): |
288 | + app = ISchoolToolApplication(None) |
289 | + catalogs = ICatalogs(app) |
290 | + entry = catalogs.get(cls.key()) |
291 | + if entry is None: |
292 | + return None |
293 | + return entry.catalog |
294 | + |
295 | + def getVersion(self): |
296 | + return unicode(self.version) |
297 | + |
298 | + def createCatalog(self): |
299 | + raise NotImplementedError() |
300 | + |
301 | + def setIndexes(self, catalog): |
302 | + raise NotImplementedError() |
303 | + |
304 | + def __call__(self): |
305 | + app = ISchoolToolApplication(None) |
306 | + catalogs = ICatalogs(app) |
307 | + key = self.key() |
308 | + version = self.getVersion() |
309 | + |
310 | + if key in catalogs: |
311 | + if catalogs[key].version == version: |
312 | + catalogs[key].expired = False |
313 | + else: |
314 | + del catalogs[key] |
315 | + |
316 | + if key not in catalogs: |
317 | + catalog = self.createCatalog() |
318 | + catalogs[key] = VersionedCatalog(catalog, version) |
319 | + # XXX: if setIndexes throw, delete the catalog and rethrow |
320 | + self.setIndexes(catalog) |
321 | + |
322 | + |
323 | +class CatalogImplementing(CatalogFactory): |
324 | + """Factory of catalogs containing objects implementing the given interface.""" |
325 | + |
326 | + interface = None # override in child classes |
327 | + |
328 | + def createCatalog(self): |
329 | + return extentcatalog.Catalog( |
330 | + extentcatalog.FilterExtent( |
331 | + FilterImplementing(self.interface))) |
332 | + |
333 | + def getVersion(self): |
334 | + return u'interface:%s, version:%s' % ( |
335 | + u'%s.%s' % (self.interface.__module__, self.interface.__name__), |
336 | + super(CatalogImplementing, self).getVersion()) |
337 | + |
338 | + |
339 | +class AttributeCatalog(CatalogImplementing): |
340 | + """Catalog indexing specified attributes of objects implementing |
341 | + the given interface.""" |
342 | + |
343 | + attributes = () |
344 | + |
345 | + def getVersion(self): |
346 | + return u'attributes:%s, %s' % ( |
347 | + tuple(sorted(self.attributes)), |
348 | + super(AttributeCatalog, self).getVersion()) |
349 | + |
350 | + def setIndexes(self, catalog): |
351 | + for name in self.attributes: |
352 | + catalog[name] = catalogindex.ValueIndex(name) |
353 | + |
354 | + |
355 | +@adapter(IIntIdAddedEvent) |
356 | +def indexDocSubscriber(event): |
357 | + app = ISchoolToolApplication(None, None) |
358 | + if app is None: |
359 | + return |
360 | + obj = event.object |
361 | + util = getUtility(IIntIds, context=app) |
362 | + obj_id = util.getId(obj) |
363 | + catalogs = ICatalogs(app) |
364 | + for entry in catalogs.values(): |
365 | + entry.catalog.index_doc(obj_id, obj) |
366 | + |
367 | + |
368 | +@adapter(IObjectModifiedEvent) |
369 | +def reindexDocSubscriber(event): |
370 | + app = ISchoolToolApplication(None, None) |
371 | + if app is None: |
372 | + return |
373 | + obj = event.object |
374 | + util = queryUtility(IIntIds, context=app) |
375 | + if util is None: |
376 | + return |
377 | + obj_id = util.queryId(obj) |
378 | + if obj_id is None: |
379 | + return |
380 | + catalogs = ICatalogs(app) |
381 | + for entry in catalogs.values(): |
382 | + entry.catalog.index_doc(obj_id, obj) |
383 | + |
384 | + |
385 | +@adapter(IIntIdRemovedEvent) |
386 | +def unindexDocSubscriber(event): |
387 | + app = ISchoolToolApplication(None, None) |
388 | + if app is None: |
389 | + return |
390 | + obj = event.object |
391 | + util = getUtility(IIntIds, context=app) |
392 | + obj_id = util.queryId(obj) |
393 | + if obj_id is None: |
394 | + return |
395 | + catalogs = ICatalogs(app) |
396 | + for entry in catalogs.values(): |
397 | + entry.catalog.unindex_doc(obj_id) |
398 | |
399 | === added file 'src/schooltool/app/catalog.zcml' |
400 | --- src/schooltool/app/catalog.zcml 1970-01-01 00:00:00 +0000 |
401 | +++ src/schooltool/app/catalog.zcml 2010-05-28 11:09:37 +0000 |
402 | @@ -0,0 +1,22 @@ |
403 | +<?xml version="1.0"?> |
404 | +<configure xmlns="http://namespaces.zope.org/zope"> |
405 | + |
406 | + <adapter factory=".catalog.getAppCatalogs" /> |
407 | + |
408 | + <subscriber |
409 | + for="schooltool.app.interfaces.ICatalogStartUpEvent" |
410 | + handler=".main.startSchoolToolCatalogs" /> |
411 | + |
412 | + <adapter |
413 | + factory=".catalog.PrepareCatalogContainer" |
414 | + name="prepare-catalog-container" /> |
415 | + |
416 | + <adapter |
417 | + factory=".catalog.ExpiredCatalogCleanup" |
418 | + name="expired-catalog-cleanup" /> |
419 | + |
420 | + <subscriber handler=".catalog.indexDocSubscriber" /> |
421 | + <subscriber handler=".catalog.reindexDocSubscriber" /> |
422 | + <subscriber handler=".catalog.unindexDocSubscriber" /> |
423 | + |
424 | +</configure> |
425 | |
426 | === modified file 'src/schooltool/app/configure.zcml' |
427 | --- src/schooltool/app/configure.zcml 2010-01-19 16:41:42 +0000 |
428 | +++ src/schooltool/app/configure.zcml 2010-05-28 11:09:37 +0000 |
429 | @@ -169,6 +169,9 @@ |
430 | for="schooltool.app.interfaces.IApplicationStartUpEvent" |
431 | handler=".main.startSchoolToolPlugins" /> |
432 | |
433 | + <!-- Catalog support --> |
434 | + <include file="catalog.zcml" /> |
435 | + |
436 | <!-- Timetables for content components --> |
437 | <class class="schooltool.app.app.SchoolToolApplication"> |
438 | <implements interface="schooltool.timetable.interfaces.IHaveTimetables" /> |
439 | @@ -185,7 +188,6 @@ |
440 | <class class="schooltool.resource.resource.BaseResource"> |
441 | <implements interface="schooltool.timetable.interfaces.IHaveTimetables" /> |
442 | </class> |
443 | - |
444 | |
445 | <!-- Core and Domain content objects --> |
446 | |
447 | |
448 | === modified file 'src/schooltool/app/interfaces.py' |
449 | --- src/schooltool/app/interfaces.py 2010-01-19 16:41:42 +0000 |
450 | +++ src/schooltool/app/interfaces.py 2010-05-28 11:09:37 +0000 |
451 | @@ -23,7 +23,9 @@ |
452 | import zope.schema |
453 | |
454 | from zope.component.interfaces import ObjectEvent, IObjectEvent |
455 | +from zope.container.interfaces import IContainer, IContained |
456 | from zope.container.interfaces import IReadContainer |
457 | +from zope.container.constraints import contains |
458 | from zope.interface import implements |
459 | from zope.interface import Interface, Attribute |
460 | from zope.location.interfaces import ILocation, IContained |
461 | @@ -72,15 +74,26 @@ |
462 | |
463 | |
464 | class ICatalogSetUpEvent(IObjectEvent): |
465 | - """The SchoolTool catalogs are being initialized. |
466 | + """Old style notification that the SchoolTool catalogs are being initialized. |
467 | |
468 | - Subscribers should set up their catalogs. |
469 | + Subject to deprecation. Subscribers should no longer use this to set up their |
470 | + catalogs. |
471 | """ |
472 | |
473 | class CatalogSetUpEvent(ObjectEvent): |
474 | implements(ICatalogSetUpEvent) |
475 | |
476 | |
477 | +class ICatalogStartUpEvent(IObjectEvent): |
478 | + """The SchoolTool catalogs has started up. |
479 | + |
480 | + A hook for catalog initialization. |
481 | + """ |
482 | + |
483 | +class CatalogStartUpEvent(ObjectEvent): |
484 | + implements(ICatalogStartUpEvent) |
485 | + |
486 | + |
487 | class ISchoolToolCalendar(IEditCalendar, ILocation): |
488 | """A SchoolTool calendar. |
489 | |
490 | @@ -234,6 +247,10 @@ |
491 | """Perform plugin specific set up.""" |
492 | |
493 | |
494 | +class ICatalogStartUp(IPluginAction): |
495 | + """Set up SchoolTool catalogs.""" |
496 | + |
497 | + |
498 | class IPluginInit(IPluginAction): |
499 | """Perform plugin initialization when setting up the SchoolTool |
500 | application.""" |
501 | @@ -245,3 +262,24 @@ |
502 | |
503 | class ISchoolToolAuthenticationPlugin(ISchoolToolAuthentication): |
504 | """A plugin for local schooltool authentication utility. """ |
505 | + |
506 | + |
507 | +class IVersionedCatalog(IContained): |
508 | + """Versioned catalog entry.""" |
509 | + |
510 | + version = zope.schema.TextLine( |
511 | + title=u"Version", |
512 | + description=u"Current version of the catalog.") |
513 | + |
514 | + expired = zope.schema.Bool( |
515 | + title=u"Expired", |
516 | + description=u"Expired catalogs should be removed.") |
517 | + |
518 | + catalog = Attribute("""The catalog object.""") |
519 | + |
520 | + |
521 | +class ICatalogs(IContainer): |
522 | + """Container of versioned catalogs.""" |
523 | + |
524 | + contains(IVersionedCatalog) |
525 | + |
526 | |
527 | === modified file 'src/schooltool/app/main.py' |
528 | --- src/schooltool/app/main.py 2010-04-28 21:27:43 +0000 |
529 | +++ src/schooltool/app/main.py 2010-05-28 11:09:37 +0000 |
530 | @@ -71,12 +71,14 @@ |
531 | from schooltool.app.interfaces import ApplicationStartUpEvent |
532 | from schooltool.app.interfaces import ApplicationInitializationEvent |
533 | from schooltool.app.interfaces import IPluginInit, IPluginStartUp |
534 | +from schooltool.app.interfaces import ICatalogStartUp |
535 | from schooltool.app.interfaces import ISchoolToolInitializationUtility |
536 | from schooltool.app.app import SchoolToolApplication |
537 | from schooltool.app import pdf |
538 | from schooltool.person.interfaces import IPersonFactory |
539 | from schooltool.app.interfaces import ICookieLanguageSelector |
540 | from schooltool.app.interfaces import CatalogSetUpEvent |
541 | +from schooltool.app.interfaces import CatalogStartUpEvent |
542 | from schooltool.utility.utility import setUpUtilities |
543 | from schooltool.utility.utility import UtilitySpecification |
544 | |
545 | @@ -448,6 +450,12 @@ |
546 | print >> sys.stderr, "Failed to execute %s: %s" % (action, exception) |
547 | |
548 | |
549 | +def startSchoolToolCatalogs(event): |
550 | + adapters = getAdapters((event.object, ), ICatalogStartUp) |
551 | + sorter = PluginActionSorter(adapters) |
552 | + executePluginActions(sorter()) |
553 | + |
554 | + |
555 | def initializeSchoolToolPlugins(event): |
556 | adapters = getAdapters((event.object, ), IPluginInit) |
557 | sorter = PluginActionSorter(adapters) |
558 | @@ -617,6 +625,7 @@ |
559 | # before initializing plugins themselves or else all the |
560 | # initial groups, persons, resources will not get indexed |
561 | notify(CatalogSetUpEvent(app)) |
562 | + notify(CatalogStartUpEvent(app)) |
563 | |
564 | # initialize plugins themselves |
565 | notify(ApplicationInitializationEvent(app)) |
566 | @@ -715,7 +724,10 @@ |
567 | connection = db.open() |
568 | root = connection.root() |
569 | app = root[ZopePublication.root_name] |
570 | + setSite(app) |
571 | + notify(CatalogStartUpEvent(app)) |
572 | notify(ApplicationStartUpEvent(app)) |
573 | + setSite(None) |
574 | transaction.commit() |
575 | connection.close() |
576 | |
577 | |
578 | === modified file 'src/schooltool/app/security.py' |
579 | --- src/schooltool/app/security.py 2010-02-09 16:54:13 +0000 |
580 | +++ src/schooltool/app/security.py 2010-05-28 11:09:37 +0000 |
581 | @@ -40,7 +40,8 @@ |
582 | from zope.site import LocalSiteManager |
583 | from zope.traversing.browser.absoluteurl import absoluteURL |
584 | from zope.traversing.api import traverse |
585 | -from schooltool.app.app import getSchoolToolApplication |
586 | + |
587 | +from schooltool.app.interfaces import ISchoolToolApplication |
588 | from schooltool.app.interfaces import ISchoolToolAuthentication |
589 | from schooltool.app.interfaces import IAsset |
590 | from schooltool.person.interfaces import IPerson |
591 | @@ -95,13 +96,13 @@ |
592 | return self.getPrincipal('sb.person.' + login) |
593 | |
594 | def _checkPlainTextPassword(self, username, password): |
595 | - app = getSchoolToolApplication() |
596 | + app = ISchoolToolApplication(None) |
597 | if username in app['persons']: |
598 | person = app['persons'][username] |
599 | return person.checkPassword(password) |
600 | |
601 | def _checkHashedPassword(self, username, password): |
602 | - app = getSchoolToolApplication() |
603 | + app = ISchoolToolApplication(None) |
604 | if username in app['persons']: |
605 | person = app['persons'][username] |
606 | return (person._hashed_password is not None |
607 | @@ -139,7 +140,7 @@ |
608 | |
609 | def unauthorized(self, id, request): |
610 | """Signal an authorization failure.""" |
611 | - app = getSchoolToolApplication() |
612 | + app = ISchoolToolApplication(None) |
613 | app_url = absoluteURL(app, request) |
614 | query_string = request.getHeader('QUERY_STRING') |
615 | post_form_id = self.storePOSTData(request) |
616 | @@ -162,7 +163,7 @@ |
617 | |
618 | Returns principals for groups and persons. |
619 | """ |
620 | - app = getSchoolToolApplication() |
621 | + app = ISchoolToolApplication(None) |
622 | if id.startswith(self.person_prefix): |
623 | username = id[len(self.person_prefix):] |
624 | if username in app['persons']: |
625 | @@ -331,7 +332,7 @@ |
626 | prefix = PersonContainerAuthenticationPlugin.group_prefix |
627 | if not group_principal_id.startswith(prefix): |
628 | return None |
629 | - groups = IGroupContainer(getSchoolToolApplication(), None) |
630 | + groups = IGroupContainer(ISchoolToolApplication(None), None) |
631 | if groups is None: |
632 | return None |
633 | group_id = group_principal_id[len(prefix):] |
634 | |
635 | === modified file 'src/schooltool/app/security.txt' |
636 | --- src/schooltool/app/security.txt 2010-02-09 16:54:13 +0000 |
637 | +++ src/schooltool/app/security.txt 2010-05-28 11:09:37 +0000 |
638 | @@ -66,9 +66,12 @@ |
639 | >>> verifyObject(ISite, app) |
640 | Traceback (most recent call last): |
641 | ... |
642 | - DoesNotImplement: ... |
643 | - ... |
644 | + DoesNotImplement: An object does not implement interface |
645 | + <InterfaceClass zope.component.interfaces.ISite> |
646 | |
647 | + >>> from zope.component import provideAdapter |
648 | + >>> from schooltool.app.app import getSchoolToolApplication |
649 | + >>> provideAdapter(getSchoolToolApplication) |
650 | |
651 | The SchoolToolAuthenticationUtility is an IAuthentication utility that |
652 | lives in that site: |
653 | |
654 | === added file 'src/schooltool/app/tests/catalog-integration.txt' |
655 | --- src/schooltool/app/tests/catalog-integration.txt 1970-01-01 00:00:00 +0000 |
656 | +++ src/schooltool/app/tests/catalog-integration.txt 2010-05-28 11:09:37 +0000 |
657 | @@ -0,0 +1,105 @@ |
658 | +Version-controlled catalogs |
659 | +=========================== |
660 | + |
661 | + A Catalog in it's essence is a set of value caches (indexes). |
662 | +Usually these are cached attributes of objects in a container or |
663 | +objects implementing a given interface. Sometimes aggregated values |
664 | +like persons full title are cached. |
665 | + |
666 | + The main purpose of catalogs is giving access to the values without |
667 | +the need to load the whole object into memory. Think searching persons |
668 | +named 'M...' in a container with several thousands of them, you'll get |
669 | +the picture. |
670 | + |
671 | + Due to their nature, catalogs need to be rebuilt, changed, |
672 | +reindexed, etc. during active development. They are commonly stored |
673 | +in the database - that would mean evolution scripts. |
674 | + |
675 | + For the above reasons, SchoolTool provides catalog version control |
676 | +and catalog cache that gets updated when catalog versions change. |
677 | + |
678 | + |
679 | +How it works |
680 | +------------ |
681 | + |
682 | + To register a versioned catalog, let's first inherit the CatalogFactory and |
683 | +implement the required methods: |
684 | + |
685 | + >>> from schooltool.app.catalog import CatalogFactory |
686 | + |
687 | + >>> class MyCatalog(CatalogFactory): |
688 | + ... version = 1 |
689 | + ... def createCatalog(self): |
690 | + ... return CatalogStub('my catalog') |
691 | + ... def setIndexes(self, catalog): |
692 | + ... print 'Setting indexes for', catalog |
693 | + |
694 | + Catalogs are registered as named adapters. |
695 | + |
696 | + >>> provideAdapter( |
697 | + ... MyCatalog, name="test-catalog-integration-mycatalog") |
698 | + |
699 | + Sometime at server start up catalog setup event is fired and |
700 | +catalogs get created. |
701 | + |
702 | + >>> from schooltool.app.interfaces import ISchoolToolApplication |
703 | + >>> from schooltool.app.interfaces import CatalogStartUpEvent |
704 | + >>> from zope.event import notify |
705 | + |
706 | + >>> print MyCatalog.get() |
707 | + None |
708 | + |
709 | + >>> notify(CatalogStartUpEvent(ISchoolToolApplication(None))) |
710 | + Setting indexes for <CatalogStub 'my catalog'> |
711 | + |
712 | + >>> catalog = MyCatalog.get() |
713 | + >>> print catalog |
714 | + <CatalogStub 'my catalog'> |
715 | + |
716 | + Catalog's __parent__ holds the versioning information. |
717 | + |
718 | + >>> print catalog.__parent__ |
719 | + <VersionedCatalog v. u'1'>: <CatalogStub 'my catalog'> |
720 | + |
721 | + The catalog's unique name is constructed from the factory class |
722 | +name. So if you rename the class, know that the catalogs will be |
723 | +re-created and re-indexed. |
724 | + |
725 | + >>> from schooltool.app.interfaces import ICatalogs |
726 | + >>> print sorted(ICatalogs(ISchoolToolApplication(None))) |
727 | + [u'catalog:__builtin__.MyCatalog'] |
728 | + |
729 | + Next time the server starts up, catalog is left as it is. We'll |
730 | +demonstrate this by changing the catalog creation method, but leaving |
731 | +the version unchanged. |
732 | + |
733 | + >>> MyCatalog.createCatalog = lambda self: CatalogStub('my better catalog') |
734 | + >>> notify(CatalogStartUpEvent(ISchoolToolApplication(None))) |
735 | + |
736 | + >>> print MyCatalog.get() |
737 | + <CatalogStub 'my catalog'> |
738 | + |
739 | + But once we change the version, the catalog gets re-created. |
740 | + |
741 | + >>> MyCatalog.version = 1.1 |
742 | + |
743 | + >>> notify(CatalogStartUpEvent(ISchoolToolApplication(None))) |
744 | + Setting indexes for <CatalogStub 'my better catalog'> |
745 | + |
746 | + >>> catalog = MyCatalog.get() |
747 | + >>> print catalog.__parent__ |
748 | + <VersionedCatalog v. u'1.1'>: <CatalogStub 'my better catalog'> |
749 | + |
750 | + And of course once the catalog factory is no longer provided, catalog |
751 | +will be deleted. |
752 | + |
753 | + >>> unregisterAdapter( |
754 | + ... MyCatalog, name="test-catalog-integration-mycatalog") |
755 | + |
756 | + >>> notify(CatalogStartUpEvent(ISchoolToolApplication(None))) |
757 | + |
758 | + >>> print MyCatalog.get() |
759 | + None |
760 | + |
761 | + >>> print sorted(ICatalogs(ISchoolToolApplication(None))) |
762 | + [] |
763 | |
764 | === modified file 'src/schooltool/app/tests/test_app.py' |
765 | --- src/schooltool/app/tests/test_app.py 2010-02-09 19:18:22 +0000 |
766 | +++ src/schooltool/app/tests/test_app.py 2010-05-28 11:09:37 +0000 |
767 | @@ -107,13 +107,11 @@ |
768 | >>> from zope.site import LocalSiteManager |
769 | >>> app.setSiteManager(LocalSiteManager(app)) |
770 | |
771 | - If site is not a SchoolToolApplication, we get an error |
772 | + If site is not a SchoolToolApplication, we get None |
773 | |
774 | >>> from schooltool.app.app import getSchoolToolApplication |
775 | - >>> getSchoolToolApplication() |
776 | - Traceback (most recent call last): |
777 | - ... |
778 | - ValueError: can't get a SchoolToolApplication |
779 | + >>> print getSchoolToolApplication() |
780 | + None |
781 | |
782 | If current site is a SchoolToolApplication, we get it: |
783 | |
784 | |
785 | === added file 'src/schooltool/app/tests/test_catalog.py' |
786 | --- src/schooltool/app/tests/test_catalog.py 1970-01-01 00:00:00 +0000 |
787 | +++ src/schooltool/app/tests/test_catalog.py 2010-05-28 11:09:37 +0000 |
788 | @@ -0,0 +1,749 @@ |
789 | +# |
790 | +# SchoolTool - common information systems platform for school administration |
791 | +# Copyright (c) 2010 Shuttleworth Foundation |
792 | +# |
793 | +# This program is free software; you can redistribute it and/or modify |
794 | +# it under the terms of the GNU General Public License as published by |
795 | +# the Free Software Foundation; either version 2 of the License, or |
796 | +# (at your option) any later version. |
797 | +# |
798 | +# This program is distributed in the hope that it will be useful, |
799 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
800 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
801 | +# GNU General Public License for more details. |
802 | +# |
803 | +# You should have received a copy of the GNU General Public License |
804 | +# along with this program; if not, write to the Free Software |
805 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
806 | +# |
807 | +""" |
808 | +Unit tests for catalogs |
809 | +""" |
810 | +import unittest |
811 | +import doctest |
812 | + |
813 | +from zope.app.testing import setup |
814 | +from zope.interface import implements, Interface |
815 | +from zope.interface.verify import verifyObject |
816 | +from zope.component import provideAdapter |
817 | +from zope.component.hooks import getSite, setSite |
818 | +from zope.site import SiteManagerContainer |
819 | +from zope.site.folder import rootFolder |
820 | + |
821 | +from schooltool.testing.setup import ZCMLWrapper |
822 | +from schooltool.app.interfaces import ISchoolToolApplication |
823 | +from schooltool.app.interfaces import ICatalogs |
824 | +from schooltool.app.catalog import getAppCatalogs |
825 | + |
826 | + |
827 | +class AppStub(dict, SiteManagerContainer): |
828 | + implements(ISchoolToolApplication) |
829 | + |
830 | + |
831 | +class CatalogStub(dict): |
832 | + |
833 | + def __init__(self, name): |
834 | + self.name = name |
835 | + |
836 | + def __repr__(self): |
837 | + return '<%s %r>' % (self.__class__.__name__, self.name) |
838 | + |
839 | + |
840 | +def provideApplicationStub(): |
841 | + app = AppStub() |
842 | + provideAdapter( |
843 | + lambda ignored: app, |
844 | + adapts=(None,), |
845 | + provides=ISchoolToolApplication) |
846 | + return app |
847 | + |
848 | + |
849 | +def doctest_Catalogs(): |
850 | + """Tests for Catalogs. |
851 | + |
852 | + >>> app = provideApplicationStub() |
853 | + |
854 | + There is an adapter that sets up and returns the catalogs container for the app. |
855 | + |
856 | + >>> list(app.items()) |
857 | + [] |
858 | + |
859 | + >>> catalogs = ICatalogs(app) |
860 | + |
861 | + >>> list(app.items()) |
862 | + [('schooltool.app.catalog:Catalogs', |
863 | + <schooltool.app.catalog.Catalogs object at ...>)] |
864 | + |
865 | + >>> verifyObject(ICatalogs, catalogs) |
866 | + True |
867 | + |
868 | + >>> catalogs is app.values()[0] |
869 | + True |
870 | + |
871 | + Catalogs is a dict-like container. |
872 | + |
873 | + >>> print dict(catalogs) |
874 | + {} |
875 | + |
876 | + """ |
877 | + |
878 | + |
879 | +def doctest_PrepareCatalogContainer(): |
880 | + """Tests for PrepareCatalogContainer. |
881 | + |
882 | + >>> class VersionedCatalogStub(object): |
883 | + ... def __init__(self, expired=False): |
884 | + ... self.expired = expired |
885 | + ... def __repr__(self): |
886 | + ... return '<Stub expired=%s>' % self.expired |
887 | + |
888 | + Let's build an app with some catalog entries. |
889 | + |
890 | + >>> app = provideApplicationStub() |
891 | + >>> catalogs = ICatalogs(app) |
892 | + |
893 | + >>> catalogs['cat-1'] = VersionedCatalogStub(expired=False) |
894 | + >>> catalogs['cat-2'] = VersionedCatalogStub(expired=True) |
895 | + >>> catalogs['cat-3'] = VersionedCatalogStub(expired=False) |
896 | + |
897 | + PrepareCatalogContainer is supposed to be executed as the first action |
898 | + of application catalog startup. |
899 | + |
900 | + >>> from schooltool.app.interfaces import ICatalogStartUp |
901 | + >>> from schooltool.app.catalog import PrepareCatalogContainer |
902 | + |
903 | + >>> action = PrepareCatalogContainer(app) |
904 | + >>> verifyObject(ICatalogStartUp, action) |
905 | + True |
906 | + |
907 | + When executed it marks all catalogs expired. |
908 | + |
909 | + >>> action() |
910 | + |
911 | + >>> print sorted(ICatalogs(app).items()) |
912 | + [(u'cat-1', <Stub expired=True>), |
913 | + (u'cat-2', <Stub expired=True>), |
914 | + (u'cat-3', <Stub expired=True>)] |
915 | + |
916 | + """ |
917 | + |
918 | + |
919 | +def doctest_ExpiredCatalogCleanup(): |
920 | + """Tests for ExpiredCatalogCleanup. |
921 | + |
922 | + >>> class VersionedCatalogStub(object): |
923 | + ... def __init__(self, expired=False): |
924 | + ... self.expired = expired |
925 | + ... def __repr__(self): |
926 | + ... return '<Stub expired=%s>' % self.expired |
927 | + |
928 | + Let's build an app with some catalog entries. |
929 | + |
930 | + >>> app = provideApplicationStub() |
931 | + >>> catalogs = ICatalogs(app) |
932 | + |
933 | + >>> catalogs['cat-1'] = VersionedCatalogStub(expired=False) |
934 | + >>> catalogs['cat-2'] = VersionedCatalogStub(expired=True) |
935 | + >>> catalogs['cat-3'] = VersionedCatalogStub(expired=False) |
936 | + |
937 | + ExpiredCatalogCleanup is supposed to be executed as the last action |
938 | + of application catalog startup. |
939 | + |
940 | + >>> from schooltool.app.interfaces import ICatalogStartUp |
941 | + >>> from schooltool.app.catalog import ExpiredCatalogCleanup |
942 | + |
943 | + >>> action = ExpiredCatalogCleanup(app) |
944 | + >>> verifyObject(ICatalogStartUp, action) |
945 | + True |
946 | + |
947 | + When executed it removes expired catalogs. |
948 | + |
949 | + >>> action() |
950 | + |
951 | + >>> print sorted(ICatalogs(app).items()) |
952 | + [(u'cat-1', <Stub expired=False>), |
953 | + (u'cat-3', <Stub expired=False>)] |
954 | + |
955 | + """ |
956 | + |
957 | + |
958 | +def doctest_CatalogFactory(): |
959 | + """Tests for CatalogFactory. |
960 | + |
961 | + >>> app = provideApplicationStub() |
962 | + |
963 | + CatalogFactory is a base class for creating and registering versioned catalogs. |
964 | + |
965 | + >>> from schooltool.app.catalog import CatalogFactory |
966 | + >>> factory = CatalogFactory(app) |
967 | + |
968 | + >>> factory.createCatalog() |
969 | + Traceback (most recent call last): |
970 | + ... |
971 | + NotImplementedError |
972 | + |
973 | + >>> factory.setIndexes(None) |
974 | + Traceback (most recent call last): |
975 | + ... |
976 | + NotImplementedError |
977 | + |
978 | + It is also an action to be executed on application catalog startup. |
979 | + |
980 | + >>> from schooltool.app.interfaces import ICatalogStartUp |
981 | + |
982 | + >>> class CatalogFactoryForTest(CatalogFactory): |
983 | + ... version = 1 |
984 | + ... def createCatalog(self): |
985 | + ... return CatalogStub('test') |
986 | + ... def setIndexes(self, catalog): |
987 | + ... catalog['idx-1'] = 'Stub index 1' |
988 | + |
989 | + >>> factory = CatalogFactoryForTest(app) |
990 | + |
991 | + >>> verifyObject(ICatalogStartUp, factory) |
992 | + True |
993 | + |
994 | + It defines some control on when it wants to be executed. |
995 | + |
996 | + >>> factory.after |
997 | + ('prepare-catalog-container',) |
998 | + |
999 | + >>> factory.before |
1000 | + ('expired-catalog-cleanup',) |
1001 | + |
1002 | + The class itself has some helpers to fetch the catalog from app. |
1003 | + |
1004 | + >>> print CatalogFactoryForTest.key() |
1005 | + catalog:schooltool.app.tests.test_catalog.CatalogFactoryForTest |
1006 | + |
1007 | + >>> print CatalogFactoryForTest.get() |
1008 | + None |
1009 | + |
1010 | + Well, the catalog was not set up yet. Let's build it. |
1011 | + |
1012 | + >>> from schooltool.app.interfaces import IVersionedCatalog |
1013 | + |
1014 | + >>> factory() |
1015 | + |
1016 | + >>> catalog = CatalogFactoryForTest.get() |
1017 | + |
1018 | + >>> print catalog |
1019 | + <CatalogStub 'test'> |
1020 | + |
1021 | + >>> sorted(catalog.items()) |
1022 | + [('idx-1', 'Stub index 1')] |
1023 | + |
1024 | + Both catalog and it's version entry are stored in app's catalog container. |
1025 | + |
1026 | + >>> version_entry = catalog.__parent__ |
1027 | + |
1028 | + >>> print version_entry |
1029 | + <VersionedCatalog v. u'1'>: <CatalogStub 'test'> |
1030 | + |
1031 | + >>> verifyObject(IVersionedCatalog, version_entry) |
1032 | + True |
1033 | + |
1034 | + >>> print version_entry.__parent__ |
1035 | + <schooltool.app.catalog.Catalogs object at ...> |
1036 | + |
1037 | + >>> version_entry.__parent__ is ICatalogs(app) |
1038 | + True |
1039 | + |
1040 | + >>> print version_entry.__name__ |
1041 | + catalog:schooltool.app.tests.test_catalog.CatalogFactoryForTest |
1042 | + |
1043 | + >>> print version_entry.__name__ == CatalogFactoryForTest.key() |
1044 | + True |
1045 | + |
1046 | + """ |
1047 | + |
1048 | + |
1049 | +def doctest_CatalogFactory_versioning(): |
1050 | + """Tests for CatalogFactory handling of catalog versions. |
1051 | + |
1052 | + >>> class CatalogCreatorMixin(object): |
1053 | + ... def createCatalog(self): |
1054 | + ... return CatalogStub('test-1') |
1055 | + ... def setIndexes(self, catalog): |
1056 | + ... catalog['idx-1'] = 'Stub index 1' |
1057 | + |
1058 | + >>> app = provideApplicationStub() |
1059 | + >>> catalogs = ICatalogs(app) |
1060 | + |
1061 | + Let's create a catalog first. |
1062 | + |
1063 | + >>> from schooltool.app.catalog import CatalogFactory |
1064 | + >>> class Factory1(CatalogCreatorMixin, CatalogFactory): |
1065 | + ... version = 1 |
1066 | + |
1067 | + >>> factory1 = Factory1(app) |
1068 | + |
1069 | + >>> factory1() |
1070 | + >>> catalog1 = Factory1.get() |
1071 | + |
1072 | + >>> print catalog1 |
1073 | + <CatalogStub 'test-1'> |
1074 | + |
1075 | + >>> print sorted(catalogs.keys()) |
1076 | + [u'catalog:schooltool.app.tests.test_catalog.Factory1'] |
1077 | + |
1078 | + If the version stays the same, factory leaves the catalog untouched, but |
1079 | + changes it's expired status. |
1080 | + |
1081 | + >>> class Factory1(CatalogCreatorMixin, CatalogFactory): |
1082 | + ... version = 1 |
1083 | + |
1084 | + >>> catalog1.__parent__.expired = True |
1085 | + |
1086 | + >>> factory1 = Factory1(app) |
1087 | + >>> factory1() |
1088 | + |
1089 | + >>> Factory1.get() is catalog1 |
1090 | + True |
1091 | + |
1092 | + >>> catalog1.__parent__.expired |
1093 | + False |
1094 | + |
1095 | + >>> print sorted(catalogs.keys()) |
1096 | + [u'catalog:schooltool.app.tests.test_catalog.Factory1'] |
1097 | + |
1098 | + If we changed the version, catalog gets re-created. |
1099 | + |
1100 | + >>> class Factory1(CatalogCreatorMixin, CatalogFactory): |
1101 | + ... version = 1.2 |
1102 | + ... def setIndexes(self, catalog): |
1103 | + ... catalog['idx-1'] = 'Stub index 1 point 2' |
1104 | + |
1105 | + >>> factory12 = Factory1(app) |
1106 | + >>> factory12() |
1107 | + |
1108 | + >>> catalog12 = Factory1.get() |
1109 | + >>> catalog12 is catalog1 |
1110 | + False |
1111 | + |
1112 | + >>> print sorted(catalog12.items()) |
1113 | + [('idx-1', 'Stub index 1 point 2')] |
1114 | + |
1115 | + CatalogFactory actually uses the getVersion method, not the version |
1116 | + attribute. Let's override getVersion |
1117 | + |
1118 | + >>> class Factory1(CatalogCreatorMixin, CatalogFactory): |
1119 | + ... version = 1.2 |
1120 | + ... def getVersion(self): |
1121 | + ... return 'stable-version' |
1122 | + |
1123 | + >>> factory = Factory1(app) |
1124 | + >>> factory() |
1125 | + |
1126 | + >>> catalog_stable = Factory1.get() |
1127 | + |
1128 | + >>> catalog_stable is catalog12 |
1129 | + False |
1130 | + |
1131 | + >>> print Factory1.get().__parent__ |
1132 | + <VersionedCatalog v. 'stable-version'>: <CatalogStub 'test-1'> |
1133 | + |
1134 | + >>> class Factory1(CatalogCreatorMixin, CatalogFactory): |
1135 | + ... version = 1.314 |
1136 | + ... def getVersion(self): |
1137 | + ... return 'stable-version' |
1138 | + |
1139 | + >>> factory1 = Factory1(app) |
1140 | + >>> factory1() |
1141 | + |
1142 | + >>> Factory1.get() is catalog_stable |
1143 | + True |
1144 | + |
1145 | + """ |
1146 | + |
1147 | + |
1148 | +def doctest_CatalogImplementing(): |
1149 | + """Tests for CatalogImplementing. This is a factory of catalogs that |
1150 | + contain only objects implementing given interface. |
1151 | + |
1152 | + Say we have two objects implementing very similar interfaces. |
1153 | + |
1154 | + >>> from zope.schema import TextLine |
1155 | + |
1156 | + >>> class TitledObject(object): |
1157 | + ... def __init__(self, title): |
1158 | + ... self.title = title |
1159 | + ... def __repr__(self): |
1160 | + ... return '<%s (%r)>' % (self.__class__.__name__, self.title) |
1161 | + |
1162 | + >>> class IFoo(Interface): |
1163 | + ... title = TextLine(title=u"Title") |
1164 | + >>> class Foo(TitledObject): |
1165 | + ... implements(IFoo) |
1166 | + |
1167 | + >>> class IBar(Interface): |
1168 | + ... title = TextLine(title=u"Title") |
1169 | + >>> class Bar(TitledObject): |
1170 | + ... implements(IBar) |
1171 | + |
1172 | + >>> app = provideApplicationStub() |
1173 | + >>> app['objects'] = { |
1174 | + ... 1: Foo('one'), |
1175 | + ... 2: Bar('two'), |
1176 | + ... 3: Foo('three'), |
1177 | + ... } |
1178 | + |
1179 | + Let's create a catalog that indexes titles of IFoo. |
1180 | + |
1181 | + >>> from zope.container.contained import Contained |
1182 | + >>> from schooltool.app.catalog import CatalogImplementing |
1183 | + |
1184 | + >>> class TitleIndex(Contained): |
1185 | + ... def __init__(self): |
1186 | + ... self.data = [] |
1187 | + ... def index_doc(self, id, value): |
1188 | + ... self.data.append((id, value)) |
1189 | + |
1190 | + >>> class CatalogTitles(CatalogImplementing): |
1191 | + ... version = 1 |
1192 | + ... interface = IFoo |
1193 | + ... def setIndexes(self, catalog): |
1194 | + ... catalog['title'] = TitleIndex() |
1195 | + |
1196 | + >>> factory = CatalogTitles(app) |
1197 | + >>> factory() |
1198 | + |
1199 | + >>> catalog = CatalogTitles.get() |
1200 | + >>> print catalog.__parent__ |
1201 | + <VersionedCatalog v. |
1202 | + u'interface:schooltool.app.tests.test_catalog.IFoo, |
1203 | + version:1'>: |
1204 | + <zc.catalog.extentcatalog.Catalog object at ...> |
1205 | + |
1206 | + This catalog only indexes objects implementing IFoo. |
1207 | + |
1208 | + >>> def index_app(catalog): |
1209 | + ... for docid, doc in app['objects'].items(): |
1210 | + ... catalog.index_doc(docid, doc) |
1211 | + ... print sorted(catalog['title'].data) |
1212 | + |
1213 | + >>> index_app(CatalogTitles.get()) |
1214 | + [(1, <Foo ('one')>), (3, <Foo ('three')>)] |
1215 | + |
1216 | + Catalog indexes are untouched after app restart. |
1217 | + |
1218 | + >>> factory = CatalogTitles(app) |
1219 | + >>> factory() |
1220 | + |
1221 | + >>> CatalogTitles.get() is catalog |
1222 | + True |
1223 | + |
1224 | + >>> print sorted(CatalogTitles.get()['title'].data) |
1225 | + [(1, <Foo ('one')>), (3, <Foo ('three')>)] |
1226 | + |
1227 | + If we change the version, catalog will be re-created, as expected. |
1228 | + |
1229 | + >>> class CatalogTitles(CatalogImplementing): |
1230 | + ... version = 2 |
1231 | + ... interface = IFoo |
1232 | + ... def setIndexes(self, catalog): |
1233 | + ... catalog['title'] = TitleIndex() |
1234 | + |
1235 | + >>> factory = CatalogTitles(app) |
1236 | + >>> factory() |
1237 | + |
1238 | + >>> catalog2 = CatalogTitles.get() |
1239 | + >>> catalog2 is catalog |
1240 | + False |
1241 | + |
1242 | + We trust other machinery to reindex the catalog. |
1243 | + |
1244 | + >>> print sorted(CatalogTitles.get()['title'].data) |
1245 | + [] |
1246 | + |
1247 | + If we change the interface, catalog also gets re-created. |
1248 | + |
1249 | + >>> index_app(CatalogTitles.get()) |
1250 | + [(1, <Foo ('one')>), (3, <Foo ('three')>)] |
1251 | + |
1252 | + >>> class CatalogTitles(CatalogImplementing): |
1253 | + ... version = 2 |
1254 | + ... interface = IBar |
1255 | + ... def setIndexes(self, catalog): |
1256 | + ... catalog['title'] = TitleIndex() |
1257 | + |
1258 | + >>> factory = CatalogTitles(app) |
1259 | + >>> factory() |
1260 | + |
1261 | + >>> CatalogTitles.get() is catalog2 |
1262 | + False |
1263 | + |
1264 | + >>> index_app(CatalogTitles.get()) |
1265 | + [(2, <Bar ('two')>)] |
1266 | + |
1267 | + >>> catalog3 = CatalogTitles.get() |
1268 | + >>> print catalog3.__parent__ |
1269 | + <VersionedCatalog v. |
1270 | + u'interface:schooltool.app.tests.test_catalog.IBar, |
1271 | + version:2'>: |
1272 | + <zc.catalog.extentcatalog.Catalog object at ...> |
1273 | + |
1274 | + """ |
1275 | + |
1276 | + |
1277 | +def doctest_AttributeCatalog(): |
1278 | + """Tests for AttributeCatalog. This is a factory of catalogs that |
1279 | + index attributes of objects implementing given interface. |
1280 | + |
1281 | + Say we have an object we want to catalog. |
1282 | + |
1283 | + >>> from zope.schema import TextLine |
1284 | + |
1285 | + >>> class ITriplet(Interface): |
1286 | + ... a = TextLine(title=u"Title") |
1287 | + ... b = TextLine(title=u"Title") |
1288 | + ... c = TextLine(title=u"Title") |
1289 | + >>> class Triplet(object): |
1290 | + ... implements(ITriplet) |
1291 | + ... def __init__(self, a, b, c): |
1292 | + ... self.a, self.b, self.c = a, b, c |
1293 | + ... def __repr__(self): |
1294 | + ... return '<%s (%s:%s:%s)>' % ( |
1295 | + ... self.__class__.__name__, |
1296 | + ... self.a, self.b, self.c) |
1297 | + |
1298 | + >>> class Foo(object): |
1299 | + ... implements(Interface) |
1300 | + ... a = b = c = 0 |
1301 | + |
1302 | + >>> app = provideApplicationStub() |
1303 | + >>> app['objects'] = { |
1304 | + ... 0: Foo(), |
1305 | + ... 1: Triplet('one', 'eins', 'I'), |
1306 | + ... 2: Triplet('two', 'zwei', 'II'), |
1307 | + ... 3: Triplet('three', 'drei', 'III'), |
1308 | + ... } |
1309 | + |
1310 | + Let's create a catalog that indexes few attributes of the triplet. |
1311 | + |
1312 | + >>> from schooltool.app.catalog import AttributeCatalog |
1313 | + |
1314 | + >>> class CatalogTriplets(AttributeCatalog): |
1315 | + ... version = 1 |
1316 | + ... interface = ITriplet |
1317 | + ... attributes = ('a', 'c') |
1318 | + |
1319 | + >>> factory = CatalogTriplets(app) |
1320 | + >>> factory() |
1321 | + |
1322 | + And index some objects. |
1323 | + |
1324 | + >>> def index_app(catalog): |
1325 | + ... for docid, doc in app['objects'].items(): |
1326 | + ... catalog.index_doc(docid, doc) |
1327 | + ... for name, index in catalog.items(): |
1328 | + ... print 'catalog[%r]: %s' % ( |
1329 | + ... name, |
1330 | + ... sorted(index.documents_to_values.items())) |
1331 | + |
1332 | + >>> index_app(CatalogTriplets.get()) |
1333 | + catalog[u'a']: [(1, 'one'), (2, 'two'), (3, 'three')] |
1334 | + catalog[u'c']: [(1, 'I'), (2, 'II'), (3, 'III')] |
1335 | + |
1336 | + We can see that version of the catalog also includes the list of attributes, |
1337 | + so the catalog will be recreated when attributes, interface or version changes. |
1338 | + |
1339 | + >>> catalog = CatalogTriplets.get() |
1340 | + >>> print catalog.__parent__ |
1341 | + <VersionedCatalog v. |
1342 | + u"attributes:('a', 'c'), |
1343 | + interface:schooltool.app.tests.test_catalog.ITriplet, |
1344 | + version:1">: |
1345 | + <zc.catalog.extentcatalog.Catalog object at ...> |
1346 | + |
1347 | + >>> class CatalogTriplets(AttributeCatalog): |
1348 | + ... version = 1 |
1349 | + ... interface = ITriplet |
1350 | + ... attributes = ('b') |
1351 | + |
1352 | + >>> factory = CatalogTriplets(app) |
1353 | + >>> factory() |
1354 | + |
1355 | + >>> CatalogTriplets.get() is catalog |
1356 | + False |
1357 | + |
1358 | + >>> index_app(CatalogTriplets.get()) |
1359 | + catalog[u'b']: [(1, 'eins'), (2, 'zwei'), (3, 'drei')] |
1360 | + |
1361 | + """ |
1362 | + |
1363 | + |
1364 | +def doctest_catalog_subscribers(): |
1365 | + """Tests for catalog subscribers. |
1366 | + |
1367 | + Let's provide our subscribers. |
1368 | + |
1369 | + >>> from zope.component import provideHandler |
1370 | + >>> from schooltool.app.catalog import indexDocSubscriber |
1371 | + >>> from schooltool.app.catalog import reindexDocSubscriber |
1372 | + >>> from schooltool.app.catalog import unindexDocSubscriber |
1373 | + |
1374 | + >>> provideHandler(indexDocSubscriber) |
1375 | + >>> provideHandler(reindexDocSubscriber) |
1376 | + >>> provideHandler(unindexDocSubscriber) |
1377 | + |
1378 | + And set up the event firing helpers. |
1379 | + |
1380 | + >>> from zope.component import provideUtility, queryUtility |
1381 | + >>> from zope.event import notify |
1382 | + >>> from zope.intid.interfaces import ( |
1383 | + ... IIntIds, IntIdAddedEvent, IntIdRemovedEvent) |
1384 | + >>> from zope.lifecycleevent import ( |
1385 | + ... ObjectAddedEvent, ObjectRemovedEvent, ObjectModifiedEvent) |
1386 | + |
1387 | + >>> def addAndNotify(obj, obj_id): |
1388 | + ... util = queryUtility(IIntIds) |
1389 | + ... if util is None: |
1390 | + ... print "No IIntIds utility, so don't fire IntIdAddedEvent" |
1391 | + ... return |
1392 | + ... util[obj] = obj_id |
1393 | + ... print 'firing IntIdAddedEvent' |
1394 | + ... notify(IntIdAddedEvent(obj, ObjectAddedEvent(obj))) |
1395 | + |
1396 | + >>> def notifyModified(obj): |
1397 | + ... print 'firing ObjectModifiedEvent' |
1398 | + ... notify(ObjectModifiedEvent(obj)) |
1399 | + |
1400 | + >>> def notifyAndRemove(obj): |
1401 | + ... util = queryUtility(IIntIds) |
1402 | + ... if util is None: |
1403 | + ... print "No IIntIds utility, so don't fire IntIdRemovedEvent" |
1404 | + ... return |
1405 | + ... print 'firing IntIdRemovedEvent' |
1406 | + ... notify(IntIdRemovedEvent(obj, ObjectAddedEvent(obj))) |
1407 | + ... del util[obj] |
1408 | + |
1409 | + When database is being set up, we may have no SchoolToolApplication and |
1410 | + no IntIds utility. |
1411 | + |
1412 | + >>> class TestObj(object): |
1413 | + ... __parent__ = __name__ = None |
1414 | + ... def __init__(self, name): |
1415 | + ... self.name = name |
1416 | + ... def __repr__(self): |
1417 | + ... return '<%s %r>' % (self.__class__.__name__, self.name) |
1418 | + |
1419 | + >>> test_one = TestObj('missing_one') |
1420 | + >>> addAndNotify(test_one, 1) |
1421 | + No IIntIds utility, so don't fire IntIdAddedEvent |
1422 | + |
1423 | + >>> notifyModified(test_one) |
1424 | + firing ObjectModifiedEvent |
1425 | + |
1426 | + >>> notifyAndRemove(test_one) |
1427 | + No IIntIds utility, so don't fire IntIdRemovedEvent |
1428 | + |
1429 | + Having IntIds utility is not enough as catalogs live within the application |
1430 | + itself. Subscribers handle this case also. |
1431 | + |
1432 | + >>> class IntIdsStub(dict): |
1433 | + ... def getId(self, obj): |
1434 | + ... return self[obj] |
1435 | + ... def queryId(self, obj): |
1436 | + ... return self.get(obj) |
1437 | + |
1438 | + >>> provideUtility(IntIdsStub(), IIntIds) |
1439 | + |
1440 | + >>> test_two = TestObj('two') |
1441 | + >>> addAndNotify(test_two, 2) |
1442 | + firing IntIdAddedEvent |
1443 | + |
1444 | + >>> notifyModified(test_two) |
1445 | + firing ObjectModifiedEvent |
1446 | + |
1447 | + >>> notifyAndRemove(test_two) |
1448 | + firing IntIdRemovedEvent |
1449 | + |
1450 | + Let's provide an application and set up a catalog. |
1451 | + |
1452 | + >>> from schooltool.app.catalog import VersionedCatalog |
1453 | + |
1454 | + >>> class PrintingCatalogStub(CatalogStub): |
1455 | + ... def index_doc(self, doc_id, doc): |
1456 | + ... print 'CatalogStub(%r) indexed doc %s (%s)' % ( |
1457 | + ... self.name, doc_id, doc) |
1458 | + ... def unindex_doc(self, doc_id): |
1459 | + ... print 'CatalogStub(%r) unindexed doc %s' % ( |
1460 | + ... self.name, doc_id) |
1461 | + |
1462 | + >>> app = provideApplicationStub() |
1463 | + >>> catalogs = ICatalogs(app) |
1464 | + >>> catalogs['demo'] = VersionedCatalog(PrintingCatalogStub('demo'), 'v1') |
1465 | + |
1466 | + We can now see objects being indexed. |
1467 | + |
1468 | + >>> test_three = TestObj('three') |
1469 | + >>> addAndNotify(test_three, 3) |
1470 | + firing IntIdAddedEvent |
1471 | + CatalogStub('demo') indexed doc 3 (<TestObj 'three'>) |
1472 | + |
1473 | + >>> test_three.name='three and a half' |
1474 | + >>> notifyModified(test_three) |
1475 | + firing ObjectModifiedEvent |
1476 | + CatalogStub('demo') indexed doc 3 (<TestObj 'three and a half'>) |
1477 | + |
1478 | + >>> notifyAndRemove(test_three) |
1479 | + firing IntIdRemovedEvent |
1480 | + CatalogStub('demo') unindexed doc 3 |
1481 | + |
1482 | + """ |
1483 | + |
1484 | + |
1485 | +def setUp(test): |
1486 | + setup.placefulSetUp() |
1487 | + provideAdapter(getAppCatalogs) |
1488 | + |
1489 | + |
1490 | +def tearDown(test): |
1491 | + setup.placefulTearDown() |
1492 | + |
1493 | + |
1494 | +def provideStubAdapter(factory, adapts=None, provides=None, name=u''): |
1495 | + sm = getSite().getSiteManager() |
1496 | + sm.registerAdapter(factory, required=adapts, provided=provides, name=name) |
1497 | + |
1498 | + |
1499 | +def unregisterStubAdapter(factory, adapts=None, provides=None, name=u''): |
1500 | + sm = getSite().getSiteManager() |
1501 | + sm.unregisterAdapter(factory, required=adapts, provided=provides, name=name) |
1502 | + |
1503 | + |
1504 | +def setUpIntegration(test): |
1505 | + setup.placefulSetUp() |
1506 | + zcml = ZCMLWrapper() |
1507 | + zcml.setUp( |
1508 | + namespaces={"": "http://namespaces.zope.org/zope"}, |
1509 | + i18n_domain='schooltool') |
1510 | + zcml.include('zope.app.zcmlfiles') |
1511 | + zcml.include('schooltool.app', file='catalog.zcml') |
1512 | + root = rootFolder() |
1513 | + root['app'] = provideApplicationStub() |
1514 | + setup.createSiteManager(root['app'], setsite=True) |
1515 | + test.globs.update({ |
1516 | + 'zcml': zcml, |
1517 | + 'CatalogStub': CatalogStub, |
1518 | + 'provideAdapter': provideStubAdapter, |
1519 | + 'unregisterAdapter': unregisterStubAdapter, |
1520 | + }) |
1521 | + |
1522 | + |
1523 | +def test_suite(): |
1524 | + optionflags = (doctest.NORMALIZE_WHITESPACE | |
1525 | + doctest.ELLIPSIS | |
1526 | + doctest.REPORT_NDIFF) |
1527 | + return unittest.TestSuite([ |
1528 | + doctest.DocTestSuite(setUp=setUp, tearDown=tearDown, |
1529 | + optionflags=optionflags), |
1530 | + doctest.DocFileSuite( |
1531 | + 'catalog-integration.txt', |
1532 | + setUp=setUpIntegration, tearDown=tearDown, |
1533 | + optionflags=optionflags), |
1534 | + ]) |
1535 | + |
1536 | +if __name__ == '__main__': |
1537 | + unittest.main(defaultTest='test_suite') |
1538 | |
1539 | === modified file 'src/schooltool/app/tests/test_security.py' |
1540 | --- src/schooltool/app/tests/test_security.py 2010-01-21 17:31:17 +0000 |
1541 | +++ src/schooltool/app/tests/test_security.py 2010-05-28 11:09:37 +0000 |
1542 | @@ -65,10 +65,13 @@ |
1543 | |
1544 | |
1545 | def test_suite(): |
1546 | + optionflags = (doctest.ELLIPSIS | |
1547 | + doctest.NORMALIZE_WHITESPACE | |
1548 | + doctest.REPORT_NDIFF) |
1549 | return unittest.TestSuite([ |
1550 | unittest.makeSuite(TestAuthSetUpSubscriber), |
1551 | - doctest.DocTestSuite(optionflags=doctest.ELLIPSIS), |
1552 | - doctest.DocFileSuite('../security.txt', optionflags=doctest.ELLIPSIS), |
1553 | + doctest.DocTestSuite(optionflags=optionflags), |
1554 | + doctest.DocFileSuite('../security.txt', optionflags=optionflags), |
1555 | ]) |
1556 | |
1557 | |
1558 | |
1559 | === modified file 'src/schooltool/basicperson/configure.zcml' |
1560 | --- src/schooltool/basicperson/configure.zcml 2009-11-02 13:45:40 +0000 |
1561 | +++ src/schooltool/basicperson/configure.zcml 2010-05-28 11:09:37 +0000 |
1562 | @@ -64,11 +64,6 @@ |
1563 | provides="schooltool.app.interfaces.ICalendarParentCrowd" |
1564 | name="schooltool.view" /> |
1565 | |
1566 | - <subscriber |
1567 | - for="schooltool.app.interfaces.ICatalogSetUpEvent" |
1568 | - handler=".person.catalogSetUpSubscriber" |
1569 | - /> |
1570 | - |
1571 | <utility |
1572 | factory=".vocabularies.groupVocabularyFactory" |
1573 | provides="zope.schema.interfaces.IVocabularyFactory" |
1574 | |
1575 | === modified file 'src/schooltool/basicperson/overrides.zcml' |
1576 | --- src/schooltool/basicperson/overrides.zcml 2009-12-23 20:59:28 +0000 |
1577 | +++ src/schooltool/basicperson/overrides.zcml 2010-05-28 11:09:37 +0000 |
1578 | @@ -10,9 +10,12 @@ |
1579 | name="person_info_viewers" |
1580 | factory=".security.PersonInfoViewersCrowd" /> |
1581 | |
1582 | + <adapter factory=".person.PersonCatalog" |
1583 | + name="schooltool.person.person.PersonCatalog" /> |
1584 | + |
1585 | <adapter |
1586 | for="schooltool.person.interfaces.IPersonContainer" |
1587 | - factory=".person.getPersonContainerCatalog" |
1588 | + factory=".person.getPersonCatalog" |
1589 | provides="zope.catalog.interfaces.ICatalog" /> |
1590 | |
1591 | <configure |
1592 | |
1593 | === modified file 'src/schooltool/basicperson/person.py' |
1594 | --- src/schooltool/basicperson/person.py 2009-12-23 20:59:28 +0000 |
1595 | +++ src/schooltool/basicperson/person.py 2010-05-28 11:09:37 +0000 |
1596 | @@ -21,11 +21,6 @@ |
1597 | """ |
1598 | from zope.interface import implements |
1599 | from zope.component import adapts |
1600 | -from zope.catalog.interfaces import ICatalog |
1601 | -from zope.catalog.catalog import Catalog |
1602 | -from zope.component import getUtility |
1603 | - |
1604 | -from zc.catalog.catalogindex import ValueIndex |
1605 | |
1606 | from schooltool.person.person import Person |
1607 | from schooltool.person.interfaces import IPersonFactory |
1608 | @@ -37,12 +32,10 @@ |
1609 | from schooltool.relationship import RelationshipProperty |
1610 | from schooltool.basicperson.advisor import URIAdvisor, URIAdvising, URIStudent |
1611 | from schooltool.basicperson.interfaces import IBasicPerson |
1612 | +from schooltool.app.catalog import AttributeCatalog |
1613 | from schooltool.common import SchoolToolMessage as _ |
1614 | |
1615 | |
1616 | -PERSON_CATALOG_KEY = 'schooltool.basicperson' |
1617 | - |
1618 | - |
1619 | class BasicPerson(Person): |
1620 | implements(IBasicPerson) |
1621 | |
1622 | @@ -119,16 +112,11 @@ |
1623 | PersonInstructorsCrowd(self.context).contains(principal)) |
1624 | |
1625 | |
1626 | -def catalogSetUp(catalog): |
1627 | - catalog['__name__'] = ValueIndex('__name__', IBasicPerson) |
1628 | - catalog['title'] = ValueIndex('title', IBasicPerson) |
1629 | - catalog['first_name'] = ValueIndex('first_name', IBasicPerson) |
1630 | - catalog['last_name'] = ValueIndex('last_name', IBasicPerson) |
1631 | - |
1632 | - |
1633 | -catalogSetUpSubscriber = UtilitySetUp( |
1634 | - Catalog, ICatalog, PERSON_CATALOG_KEY, setUp=catalogSetUp) |
1635 | - |
1636 | - |
1637 | -def getPersonContainerCatalog(container): |
1638 | - return getUtility(ICatalog, PERSON_CATALOG_KEY) |
1639 | +class PersonCatalog(AttributeCatalog): |
1640 | + |
1641 | + version = '1 - replaced catalog utility' |
1642 | + interface = IBasicPerson |
1643 | + attributes = ('__name__', 'title', 'first_name', 'last_name') |
1644 | + |
1645 | + |
1646 | +getPersonCatalog = PersonCatalog.get |
1647 | |
1648 | === modified file 'src/schooltool/commendation/browser.py' |
1649 | --- src/schooltool/commendation/browser.py 2010-04-28 23:13:27 +0000 |
1650 | +++ src/schooltool/commendation/browser.py 2010-05-28 11:09:37 +0000 |
1651 | @@ -32,6 +32,7 @@ |
1652 | from schooltool.commendation import interfaces, commendation |
1653 | from schooltool.commendation.interfaces import _ |
1654 | from schooltool.app import app |
1655 | +from schooltool.app.interfaces import ISchoolToolApplication |
1656 | |
1657 | |
1658 | # This is a simple view class that provides the information of a commendation |
1659 | @@ -164,7 +165,7 @@ |
1660 | self.request.get('search_scope', 'group')] |
1661 | |
1662 | # Get the SchoolTool application instance and access its annotations. |
1663 | - stapp = app.getSchoolToolApplication() |
1664 | + stapp = ISchoolToolApplication(None) |
1665 | annotations = IAnnotations(stapp) |
1666 | # A list comprehension that iterates through all the commendations and |
1667 | # applies the filter one-by-one. |
1668 | |
1669 | === modified file 'src/schooltool/commendation/commendation.py' |
1670 | --- src/schooltool/commendation/commendation.py 2010-04-28 23:13:27 +0000 |
1671 | +++ src/schooltool/commendation/commendation.py 2010-05-28 11:09:37 +0000 |
1672 | @@ -30,6 +30,7 @@ |
1673 | from zope.security.management import queryInteraction |
1674 | |
1675 | from schooltool.app import app |
1676 | +from schooltool.app.interfaces import ISchoolToolApplication |
1677 | from schooltool.commendation.interfaces import ICommendation |
1678 | from schooltool.commendation.interfaces import ICommendations |
1679 | from schooltool.commendation.interfaces import ICommendationContained |
1680 | @@ -111,7 +112,7 @@ |
1681 | # Whereever we are, get the SchoolTool application and access its |
1682 | # annotations. Then add the commendation to the list of all cached |
1683 | # commendations. |
1684 | - stapp = app.getSchoolToolApplication() |
1685 | + stapp = ISchoolToolApplication(None) |
1686 | annotations = IAnnotations(stapp) |
1687 | if CommendationsCacheKey not in annotations: |
1688 | annotations[CommendationsCacheKey] = persistent.list.PersistentList() |
1689 | |
1690 | === modified file 'src/schooltool/contact/configure.zcml' |
1691 | --- src/schooltool/contact/configure.zcml 2010-01-19 12:09:22 +0000 |
1692 | +++ src/schooltool/contact/configure.zcml 2010-05-28 11:09:37 +0000 |
1693 | @@ -82,10 +82,9 @@ |
1694 | factory=".basicperson.PersonRemovedSubsciber" |
1695 | name="notify_person_contact_removed" /> |
1696 | |
1697 | - <subscriber |
1698 | - for="schooltool.app.interfaces.ICatalogSetUpEvent" |
1699 | - handler=".contact.catalogSetUpSubscriber" |
1700 | - /> |
1701 | + <adapter |
1702 | + factory=".contact.ContactCatalog" |
1703 | + name="schooltool.contact.contact.ContactCatalog" /> |
1704 | |
1705 | <adapter |
1706 | for=".interfaces.IContactContainer" |
1707 | |
1708 | === modified file 'src/schooltool/contact/contact.py' |
1709 | --- src/schooltool/contact/contact.py 2010-01-19 12:09:22 +0000 |
1710 | +++ src/schooltool/contact/contact.py 2010-05-28 11:09:37 +0000 |
1711 | @@ -25,18 +25,14 @@ |
1712 | from zope.component import adapter |
1713 | from zope.interface import implementer |
1714 | from zope.interface import implements |
1715 | -from zope.catalog.interfaces import ICatalog |
1716 | from zope.intid.interfaces import IIntIds |
1717 | from zope.container.contained import Contained |
1718 | from zope.container.btree import BTreeContainer |
1719 | from zope.component import getUtility |
1720 | |
1721 | -from zc.catalog.catalogindex import ValueIndex |
1722 | -from zc.catalog.extentcatalog import Catalog |
1723 | -from zc.catalog.extentcatalog import FilterExtent |
1724 | - |
1725 | from schooltool.app.interfaces import ISchoolToolApplication |
1726 | from schooltool.app.app import InitBase, StartUpBase |
1727 | +from schooltool.app.catalog import AttributeCatalog |
1728 | from schooltool.utility.utility import UtilitySetUp |
1729 | from schooltool.contact.interfaces import IContactPersonInfo |
1730 | from schooltool.contact.interfaces import IContactable |
1731 | @@ -49,7 +45,6 @@ |
1732 | from schooltool.relationship.relationship import RelationshipSchema |
1733 | from schooltool.securitypolicy import crowds |
1734 | from schooltool.table.catalog import ConvertingIndex |
1735 | -from schooltool.table.catalog import FilterImplementing |
1736 | from schooltool.table.table import simple_form_key |
1737 | from schooltool.person.interfaces import IPerson |
1738 | from schooltool.course.section import PersonInstructorsCrowd |
1739 | @@ -57,8 +52,6 @@ |
1740 | from schooltool.common import SchoolToolMessage as _ |
1741 | |
1742 | |
1743 | -CONTACT_CATALOG_KEY = 'schooltool.contact' |
1744 | - |
1745 | URIContactRelationship = URIObject('http://schooltool.org/ns/contact', |
1746 | 'Contact relationship', |
1747 | 'The contact relationship.') |
1748 | @@ -186,22 +179,17 @@ |
1749 | return 'contacts.%s%x' % (simple_form_key(contact), doc_id) |
1750 | |
1751 | |
1752 | -def catalogFactory(): |
1753 | - return Catalog(FilterExtent(FilterImplementing(IContact))) |
1754 | - |
1755 | - |
1756 | -def catalogSetUp(catalog): |
1757 | - catalog['form_keys'] = ConvertingIndex(converter=IUniqueFormKey) |
1758 | - catalog['first_name'] = ValueIndex('first_name') |
1759 | - catalog['last_name'] = ValueIndex('last_name') |
1760 | - |
1761 | - |
1762 | -catalogSetUpSubscriber = UtilitySetUp( |
1763 | - catalogFactory, ICatalog, CONTACT_CATALOG_KEY, setUp=catalogSetUp) |
1764 | - |
1765 | - |
1766 | -def getContactCatalog(container): |
1767 | - return getUtility(ICatalog, CONTACT_CATALOG_KEY) |
1768 | +class ContactCatalog(AttributeCatalog): |
1769 | + version = '1 - replaced catalog utility' |
1770 | + interface = IContact |
1771 | + attributes = ('first_name', 'last_name') |
1772 | + |
1773 | + def setIndexes(self, catalog): |
1774 | + super(ContactCatalog, self).setIndexes(catalog) |
1775 | + catalog['form_keys'] = ConvertingIndex(converter=IUniqueFormKey) |
1776 | + |
1777 | + |
1778 | +getContactCatalog = ContactCatalog.get |
1779 | |
1780 | |
1781 | class ContactPersonInstructorsCrowd(PersonInstructorsCrowd): |
1782 | @@ -218,4 +206,3 @@ |
1783 | if user in section.instructors: |
1784 | return True |
1785 | return False |
1786 | - |
1787 | |
1788 | === modified file 'src/schooltool/generations/__init__.py' |
1789 | --- src/schooltool/generations/__init__.py 2010-04-27 13:40:34 +0000 |
1790 | +++ src/schooltool/generations/__init__.py 2010-05-28 11:09:37 +0000 |
1791 | @@ -25,6 +25,6 @@ |
1792 | from zope.app.generations.generations import SchemaManager |
1793 | |
1794 | schemaManager = SchemaManager( |
1795 | - minimum_generation=34, |
1796 | - generation=34, |
1797 | + minimum_generation=35, |
1798 | + generation=35, |
1799 | package_name='schooltool.generations') |
1800 | |
1801 | === modified file 'src/schooltool/generations/evolve33.py' |
1802 | --- src/schooltool/generations/evolve33.py 2010-02-09 16:54:13 +0000 |
1803 | +++ src/schooltool/generations/evolve33.py 2010-05-28 11:09:37 +0000 |
1804 | @@ -25,13 +25,10 @@ |
1805 | from zope.app.publication.zopepublication import ZopePublication |
1806 | from zope.component.hooks import getSite, setSite |
1807 | from zope.component import getUtility |
1808 | -from zope.catalog.interfaces import ICatalog |
1809 | |
1810 | from schooltool.app.interfaces import ISchoolToolApplication |
1811 | from schooltool.app.interfaces import CatalogSetUpEvent |
1812 | from schooltool.contact.interfaces import IContact |
1813 | -from schooltool.contact.contact import catalogSetUpSubscriber |
1814 | -from schooltool.contact.contact import CONTACT_CATALOG_KEY |
1815 | from schooltool.person.interfaces import IPerson |
1816 | |
1817 | |
1818 | @@ -46,10 +43,6 @@ |
1819 | persons = findObjectsProviding(app, IPerson) |
1820 | for person in persons: |
1821 | contact = IContact(person, None) |
1822 | - # regsiter contact catalogs |
1823 | - catalogSetUpSubscriber(CatalogSetUpEvent(app)) |
1824 | - util = getUtility(ICatalog, CONTACT_CATALOG_KEY) |
1825 | - util.updateIndexes() |
1826 | |
1827 | setSite(old_site) |
1828 | |
1829 | |
1830 | === added file 'src/schooltool/generations/evolve35.py' |
1831 | --- src/schooltool/generations/evolve35.py 1970-01-01 00:00:00 +0000 |
1832 | +++ src/schooltool/generations/evolve35.py 2010-05-28 11:09:37 +0000 |
1833 | @@ -0,0 +1,59 @@ |
1834 | +# |
1835 | +# SchoolTool - common information systems platform for school administration |
1836 | +# Copyright (c) 2010 Shuttleworth Foundation |
1837 | +# |
1838 | +# This program is free software; you can redistribute it and/or modify |
1839 | +# it under the terms of the GNU General Public License as published by |
1840 | +# the Free Software Foundation; either version 2 of the License, or |
1841 | +# (at your option) any later version. |
1842 | +# |
1843 | +# This program is distributed in the hope that it will be useful, |
1844 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1845 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1846 | +# GNU General Public License for more details. |
1847 | +# |
1848 | +# You should have received a copy of the GNU General Public License |
1849 | +# along with this program; if not, write to the Free Software |
1850 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1851 | +# |
1852 | +""" |
1853 | +Upgrade SchoolTool to generation 35. |
1854 | + |
1855 | +Evolution script to unregister old catalog utilities. |
1856 | +""" |
1857 | +from zope.app.generations.utility import findObjectsProviding |
1858 | +from zope.app.publication.zopepublication import ZopePublication |
1859 | +from zope.component import queryUtility |
1860 | +from zope.component.hooks import getSite, setSite |
1861 | +from zope.traversing.api import traverse |
1862 | + |
1863 | +from schooltool.app.interfaces import ISchoolToolApplication |
1864 | +from zope.catalog.interfaces import ICatalog |
1865 | + |
1866 | + |
1867 | +CATALOG_KEYS = [ |
1868 | + 'schooltool.basicperson', |
1869 | + 'schooltool.contact', |
1870 | + 'schooltool.person', |
1871 | + ] |
1872 | + |
1873 | + |
1874 | +def evolve(context): |
1875 | + root = context.connection.root().get(ZopePublication.root_name, None) |
1876 | + |
1877 | + old_site = getSite() |
1878 | + apps = findObjectsProviding(root, ISchoolToolApplication) |
1879 | + for app in apps: |
1880 | + setSite(app) |
1881 | + sm = app.getSiteManager() |
1882 | + default = traverse(app, '++etc++site/default') |
1883 | + |
1884 | + for key in CATALOG_KEYS: |
1885 | + util = queryUtility(ICatalog, name=key, default=None) |
1886 | + if util is None: |
1887 | + continue |
1888 | + name = util.__name__ |
1889 | + sm.unregisterUtility(util, ICatalog, key) |
1890 | + del default[name] |
1891 | + |
1892 | + setSite(old_site) |
1893 | |
1894 | === modified file 'src/schooltool/generations/tests/__init__.py' |
1895 | --- src/schooltool/generations/tests/__init__.py 2010-01-19 12:09:22 +0000 |
1896 | +++ src/schooltool/generations/tests/__init__.py 2010-05-28 11:09:37 +0000 |
1897 | @@ -48,6 +48,7 @@ |
1898 | |
1899 | |
1900 | def catalogSetUp(test): |
1901 | + """This code is deprecated, to be used only by old evolution tests.""" |
1902 | setup.placefulSetUp() |
1903 | setup.setUpTraversal() |
1904 | setUpAnnotations() |
1905 | @@ -91,5 +92,6 @@ |
1906 | [IIntIdRemovedEvent]) |
1907 | |
1908 | def catalogTearDown(test): |
1909 | + """This code is deprecated, to be used only by old evolution tests.""" |
1910 | setup.placefulTearDown() |
1911 | _d.clear() |
1912 | |
1913 | === modified file 'src/schooltool/generations/tests/test_evolve33.py' |
1914 | --- src/schooltool/generations/tests/test_evolve33.py 2010-02-09 19:18:22 +0000 |
1915 | +++ src/schooltool/generations/tests/test_evolve33.py 2010-05-28 11:09:37 +0000 |
1916 | @@ -24,13 +24,12 @@ |
1917 | |
1918 | from zope.app.testing import setup |
1919 | |
1920 | -from zope.component import queryUtility, provideUtility |
1921 | +from zope.component import provideUtility |
1922 | from zope.component import provideAdapter |
1923 | from zope.interface import implements |
1924 | from zope.intid import IntIds |
1925 | from zope.intid.interfaces import IIntIds |
1926 | from zope.site.folder import Folder |
1927 | -from zope.catalog.interfaces import ICatalog |
1928 | from zope.container.btree import BTreeContainer |
1929 | from zope.component.hooks import getSite, setSite |
1930 | |
1931 | @@ -89,28 +88,6 @@ |
1932 | >>> print getSite() |
1933 | None |
1934 | |
1935 | - Contact catalog was registered for ISchoolToolApplication sites. |
1936 | - |
1937 | - >>> catalog = queryUtility(ICatalog, 'schooltool.contact') |
1938 | - >>> print catalog |
1939 | - None |
1940 | - |
1941 | - >>> setSite(app) |
1942 | - |
1943 | - >>> catalog = queryUtility(ICatalog, 'schooltool.contact') |
1944 | - >>> catalog |
1945 | - <zc.catalog.extentcatalog.Catalog object ...> |
1946 | - |
1947 | - Catalog indexes were set up properly. |
1948 | - |
1949 | - >>> sorted(i.__name__ for i in catalog.values()) |
1950 | - [u'first_name', u'form_keys', u'last_name'] |
1951 | - |
1952 | - And contacts of existing persons got indexed. |
1953 | - |
1954 | - >>> sorted(catalog['first_name'].documents_to_values.values()) |
1955 | - ['Johny', 'Petey'] |
1956 | - |
1957 | """ |
1958 | |
1959 | |
1960 | |
1961 | === added file 'src/schooltool/generations/tests/test_evolve35.py' |
1962 | --- src/schooltool/generations/tests/test_evolve35.py 1970-01-01 00:00:00 +0000 |
1963 | +++ src/schooltool/generations/tests/test_evolve35.py 2010-05-28 11:09:37 +0000 |
1964 | @@ -0,0 +1,123 @@ |
1965 | +# |
1966 | +# SchoolTool - common information systems platform for school administration |
1967 | +# Copyright (c) 2010 Shuttleworth Foundation |
1968 | +# |
1969 | +# This program is free software; you can redistribute it and/or modify |
1970 | +# it under the terms of the GNU General Public License as published by |
1971 | +# the Free Software Foundation; either version 2 of the License, or |
1972 | +# (at your option) any later version. |
1973 | +# |
1974 | +# This program is distributed in the hope that it will be useful, |
1975 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1976 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1977 | +# GNU General Public License for more details. |
1978 | +# |
1979 | +# You should have received a copy of the GNU General Public License |
1980 | +# along with this program; if not, write to the Free Software |
1981 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1982 | +# |
1983 | +""" |
1984 | +Unit tests for schooltool.generations.evolve35 |
1985 | +""" |
1986 | +import unittest |
1987 | +import doctest |
1988 | + |
1989 | +from zope.app.testing import setup |
1990 | +from zope.interface import implements |
1991 | +from zope.component import queryUtility |
1992 | +from zope.component.hooks import getSite, setSite |
1993 | +from zope.traversing.api import traverse |
1994 | +from zope.site import LocalSiteManager |
1995 | +from zope.site.folder import Folder |
1996 | +from zope.location import Location |
1997 | +from zope.catalog.interfaces import ICatalog |
1998 | + |
1999 | +from schooltool.app.interfaces import ISchoolToolApplication |
2000 | +from schooltool.generations.tests import ContextStub |
2001 | +from schooltool.generations.evolve35 import CATALOG_KEYS |
2002 | + |
2003 | + |
2004 | +def registerLocalUtility(site, utility, name): |
2005 | + manager = site.getSiteManager() |
2006 | + default = traverse(site, '++etc++site/default') |
2007 | + default['storage.key:'+name] = utility |
2008 | + manager.registerUtility(utility, ICatalog, name) |
2009 | + |
2010 | + |
2011 | +class UtilityStub(Folder): |
2012 | + def __init__(self, name): |
2013 | + super(UtilityStub, self).__init__() |
2014 | + self.name = name |
2015 | + |
2016 | + def __repr__(self): |
2017 | + return '<%s (%s)>' % (self.__class__.__name__, self.name) |
2018 | + |
2019 | + |
2020 | +class AppStub(Folder): |
2021 | + implements(ISchoolToolApplication) |
2022 | + |
2023 | + |
2024 | +def doctest_evolve35(): |
2025 | + """Test evolution to generation 35. |
2026 | + |
2027 | + First, let's build ST app with local catalog utilities. |
2028 | + |
2029 | + >>> context = ContextStub() |
2030 | + >>> context.root_folder['app'] = app = AppStub() |
2031 | + >>> app.setSiteManager(LocalSiteManager(app)) |
2032 | + |
2033 | + >>> for name in CATALOG_KEYS: |
2034 | + ... registerLocalUtility(app, UtilityStub(name), name) |
2035 | + |
2036 | + >>> setSite(app) |
2037 | + >>> for name in CATALOG_KEYS: |
2038 | + ... print queryUtility(ICatalog, name=name, default=None) |
2039 | + <UtilityStub (schooltool.basicperson)> |
2040 | + <UtilityStub (schooltool.contact)> |
2041 | + <UtilityStub (schooltool.person)> |
2042 | + |
2043 | + Set the site to something else and evolve. |
2044 | + |
2045 | + >>> context.root_folder['frob'] = frob = Folder() |
2046 | + >>> frob.setSiteManager(LocalSiteManager(frob)) |
2047 | + >>> setSite(frob) |
2048 | + |
2049 | + >>> from schooltool.generations.evolve35 import evolve |
2050 | + >>> evolve(context) |
2051 | + |
2052 | + Active site was kept. |
2053 | + |
2054 | + >>> getSite() is frob |
2055 | + True |
2056 | + |
2057 | + And catalogs in our app were unregistered. |
2058 | + |
2059 | + >>> setSite(app) |
2060 | + >>> for name in CATALOG_KEYS: |
2061 | + ... print queryUtility(ICatalog, name=name, default=None) |
2062 | + None |
2063 | + None |
2064 | + None |
2065 | + |
2066 | + """ |
2067 | + |
2068 | + |
2069 | +def setUp(test): |
2070 | + setup.placefulSetUp() |
2071 | + setup.setUpTraversal() |
2072 | + |
2073 | + |
2074 | +def tearDown(test): |
2075 | + setup.placefulTearDown() |
2076 | + |
2077 | + |
2078 | +def test_suite(): |
2079 | + optionflags = (doctest.ELLIPSIS | |
2080 | + doctest.NORMALIZE_WHITESPACE | |
2081 | + doctest.REPORT_ONLY_FIRST_FAILURE) |
2082 | + return doctest.DocTestSuite(setUp=setUp, tearDown=tearDown, |
2083 | + optionflags=optionflags) |
2084 | + |
2085 | + |
2086 | +if __name__ == '__main__': |
2087 | + unittest.main(defaultTest='test_suite') |
2088 | |
2089 | === modified file 'src/schooltool/person/configure.zcml' |
2090 | --- src/schooltool/person/configure.zcml 2010-01-19 12:09:22 +0000 |
2091 | +++ src/schooltool/person/configure.zcml 2010-05-28 11:09:37 +0000 |
2092 | @@ -91,14 +91,12 @@ |
2093 | provides=".interfaces.IPerson" |
2094 | for="schooltool.person.interfaces.IPersonPreferences" /> |
2095 | |
2096 | - <subscriber |
2097 | - for="schooltool.app.interfaces.ICatalogSetUpEvent" |
2098 | - handler=".person.catalogSetUpSubscriber" |
2099 | - /> |
2100 | + <adapter factory=".person.PersonCatalog" |
2101 | + name="schooltool.person.person.PersonCatalog" /> |
2102 | |
2103 | <adapter |
2104 | - for=".interfaces.IPersonContainer" |
2105 | - factory=".person.getPersonContainerCatalog" |
2106 | + for="schooltool.person.interfaces.IPersonContainer" |
2107 | + factory=".person.getPersonCatalog" |
2108 | provides="zope.catalog.interfaces.ICatalog" /> |
2109 | |
2110 | <configure |
2111 | |
2112 | === modified file 'src/schooltool/person/person.py' |
2113 | --- src/schooltool/person/person.py 2010-01-27 17:38:36 +0000 |
2114 | +++ src/schooltool/person/person.py 2010-05-28 11:09:37 +0000 |
2115 | @@ -30,16 +30,13 @@ |
2116 | from zope.container import btree |
2117 | from zope.container.contained import Contained |
2118 | from zope.component import adapts |
2119 | -from zope.catalog.interfaces import ICatalog |
2120 | -from zope.catalog.catalog import Catalog |
2121 | from zope.component import getUtility |
2122 | |
2123 | -from zc.catalog.catalogindex import ValueIndex |
2124 | - |
2125 | -from schooltool.app.app import getSchoolToolApplication |
2126 | +from schooltool.app.interfaces import ISchoolToolApplication |
2127 | from schooltool.app.interfaces import ISchoolToolCalendar |
2128 | from schooltool.app.membership import URIMembership, URIMember, URIGroup |
2129 | from schooltool.app.overlay import OverlaidCalendarsProperty |
2130 | +from schooltool.app.catalog import AttributeCatalog |
2131 | from schooltool.person import interfaces |
2132 | from schooltool.relationship import RelationshipProperty |
2133 | from schooltool.securitypolicy.crowds import Crowd |
2134 | @@ -51,8 +48,6 @@ |
2135 | from schooltool.securitypolicy.crowds import OwnerCrowd, AggregateCrowd |
2136 | from schooltool.utility.utility import UtilitySetUp |
2137 | |
2138 | -PERSON_CATALOG_KEY = 'schooltool.person' |
2139 | - |
2140 | |
2141 | class PersonContainer(btree.BTreeContainer): |
2142 | """Container of persons.""" |
2143 | @@ -136,16 +131,15 @@ |
2144 | def personAppCalendarOverlaySubscriber(person, event): |
2145 | """Add application calendar to overlays of all new persons. |
2146 | """ |
2147 | - try: |
2148 | - app = getSchoolToolApplication() |
2149 | - person.overlaid_calendars.add(ISchoolToolCalendar(app)) |
2150 | - except ValueError: |
2151 | + app = ISchoolToolApplication(None, None) |
2152 | + if app is None: |
2153 | # If we get this we are probably in the initial new-site setup |
2154 | # or creating a new manager during startup. This should be |
2155 | # safe to ignore since it will happen very infrequently |
2156 | # (perhaps only once) and the manager can easily add the site |
2157 | # calendar to his/her overlay in the overlay selection view. |
2158 | - pass |
2159 | + return |
2160 | + person.overlaid_calendars.add(ISchoolToolCalendar(app)) |
2161 | |
2162 | |
2163 | from schooltool.app.app import InitBase |
2164 | @@ -208,14 +202,10 @@ |
2165 | OwnerCrowd(self.context.person).contains(principal)) |
2166 | |
2167 | |
2168 | -def catalogSetUp(catalog): |
2169 | - catalog['__name__'] = ValueIndex('__name__', IPerson) |
2170 | - catalog['title'] = ValueIndex('title', IPerson) |
2171 | - |
2172 | - |
2173 | -catalogSetUpSubscriber = UtilitySetUp( |
2174 | - Catalog, ICatalog, PERSON_CATALOG_KEY, setUp=catalogSetUp) |
2175 | - |
2176 | - |
2177 | -def getPersonContainerCatalog(container): |
2178 | - return getUtility(ICatalog, PERSON_CATALOG_KEY) |
2179 | +class PersonCatalog(AttributeCatalog): |
2180 | + |
2181 | + version = '1 - replaced catalog utility' |
2182 | + interface = IPerson |
2183 | + attributes = ('__name__', 'title') |
2184 | + |
2185 | +getPersonCatalog = PersonCatalog.get |
2186 | |
2187 | === modified file 'src/schooltool/resource/types.py' |
2188 | --- src/schooltool/resource/types.py 2007-10-15 16:20:41 +0000 |
2189 | +++ src/schooltool/resource/types.py 2010-05-28 11:09:37 +0000 |
2190 | @@ -29,8 +29,8 @@ |
2191 | from zc.table.column import GetterColumn |
2192 | from zc.table.interfaces import ISortableColumn |
2193 | |
2194 | +from schooltool.app.interfaces import ISchoolToolApplication |
2195 | from schooltool.common import SchoolToolMessage as _ |
2196 | -from schooltool.app.app import getSchoolToolApplication |
2197 | from schooltool.resource.interfaces import (IResource, IResourceFactoryUtility, |
2198 | ILocation, IEquipment, |
2199 | IResourceTypeInformation, |
2200 | @@ -45,7 +45,7 @@ |
2201 | self.interface = IResource |
2202 | |
2203 | def types(self): |
2204 | - app = getSchoolToolApplication() |
2205 | + app = ISchoolToolApplication(None) |
2206 | types = set() |
2207 | for resource in app['resources'].values(): |
2208 | if self.interface.providedBy(resource): |
2209 | |
2210 | === modified file 'src/schooltool/skin/skin.py' |
2211 | --- src/schooltool/skin/skin.py 2010-01-08 14:03:27 +0000 |
2212 | +++ src/schooltool/skin/skin.py 2010-05-28 11:09:37 +0000 |
2213 | @@ -43,8 +43,6 @@ |
2214 | from schooltool.app.interfaces import ISchoolToolApplication |
2215 | from schooltool.skin.interfaces import IBreadcrumbInfo |
2216 | |
2217 | -from schooltool.app.app import getSchoolToolApplication |
2218 | - |
2219 | |
2220 | class IJavaScriptManager(IViewletManager): |
2221 | """Provides a viewlet hook for the javascript link entries.""" |
2222 | @@ -141,7 +139,7 @@ |
2223 | return ISchoolToolApplication(None) |
2224 | |
2225 | def appURL(self): |
2226 | - return absoluteURL(getSchoolToolApplication(), self.request) |
2227 | + return absoluteURL(ISchoolToolApplication(None), self.request) |
2228 | |
2229 | |
2230 | class TopLevelContainerNavigationViewlet(NavigationViewlet): |
2231 | |
2232 | === modified file 'src/schooltool/term/browser/emergency.py' |
2233 | --- src/schooltool/term/browser/emergency.py 2008-09-26 17:35:45 +0000 |
2234 | +++ src/schooltool/term/browser/emergency.py 2010-05-28 11:09:37 +0000 |
2235 | @@ -32,8 +32,8 @@ |
2236 | |
2237 | from schooltool.schoolyear.interfaces import ISchoolYear |
2238 | from schooltool.common import SchoolToolMessage as _ |
2239 | -from schooltool.app.app import getSchoolToolApplication |
2240 | from schooltool.app.cal import CalendarEvent |
2241 | +from schooltool.app.interfaces import ISchoolToolApplication |
2242 | from schooltool.app.interfaces import ISchoolToolCalendar |
2243 | from schooltool.calendar.utils import parse_date |
2244 | from schooltool.term.interfaces import ITerm |
2245 | @@ -122,7 +122,7 @@ |
2246 | exceptionDayIds[self.replacement] = removeSecurityProxy(day_id) |
2247 | |
2248 | # Post calendar events to schoolwide calendar |
2249 | - calendar = ISchoolToolCalendar(getSchoolToolApplication()) |
2250 | + calendar = ISchoolToolCalendar(ISchoolToolApplication(None)) |
2251 | dtstart = datetime.datetime.combine(self.date, datetime.time()) |
2252 | msg = _('School cancelled due to emergency.' |
2253 | ' Replacement day $replacement.', |
2254 | |
2255 | === modified file 'src/schooltool/timetable/__init__.py' |
2256 | --- src/schooltool/timetable/__init__.py 2010-01-19 12:09:22 +0000 |
2257 | +++ src/schooltool/timetable/__init__.py 2010-05-28 11:09:37 +0000 |
2258 | @@ -135,7 +135,6 @@ |
2259 | from zope.interface import implementer |
2260 | from zope.interface import implements |
2261 | from zope.interface import directlyProvides |
2262 | - |
2263 | from zope.annotation.interfaces import IAnnotations |
2264 | from zope.location.interfaces import ILocation |
2265 | from zope.traversing.api import getPath |
2266 | @@ -143,10 +142,9 @@ |
2267 | from zope.container.contained import NameChooser |
2268 | from zope.app.generations.utility import findObjectsProviding |
2269 | |
2270 | +from schooltool.app.interfaces import ISchoolToolApplication |
2271 | +from schooltool.app.interfaces import ISchoolToolCalendar |
2272 | from schooltool.calendar.simple import ImmutableCalendar |
2273 | - |
2274 | -from schooltool.app.interfaces import ISchoolToolCalendar |
2275 | - |
2276 | from schooltool.timetable.interfaces import ITimetableCalendarEvent |
2277 | from schooltool.timetable.interfaces import ITimetable, ITimetableWrite |
2278 | from schooltool.timetable.interfaces import ITimetableDay, ITimetableDayWrite |
2279 | @@ -164,7 +162,6 @@ |
2280 | from schooltool.timetable.interfaces import ITimetableSchema |
2281 | from schooltool.timetable.interfaces import Unchanged |
2282 | from schooltool.term.interfaces import ITerm |
2283 | -from schooltool.app.app import getSchoolToolApplication |
2284 | from schooltool.app.app import InitBase |
2285 | |
2286 | from schooltool.common import SchoolToolMessage as _ |
2287 | @@ -673,7 +670,7 @@ |
2288 | |
2289 | |
2290 | def getAllTimetables(): |
2291 | - app = getSchoolToolApplication(None) |
2292 | + app = ISchoolToolApplication(None) |
2293 | all_timetables = [] |
2294 | for ttowner in findObjectsProviding(app, IOwnTimetables): |
2295 | timetables = queryAdapter(ttowner, ITimetables, default=None) |
2296 | |
2297 | === modified file 'src/schooltool/timetable/browser/__init__.py' |
2298 | --- src/schooltool/timetable/browser/__init__.py 2010-05-03 11:24:06 +0000 |
2299 | +++ src/schooltool/timetable/browser/__init__.py 2010-05-28 11:09:37 +0000 |
2300 | @@ -32,7 +32,6 @@ |
2301 | from zope.traversing.browser.absoluteurl import absoluteURL |
2302 | |
2303 | from schooltool.app.interfaces import ISchoolToolApplication |
2304 | -from schooltool.app.app import getSchoolToolApplication |
2305 | from schooltool.calendar.utils import parse_date, parse_time |
2306 | from schooltool.course.interfaces import ISection |
2307 | from schooltool.term.interfaces import ITerm |
2308 | @@ -369,7 +368,7 @@ |
2309 | |
2310 | If there are no timetable schemas, None is returned. |
2311 | """ |
2312 | - app = getSchoolToolApplication() |
2313 | + app = ISchoolToolApplication(None) |
2314 | ttschemas = ITimetableSchemaContainer(app, None) |
2315 | if ttschemas is None: |
2316 | return None |
2317 | |
2318 | === modified file 'src/schooltool/utility/utility.py' |
2319 | --- src/schooltool/utility/utility.py 2009-11-19 02:00:46 +0000 |
2320 | +++ src/schooltool/utility/utility.py 2010-05-28 11:09:37 +0000 |
2321 | @@ -61,7 +61,8 @@ |
2322 | if spec.override: |
2323 | # override existing utility |
2324 | name = local_utility.__name__ |
2325 | - manager.unregisterUtility(local_utility, spec.iface, name) |
2326 | + manager.unregisterUtility( |
2327 | + local_utility, spec.iface, spec.utility_name) |
2328 | del default[name] |
2329 | else: |
2330 | # do not register this utility; we already got it |