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

Last change on this file since 4968 was 4920, checked in by uli, 15 years ago

Make unit tests run again with the new package layout.

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