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

Last change on this file since 5949 was 5949, checked in by Henrik Bettermann, 13 years ago

The implementation of a workflow for certificates is an interesting case study and should thus be kept in the source code as comment. We don't need a workflow in the academics section.

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