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

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

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

File size: 12.1 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
[5140]8.. :doctest:
9.. :layer: waeup.sirp.testing.WAeUPSIRPUnitTestLayer
[4290]10
[4312]11
[4313]12Content Classes (models and containers)
13=======================================
[4290]14
[4322]15
[4313]16:class:`Certificate`
17--------------------
[4290]18
[4993]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'']]]]]]]])
[4290]20
[4313]21   Create a certificate object with the given parameters.
[4312]22
[4313]23   .. attribute:: grok.implements(ICertificate)
[4290]24
[4313]25   All parameters are optional:
[4290]26
[4920]27     >>> from waeup.sirp.university.certificate import Certificate
[4313]28     >>> mycertificate = Certificate()
[4290]29
[4313]30   Certificates have the attributes required by the `ICertificate` interface:
[4290]31
[5005]32     >>> from waeup.sirp.university.interfaces import ICertificate
[4313]33     >>> ICertificate.providedBy(mycertificate)
34     True
[4290]35
[4313]36     >>> from zope.interface.verify import verifyObject
37     >>> verifyObject(ICertificate, mycertificate)
38     True
[4290]39
[4322]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
[4313]44   .. attribute:: title
[4290]45
[4313]46      Each certificate has a title:
[4290]47
[4313]48        >>> mycertificate.title
49        u'Unnamed Certificate'
[4290]50
[4313]51   .. attribute:: code
[4290]52
[4313]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):
[4312]56
[4313]57        >>> mycertificate.code
58        u'NA'
[4312]59
[4313]60   .. attribute:: review_state
[4312]61
[4313]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``:
[4312]66
[4313]67        >>> mycertificate.review_state
68        'unchecked'
[4312]69
[4313]70      .. seealso::
71         :meth:`Certificate.check` -- mark a certificate as ``checked``
[4312]72
73
[4313]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
[4317]116        >>> mycertificate.max_pass
117        u''
[4313]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
[4760]135        >>> mycertificate.review_state = 'unchecked'
[4313]136        Traceback (most recent call last):
137        ...
[4760]138        NoTransitionAvailableError
[4313]139
[4760]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
[4322]149   .. method:: addCourseRef(course[, level=100,[ core_or_elective=True]])
[4313]150
[4322]151      Add a reference to a course. A course is an object implementing
[4920]152      :class:`waeup.sirp.interfaces.ICourse`.
[4322]153
154      Please don't be confused by the term 'reference'. This just
[4920]155      means an ordinary :class:`waeup.sirp.university.course.Course` object
[4322]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
[4313]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
[4920]214     <waeup.sirp.university.certificate.Certificate object at 0x...>
[4313]215
[4384]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
[4920]221     >>> from waeup.sirp.university.certificate import CertificateFactory
[4384]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()
[4920]230     <implementedBy waeup.sirp.university.certificate.Certificate>
[4384]231
232
[4386]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
[4920]248     <waeup.sirp.university.certificate.CertificateCourse object at 0x...>
[4386]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
[4920]255     >>> from waeup.sirp.university.certificate import CertificateCourseFactory
[4386]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()
[4920]264     <implementedBy waeup.sirp.university.certificate.CertificateCourse>
[4386]265
266
[4332]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
[4313]291Examples
292========
293
[4322]294Certificates
295------------
[4313]296
297We can create certificates:
298
[4920]299    >>> from waeup.sirp.university.certificate import Certificate
[4313]300    >>> mycertificate = Certificate()
301    >>> mycertificate
[4920]302    <waeup.sirp.university.certificate.Certificate object at 0x...>
[4313]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
[4920]311    <waeup.sirp.university.certificate.Certificate object at 0x...>
[4313]312
[4322]313CertificateCourses
314------------------
315
316:class:`CertificateCourse` instances comply with the
317:class:`ICertificateCourse` interface:
318
[5005]319    >>> from waeup.sirp.university.interfaces import ICertificateCourse
[4920]320    >>> from waeup.sirp.university.certificate import CertificateCourse
[4322]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
[4332]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
[4920]335    <waeup.sirp.university.certificate.CertificateCourse object at 0x...>
[4332]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']
[4489]413    >>> certcourse = newdbroot['mydept'].certificates['MYCERT']['MYCOURSE_100']
[4332]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.