source: waeup/branches/ulif-rewrite/src/waeup/university/certificate.txt @ 4489

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

Remove tests for which we need a functional setup (now covered in
detail in courserefs.txt).

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