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

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

Fix references to academics stuff interfaces. This is the first step to make academics stuff pluggable.

File size: 12.3 KB
RevLine 
[4920]1:mod:`waeup.sirp.university.certificate` -- Certificates for WAeUP
2******************************************************************
[4290]3
[4920]4.. module:: waeup.sirp.university.certificate
[4325]5
[4313]6Components that represent and manage certificates.
7
[4290]8:Test-Layer: unit
9
[4312]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
[4290]17
[4313]18Content Classes (models and containers)
19=======================================
[4290]20
[4322]21
[4313]22:class:`Certificate`
23--------------------
[4290]24
[4993]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'']]]]]]]])
[4290]26
[4313]27   Create a certificate object with the given parameters.
[4312]28
[4313]29   .. attribute:: grok.implements(ICertificate)
[4290]30
[4313]31   All parameters are optional:
[4290]32
[4920]33     >>> from waeup.sirp.university.certificate import Certificate
[4313]34     >>> mycertificate = Certificate()
[4290]35
[4313]36   Certificates have the attributes required by the `ICertificate` interface:
[4290]37
[5005]38     >>> from waeup.sirp.university.interfaces import ICertificate
[4313]39     >>> ICertificate.providedBy(mycertificate)
40     True
[4290]41
[4313]42     >>> from zope.interface.verify import verifyObject
43     >>> verifyObject(ICertificate, mycertificate)
44     True
[4290]45
[4322]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
[4313]50   .. attribute:: title
[4290]51
[4313]52      Each certificate has a title:
[4290]53
[4313]54        >>> mycertificate.title
55        u'Unnamed Certificate'
[4290]56
[4313]57   .. attribute:: code
[4290]58
[4313]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):
[4312]62
[4313]63        >>> mycertificate.code
64        u'NA'
[4312]65
[4313]66   .. attribute:: review_state
[4312]67
[4313]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``:
[4312]72
[4313]73        >>> mycertificate.review_state
74        'unchecked'
[4312]75
[4313]76      .. seealso::
77         :meth:`Certificate.check` -- mark a certificate as ``checked``
[4312]78
79
[4313]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
[4317]122        >>> mycertificate.max_pass
123        u''
[4313]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
[4760]141        >>> mycertificate.review_state = 'unchecked'
[4313]142        Traceback (most recent call last):
143        ...
[4760]144        NoTransitionAvailableError
[4313]145
[4760]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
[4322]155   .. method:: addCourseRef(course[, level=100,[ core_or_elective=True]])
[4313]156
[4322]157      Add a reference to a course. A course is an object implementing
[4920]158      :class:`waeup.sirp.interfaces.ICourse`.
[4322]159
160      Please don't be confused by the term 'reference'. This just
[4920]161      means an ordinary :class:`waeup.sirp.university.course.Course` object
[4322]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
[4313]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
[4920]220     <waeup.sirp.university.certificate.Certificate object at 0x...>
[4313]221
[4384]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
[4920]227     >>> from waeup.sirp.university.certificate import CertificateFactory
[4384]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()
[4920]236     <implementedBy waeup.sirp.university.certificate.Certificate>
[4384]237
238
[4386]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
[4920]254     <waeup.sirp.university.certificate.CertificateCourse object at 0x...>
[4386]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
[4920]261     >>> from waeup.sirp.university.certificate import CertificateCourseFactory
[4386]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()
[4920]270     <implementedBy waeup.sirp.university.certificate.CertificateCourse>
[4386]271
272
[4332]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
[4313]297Examples
298========
299
[4322]300Certificates
301------------
[4313]302
303We can create certificates:
304
[4920]305    >>> from waeup.sirp.university.certificate import Certificate
[4313]306    >>> mycertificate = Certificate()
307    >>> mycertificate
[4920]308    <waeup.sirp.university.certificate.Certificate object at 0x...>
[4313]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
[4920]317    <waeup.sirp.university.certificate.Certificate object at 0x...>
[4313]318
[4322]319CertificateCourses
320------------------
321
322:class:`CertificateCourse` instances comply with the
323:class:`ICertificateCourse` interface:
324
[5005]325    >>> from waeup.sirp.university.interfaces import ICertificateCourse
[4920]326    >>> from waeup.sirp.university.certificate import CertificateCourse
[4322]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
[4332]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
[4920]341    <waeup.sirp.university.certificate.CertificateCourse object at 0x...>
[4332]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']
[4489]419    >>> certcourse = newdbroot['mydept'].certificates['MYCERT']['MYCOURSE_100']
[4332]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.