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

Last change on this file since 4997 was 4993, checked in by Henrik Bettermann, 15 years ago

Remove category attribute.
max_pass and application_category must not be required.

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