source: main/waeup.sirp/trunk/src/waeup/sirp/university/certificate.txt @ 5920

Last change on this file since 5920 was 5140, checked in by uli, 14 years ago

Update all unit tests that use the ZCA to run inside the new unit test layer.

File size: 12.1 KB
Line 
1:mod:`waeup.sirp.university.certificate` -- Certificates for WAeUP
2******************************************************************
3
4.. module:: waeup.sirp.university.certificate
5
6Components that represent and manage certificates.
7
8.. :doctest:
9.. :layer: waeup.sirp.testing.WAeUPSIRPUnitTestLayer
10
11
12Content Classes (models and containers)
13=======================================
14
15
16:class:`Certificate`
17--------------------
18
19.. class:: Certificate([code=u'NA',[ title=u'Unnamed Certificate',[ study_mode=None,[ start_level=None,[ end_level=None,[ application_category=None,[ m_prefix=u'',[  max_pass = u'']]]]]]]])
20
21   Create a certificate object with the given parameters.
22
23   .. attribute:: grok.implements(ICertificate)
24
25   All parameters are optional:
26
27     >>> from waeup.sirp.university.certificate import Certificate
28     >>> mycertificate = Certificate()
29
30   Certificates have the attributes required by the `ICertificate` interface:
31
32     >>> from waeup.sirp.university.interfaces import ICertificate
33     >>> ICertificate.providedBy(mycertificate)
34     True
35
36     >>> from zope.interface.verify import verifyObject
37     >>> verifyObject(ICertificate, mycertificate)
38     True
39
40   Beside the attributes, certificates are containers for
41   certificate-courses (see :class:`CertificateCourse`). Each
42   certificate course can be accessed by the code of the course it wraps.
43
44   .. attribute:: title
45
46      Each certificate has a title:
47
48        >>> mycertificate.title
49        u'Unnamed Certificate'
50
51   .. attribute:: code
52
53      Each certificate holds a code, which might be a shortcut or
54      abbreviation of the real certificate name. By default the code
55      is ``NA`` (=not assigned):
56
57        >>> mycertificate.code
58        u'NA'
59
60   .. attribute:: review_state
61
62      The review state can have one of the ``checking`` states defined
63      in the WAeUP workflow. These are at least the states ``checked``
64      and ``unchecked``. After a certificate is created, the review
65      state is ``unchecked``:
66
67        >>> mycertificate.review_state
68        'unchecked'
69
70      .. seealso::
71         :meth:`Certificate.check` -- mark a certificate as ``checked``
72
73
74   .. attribute:: study_mode
75
76      Each :class:`Certificate` instance has a study mode:
77
78        >>> print mycertificate.study_mode
79        None
80
81      .. XXX: This is not a proper description
82
83   .. attribute:: start_level
84
85      Each :class:`Certificate` instance has a start level:
86
87        >>> print mycertificate.start_level
88        None
89
90      .. XXX: This is not a proper description
91
92   .. attribute:: end_level
93
94      Each :class:`Certificate` instance has a end level:
95
96        >>> print mycertificate.end_level
97        None
98
99      .. XXX: This is not a proper description
100   
101
102   .. attribute:: application_category
103
104      Each :class:`Certificate` instance has an application category:
105
106        >>> print mycertificate.application_category
107        None
108
109      .. XXX: This is not a proper description
110
111   
112   .. attribute:: max_pass
113
114      Each :class:`Certificate` instance has a maximum number of passes:
115
116        >>> mycertificate.max_pass
117        u''
118
119      .. XXX: This is not a proper description
120
121
122   .. method:: check()
123
124      Mark a certificate instance's review state as ``checked``:
125
126        >>> mycertificate.review_state
127        'unchecked'
128
129        >>> mycertificate.check()
130        >>> mycertificate.review_state
131        'checked'
132
133      We cannot uncheck a certificate:
134
135        >>> mycertificate.review_state = 'unchecked'
136        Traceback (most recent call last):
137        ...
138        NoTransitionAvailableError
139
140      The only states accepted at all are ``checked`` and
141      ``unchecked``. If we want to set something else this won't work:
142
143        >>> mycertificate.review_state = 'nonsense'
144        Traceback (most recent call last):
145        ...
146        NoTransitionAvailableError
147
148
149   .. method:: addCourseRef(course[, level=100,[ core_or_elective=True]])
150
151      Add a reference to a course. A course is an object implementing
152      :class:`waeup.sirp.interfaces.ICourse`.
153
154      Please don't be confused by the term 'reference'. This just
155      means an ordinary :class:`waeup.sirp.university.course.Course` object
156      in almost all cases. As this object will normaly be one stored
157      in a department, the course here will simply become a reference
158      to the 'real' one in the department container.
159
160   .. method:: delCourseRef(code)
161
162      Remove a course from a certificate.
163
164      The course must be given by its code number.
165
166:class:`CertificateCourse`
167--------------------------
168
169.. class:: CertificateCourse(course[, level=100[, core_or_elective=True]])
170
171   Create a certificate course.
172
173   A certificate-course is a course (:class:`Course`) which is part of
174   a certificate. Normally, certificate-courses are held in
175   certificates and refer to an existing :class:`Course` instance held
176   elsewhere.
177
178   A certificate can require several courses and one
179   course can be required by several certificates.
180
181   .. attribute:: course
182
183      An instance of :class:`ICourse`.
184
185   .. attribute:: level
186
187      An integer telling the level to which this course applies.
188
189   .. attribute:: core_or_elective
190
191      A bool stating whether this course is required or optional to
192      get the certificate.
193
194
195
196Utilities
197=========
198
199:class:`CertificateFactory`
200---------------------------
201
202.. class:: CertificateFactory()
203
204   .. attribute:: grok.name(u'waeup.Certificate')
205
206   .. attribute:: grok.implements(IFactory)
207
208   A named utility to deliver new instances of :class:`Certificate`
209   without the need to import the implementation before:
210
211     >>> from zope.component import createObject
212     >>> mycertificate = createObject(u'waeup.Certificate')
213     >>> mycertificate
214     <waeup.sirp.university.certificate.Certificate object at 0x...>
215
216   The factory complies with the specifications from the
217   :class:`IFactory` insterface:
218
219     >>> from zope.interface.verify import verifyClass
220     >>> from zope.component.interfaces import IFactory
221     >>> from waeup.sirp.university.certificate import CertificateFactory
222     >>> verifyClass(IFactory, CertificateFactory)
223     True
224
225   This means also, that we can get the interfaces of the created
226   object from the factory:
227
228     >>> certificate_factory = CertificateFactory()
229     >>> certificate_factory.getInterfaces()
230     <implementedBy waeup.sirp.university.certificate.Certificate>
231
232
233:class:`CertificateCourseFactory`
234---------------------------------
235
236.. class:: CertificateCourseFactory()
237
238   .. attribute:: grok.name(u'waeup.CertificateCourse')
239
240   .. attribute:: grok.implements(IFactory)
241
242   A named utility to deliver new instances of :class:`CertificateCourse`
243   without the need to import the implementation before:
244
245     >>> from zope.component import createObject
246     >>> mycertificatecourse = createObject(u'waeup.CertificateCourse')
247     >>> mycertificatecourse
248     <waeup.sirp.university.certificate.CertificateCourse object at 0x...>
249
250   The factory complies with the specifications from the
251   :class:`IFactory` insterface:
252
253     >>> from zope.interface.verify import verifyClass
254     >>> from zope.component.interfaces import IFactory
255     >>> from waeup.sirp.university.certificate import CertificateCourseFactory
256     >>> verifyClass(IFactory, CertificateCourseFactory)
257     True
258
259   This means also, that we can get the interfaces of the created
260   object from the factory:
261
262     >>> certcourse_factory = CertificateCourseFactory()
263     >>> certcourse_factory.getInterfaces()
264     <implementedBy waeup.sirp.university.certificate.CertificateCourse>
265
266
267Event Subscribers
268=================
269
270.. function:: removedCourseHandler(course, event)
271
272   An event subscriber triggered for
273   :class:`grok.IObjectRemovedEvent`s, when an :class:`ICourse`
274   instance is removed from a container.
275
276   Tries to remove all referencing :class:`CertificateCourse`
277   instances that reference the removed course.
278
279   To accomplish that, the parents of the removed course are looked up
280   for a certifcate container which contains a certificate-course that
281   contains a reference to the deleted course.
282
283   .. seealso:: :ref:`removecertificatecourses`
284
285   **handles:**
286     :class:`ICourse`
287
288   **event type:**
289     :class:`grok.IObjectRemovedEvent`
290
291Examples
292========
293
294Certificates
295------------
296
297We can create certificates:
298
299    >>> from waeup.sirp.university.certificate import Certificate
300    >>> mycertificate = Certificate()
301    >>> mycertificate
302    <waeup.sirp.university.certificate.Certificate object at 0x...>
303
304Another way to create certificates is by asking for a factory called
305``waeup.Certificate``. This way we can create a factory without
306importing a class:
307
308    >>> from zope.component import createObject
309    >>> mycertificate = createObject(u'waeup.Certificate')
310    >>> mycertificate
311    <waeup.sirp.university.certificate.Certificate object at 0x...>
312
313CertificateCourses
314------------------
315
316:class:`CertificateCourse` instances comply with the
317:class:`ICertificateCourse` interface:
318
319    >>> from waeup.sirp.university.interfaces import ICertificateCourse
320    >>> from waeup.sirp.university.certificate import CertificateCourse
321    >>> mycertcourse = CertificateCourse(None, 200, False)
322    >>> ICertificateCourse.providedBy(mycertcourse)
323    True
324
325    >>> from zope.interface.verify import verifyObject
326    >>> verifyObject(ICertificateCourse, mycertcourse)
327    True
328
329Also instances of :class:`CertificateCourse` can be created by asking
330the component architechture:
331
332    >>> from zope.component import createObject
333    >>> mycertcourse = createObject(u'waeup.CertificateCourse')
334    >>> mycertcourse
335    <waeup.sirp.university.certificate.CertificateCourse object at 0x...>
336
337.. _removecertificatecourses:
338
339Persistence of certificate courses
340----------------------------------
341
342If a certificate course requires a certain course and this is course
343is deleted, also the referencing certificate course is deleted.
344
345We setup a data structure that reflects typical usage. It looks like
346this::
347
348    Department-Instance
349    |
350    +---> courses
351    |        |
352    |        +--------------------> Course-Instance
353    |                                        ^
354    +---> certificates                       |
355             |                               |
356             +-----> Certificate-Instance    |
357                        |                    |
358                        +------> Certificate-Course
359
360The certifcate-Course here refers to a Course-Instance.
361
362In Python we build such a structure like this (from top to bottom):
363
364    >>> from zope.component import createObject
365    >>> mydept = createObject('waeup.Department')
366
367In real world use this data will be stored in a ZODB. We setup our own
368litte ZODB backend (which is easy!):
369
370    >>> from ZODB import FileStorage, DB
371    >>> dbpath = 'tinyData.fs'
372    >>> class TinyZODB(object):
373    ...   def __init__(self, path=dbpath):
374    ...     self.storage = FileStorage.FileStorage(path)
375    ...     self.db = DB(self.storage)
376    ...     self.connection = self.db.open()
377    ...     self.dbroot = self.connection.root()
378    ...   def close(self):
379    ...     self.connection.close()
380    ...     self.db.close()
381    ...     self.storage.close()
382
383Now we can use this ZODB as backend database and store our data
384structure:
385
386    >>> import transaction
387    >>> db = TinyZODB()
388    >>> dbroot = db.dbroot
389    >>> dbroot['mydept'] = mydept
390    >>> mycourse = createObject('waeup.Course')
391    >>> mycourse.code = 'MYCOURSE'
392    >>> mydept.courses.addCourse(mycourse)
393    >>> mycert = createObject('waeup.Certificate')
394    >>> mycert.code = 'MYCERT'
395    >>> mydept.certificates.addCertificate(mycert)
396    >>> mycert.addCourseRef(mycourse)
397
398    >>> transaction.commit()
399
400The data is now stored in the ZODB. We can close the DB, reopen it
401later and the data will still be there:
402
403    >>> db.close()
404    >>> newdb = TinyZODB()
405    >>> newdbroot = newdb.dbroot
406    >>> list(newdbroot)
407    ['mydept']
408
409The certificate-course we stored in the certificate is indeed a
410reference to the course, not a copy of it:
411
412    >>> course = newdbroot['mydept'].courses['MYCOURSE']
413    >>> certcourse = newdbroot['mydept'].certificates['MYCERT']['MYCOURSE_100']
414    >>> certcourse.course is course
415    True
416
417So, we can be sure that modifications to the course are immediately
418reflected in the certcourse.
419
Note: See TracBrowser for help on using the repository browser.