source: main/waeup.sirp/trunk/src/waeup/sirp/interfaces.py @ 6991

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

Move registration_state definitions from students/workflow.py to interfaces.py.

  • Property svn:eol-style set to native
File size: 16.5 KB
RevLine 
[3521]1##
2## interfaces.py
[6361]3import os
[6915]4from datetime import datetime
[6353]5from hurry.workflow.interfaces import IWorkflow, IWorkflowInfo
[4789]6from zc.sourcefactory.basic import BasicSourceFactory
[6147]7from zope import schema
[4789]8from zope.component import getUtility
[4882]9from zope.component.interfaces import IObjectEvent
[5955]10from zope.interface import Interface, Attribute, implements
[4789]11from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
[3521]12
[6990]13CREATED = 'created'
14ADMITTED = 'admitted'
15CLEARANCE = 'clearance started'
16REQUESTED = 'clearance requested'
17CLEARED = 'cleared'
18PAID = 'school fee paid'
19RETURNING = 'returning'
20REGISTERED = 'courses registered'
21VALIDATED = 'courses validated'
22
[6361]23default_frontpage = u'' + open(os.path.join(
24        os.path.dirname(__file__), 'frontpage.rst'), 'rb').read()
25
[6915]26def SimpleWAeUPVocabulary(*terms):
27    """A well-buildt vocabulary provides terms with a value, token and
28       title for each term
29    """
30    return SimpleVocabulary([
31            SimpleTerm(value, value, title) for title, value in terms])
32
33def year_range():
34    curr_year = datetime.now().year
35    return range(curr_year - 2, curr_year + 5)
36
37def academic_sessions():
38    curr_year = datetime.now().year
39    year_range = range(curr_year - 10, curr_year + 2)
40    return [('%s/%s' % (year,year+1), year) for year in year_range]
41
42academic_sessions_vocab = SimpleWAeUPVocabulary(*academic_sessions())
43
[6990]44registration_states_vocab = SimpleWAeUPVocabulary(
45    ('created', CREATED),
46    ('admitted', ADMITTED),
47    ('clearance started', CLEARANCE),
48    ('clearance requested', REQUESTED),
49    ('cleared', CLEARED),
50    ('school fee paid', PAID),
51    ('returning', RETURNING),
52    ('courses registered', REGISTERED),
53    ('courses validated', VALIDATED),
54    )
55
[4858]56class FatalCSVError(Exception):
57    """Some row could not be processed.
58    """
59    pass
60
[6226]61class DuplicationError(Exception):
62    """An exception that can be raised when duplicates are found.
63
64    When raising :exc:`DuplicationError` you can, beside the usual
65    message, specify a list of objects which are duplicates. These
66    values can be used by catching code to print something helpful or
67    similar.
68    """
69    def __init__(self, msg, entries=[]):
70        self.msg = msg
71        self.entries = entries
72
73    def __str__(self):
74        return '%r' % self.msg
75
[6143]76class RoleSource(BasicSourceFactory):
[6508]77    """A source for global roles.
78    """
[6143]79    def getValues(self):
[6157]80        # late import: in interfaces we should not import local modules
81        from waeup.sirp.permissions import getWAeUPRoleNames
82        return getWAeUPRoleNames()
83
84    def getTitle(self, value):
85        # late import: in interfaces we should not import local modules
[6143]86        from waeup.sirp.permissions import getRoles
[6157]87        roles = dict(getRoles())
88        if value in roles.keys():
89            title = roles[value].title
[6569]90            if '.' in title:
91                title = title.split('.', 2)[1]
[6157]92        return title
[6143]93
[4789]94class IWAeUPObject(Interface):
95    """A WAeUP object.
[5663]96
97    This is merely a marker interface.
[4789]98    """
99
100class IUniversity(IWAeUPObject):
[3521]101    """Representation of a university.
102    """
[5955]103
[6065]104
[4789]105class IWAeUPContainer(IWAeUPObject):
106    """A container for WAeUP objects.
107    """
108
109class IWAeUPContained(IWAeUPObject):
110    """An item contained in an IWAeUPContainer.
111    """
[6136]112
[4789]113class IWAeUPExporter(Interface):
114    """An exporter for objects.
115    """
116    def export(obj, filepath=None):
117        """Export by pickling.
118
119        Returns a file-like object containing a representation of `obj`.
120
121        This is done using `pickle`. If `filepath` is ``None``, a
122        `cStringIO` object is returned, that contains the saved data.
123        """
124
125class IWAeUPXMLExporter(Interface):
126    """An XML exporter for objects.
127    """
128    def export(obj, filepath=None):
129        """Export as XML.
130
131        Returns an XML representation of `obj`.
132
133        If `filepath` is ``None``, a StringIO` object is returned,
134        that contains the transformed data.
135        """
136
137class IWAeUPXMLImporter(Interface):
138    """An XML import for objects.
139    """
140    def doImport(filepath):
141        """Create Python object from XML.
142
143        Returns a Python object.
144        """
145
[4858]146class IBatchProcessor(Interface):
147    """A batch processor that handles mass-operations.
148    """
149    name = schema.TextLine(
150        title = u'Importer name'
151        )
152
153    mode = schema.Choice(
154        title = u'Import mode',
155        values = ['create', 'update', 'remove']
156        )
[6136]157
[5476]158    def doImport(path, headerfields, mode='create', user='Unknown',
159                 logger=None):
[4858]160        """Read data from ``path`` and update connected object.
[5476]161
162        `headerfields` is a list of headerfields as read from the file
163        to import.
164
165        `mode` gives the import mode to use (``'create'``,
166        ``'update'``, or ``'remove'``.
167
168        `user` is a string describing the user performing the
169        import. Normally fetched from current principal.
170
171        `logger` is the logger to use during import.
[4858]172        """
173
[4789]174class IUserAccount(IWAeUPObject):
175    """A user account.
176    """
177    name = schema.TextLine(
178        title = u'User ID',
[6512]179        description = u'Login name of user',
[4789]180        required = True,)
181    title = schema.TextLine(
[6512]182        title = u'Name',
183        description = u'Real name of user',
[4789]184        required = False,)
185    description = schema.TextLine(
[6512]186        title = u'Description',
[4789]187        required = False,)
188    password = schema.Password(
189        title = u'Password',
190        required = True,)
191    roles = schema.List(
[6508]192        title = u'Global roles',
[4789]193        value_type = schema.Choice(source=RoleSource()))
[6136]194
195
[4789]196class IUserContainer(IWAeUPObject):
197    """A container for users (principals).
198
199    These users are used for authentication purposes.
200    """
201
202    def addUser(name, password, title=None, description=None):
203        """Add a user.
204        """
205
206    def delUser(name):
207        """Delete a user if it exists.
208        """
209
[6141]210class ILocalRolesAssignable(Interface):
211    """The local roles assignable to an object.
212    """
213    def __call__():
214        """Returns a list of dicts.
215
216        Each dict contains a ``name`` referring to the role assignable
217        for the specified object and a `title` to describe the range
218        of users to which this role can be assigned.
219        """
220
[6907]221class IConfigurationContainer(IWAeUPObject):
222    """A container for session configuration objects.
223    """
224
225    name = schema.TextLine(
226        title = u'Name of University',
227        default = u'Sample University',
228        required = True,
229        )
230
231    title = schema.TextLine(
[6990]232        title = u'Title of Frontpage',
[6907]233        default = u'Welcome to the Student Information and Registration ' +
234                  u'Portal of Sample University',
235        required = False,
236        )
237
238    skin = schema.Choice(
239        title = u'Skin',
240        default = u'gray waeup theme',
241        vocabulary = 'waeup.sirp.browser.theming.ThemesVocabulary',
242        required = True,
243        )
244
245    frontpage = schema.Text(
246        title = u'Content in reST format',
247        required = False,
248        default = default_frontpage,
249        )
250
[6990]251    accommodation_session = schema.Choice(
252        title = u'Accommodation Booking Session',
253        source = academic_sessions_vocab,
254        default = datetime.now().year,
255        required = False,
256        readonly = False,
257        )
258
259    accommodation_states = schema.List(
260        title = u'Allowed States for Accommodation Booking',
261        value_type = schema.Choice(
262            vocabulary = registration_states_vocab,
263            ),
264        default = [],
265        )
266
[6907]267class ISessionConfiguration(IWAeUPObject):
[6915]268    """A session configuration object.
[6907]269    """
270
[6915]271    academic_session = schema.Choice(
272        title = u'Academic Session',
273        source = academic_sessions_vocab,
274        default = None,
275        required = True,
276        readonly = True,
277        )
278
279    fee_1 = schema.Int(
280        title = u'School Fee',
281        default = 0,
282        )
283
[6929]284    surcharge_1 = schema.Int(
285        title = u'Surcharge 1',
286        default = 0,
287        )
288
289    surcharge_2 = schema.Int(
290        title = u'Surcharge 2',
291        default = 0,
292        )
293
294    surcharge_3 = schema.Int(
295        title = u'Surcharge 3',
296        default = 0,
297        )
298
[6916]299    fee_2 = schema.Int(
[6929]300        title = u'Clearance Fee',
[6916]301        default = 0,
302        )
303
[6918]304    def getSessionString():
305        """Returns the session string from the vocabulary.
306        """
307
308
[6916]309class ISessionConfigurationAdd(ISessionConfiguration):
310    """A session configuration object in add mode.
311    """
312
313    academic_session = schema.Choice(
314        title = u'Academic Session',
315        source = academic_sessions_vocab,
316        default = None,
317        required = True,
318        readonly = False,
319        )
320
321ISessionConfigurationAdd['academic_session'].order =  ISessionConfiguration[
322    'academic_session'].order
323
[4789]324class IDataCenter(IWAeUPObject):
325    """A data center.
326
327    TODO : declare methods, at least those needed by pages.
328    """
329    pass
330
331class IDataCenterFile(Interface):
332    """A data center file.
333    """
[4858]334
335    name = schema.TextLine(
336        title = u'Filename')
337
338    size = schema.TextLine(
339        title = u'Human readable file size')
340
341    uploaddate = schema.TextLine(
342        title = u'Human readable upload datetime')
343
344    lines = schema.Int(
345        title = u'Number of lines in file')
[6136]346
[4789]347    def getDate():
348        """Get creation timestamp from file in human readable form.
349        """
350
351    def getSize():
352        """Get human readable size of file.
353        """
[4858]354
355    def getLinesNumber():
356        """Get number of lines of file.
357        """
[4882]358
359class IDataCenterStorageMovedEvent(IObjectEvent):
360    """Emitted, when the storage of a datacenter changes.
361    """
[5007]362
[6136]363class IObjectUpgradeEvent(IObjectEvent):
364    """Can be fired, when an object shall be upgraded.
365    """
366
[6180]367class ILocalRoleSetEvent(IObjectEvent):
368    """A local role was granted/revoked for a principal on an object.
369    """
370    role_id = Attribute(
371        "The role id that was set.")
372    principal_id = Attribute(
373        "The principal id for which the role was granted/revoked.")
374    granted = Attribute(
375        "Boolean. If false, then the role was revoked.")
376
[5007]377class IQueryResultItem(Interface):
378    """An item in a search result.
379    """
380    url = schema.TextLine(
381        title = u'URL that links to the found item')
382    title = schema.TextLine(
383        title = u'Title displayed in search results.')
384    description = schema.Text(
385        title = u'Longer description of the item found.')
[6136]386
[5013]387class IWAeUPSIRPPluggable(Interface):
388    """A component that might be plugged into a WAeUP SIRP app.
[5658]389
390    Components implementing this interface are referred to as
391    'plugins'. They are normally called when a new
392    :class:`waeup.sirp.app.University` instance is created.
393
394    Plugins can setup and update parts of the central site without the
395    site object (normally a :class:`waeup.sirp.app.University` object)
396    needing to know about that parts. The site simply collects all
397    available plugins, calls them and the plugins care for their
[5676]398    respective subarea like the applicants area or the datacenter
[5658]399    area.
400
401    Currently we have no mechanism to define an order of plugins. A
402    plugin should therefore make no assumptions about the state of the
403    site or other plugins being run before and instead do appropriate
404    checks if necessary.
405
406    Updates can be triggered for instance by the respective form in
407    the site configuration. You normally do updates when the
408    underlying software changed.
[5013]409    """
[5069]410    def setup(site, name, logger):
411        """Create an instance of the plugin.
[5013]412
[5658]413        The method is meant to be called by the central app (site)
414        when it is created.
415
416        `site`:
417           The site that requests a setup.
418
419        `name`:
420           The name under which the plugin was registered (utility name).
421
422        `logger`:
423           A standard Python logger for the plugins use.
[5069]424        """
425
426    def update(site, name, logger):
427        """Method to update an already existing plugin.
428
429        This might be called by a site when something serious
[5658]430        changes. It is a poor-man replacement for Zope generations
431        (but probably more comprehensive and better understandable).
432
433        `site`:
434           The site that requests an update.
435
436        `name`:
437           The name under which the plugin was registered (utility name).
438
439        `logger`:
440           A standard Python logger for the plugins use.
[5069]441        """
[5898]442
[5899]443class IAuthPluginUtility(Interface):
[5898]444    """A component that cares for authentication setup at site creation.
445
446    Utilities providing this interface are looked up when a Pluggable
447    Authentication Utility (PAU) for any
448    :class:`waeup.sirp.app.University` instance is created and put
449    into ZODB.
450
451    The setup-code then calls the `register` method of the utility and
452    expects a modified (or unmodified) version of the PAU back.
453
454    This allows to define any authentication setup modifications by
455    submodules or third-party modules/packages.
456    """
457
458    def register(pau):
459        """Register any plugins wanted to be in the PAU.
460        """
461
462    def unregister(pau):
463        """Unregister any plugins not wanted to be in the PAU.
464        """
[6273]465
466class IObjectConverter(Interface):
467    """Object converters are available as simple adapters, adapting
468       interfaces (not regular instances).
469
470    """
471
[6277]472    def fromStringDict(self, data_dict, context, form_fields=None):
473        """Convert values in `data_dict`.
[6273]474
[6277]475        Converts data in `data_dict` into real values based on
476        `context` and `form_fields`.
[6273]477
[6277]478        `data_dict` is a mapping (dict) from field names to values
479        represented as strings.
[6273]480
[6277]481        The fields (keys) to convert can be given in optional
482        `form_fields`. If given, form_fields should be an instance of
483        :class:`zope.formlib.form.Fields`. Suitable instances are for
484        example created by :class:`grok.AutoFields`.
[6273]485
[6277]486        If no `form_fields` are given, a default is computed from the
487        associated interface.
[6273]488
[6277]489        The `context` can be an existing object (implementing the
490        associated interface) or a factory name. If it is a string, we
491        try to create an object using
492        :func:`zope.component.createObject`.
493
494        Returns a tuple ``(<FIELD_ERRORS>, <INVARIANT_ERRORS>,
495        <DATA_DICT>)`` where
496
497        ``<FIELD_ERRORS>``
498           is a list of tuples ``(<FIELD_NAME>, <ERROR>)`` for each
499           error that happened when validating the input data in
500           `data_dict`
501
502        ``<INVARIANT_ERRORS>``
503           is a list of invariant errors concerning several fields
504
505        ``<DATA_DICT>``
506           is a dict with the values from input dict converted.
507
508        If errors happen, i.e. the error lists are not empty, always
509        an empty ``<DATA_DICT>`` is returned.
510
511        If ``<DATA_DICT>` is non-empty, there were no errors.
[6273]512        """
[6293]513
[6338]514class IObjectHistory(Interface):
515
516    messages = schema.List(
517        title = u'List of messages stored',
518        required = True,
519        )
520
521    def addMessage(message):
522        """Add a message.
523        """
[6353]524
525class IWAeUPWorkflowInfo(IWorkflowInfo):
526    """A :class:`hurry.workflow.workflow.WorkflowInfo` with additional
527       methods for convenience.
528    """
529    def getManualTransitions():
530        """Get allowed manual transitions.
531
532        Get a sorted list of tuples containing the `transition_id` and
533        `title` of each allowed transition.
534        """
[6481]535
536class ISiteLoggers(Interface):
537
538    loggers = Attribute("A list or generator of registered WAeUPLoggers")
539
540    def register(name, filename=None, site=None, **options):
541        """Register a logger `name` which logs to `filename`.
542
543        If `filename` is not given, logfile will be `name` with
544        ``.log`` as filename extension.
545        """
546
547    def unregister(name):
548        """Unregister a once registered logger.
549        """
550
551class ILogger(Interface):
552    """A logger cares for setup, update and restarting of a Python logger.
553    """
554
555    logger = Attribute("""A :class:`logging.Logger` instance""")
556
557
558    def __init__(name, filename=None, site=None, **options):
559        """Create a WAeUP logger instance.
560        """
561
562    def setup():
563        """Create a Python :class:`logging.Logger` instance.
564
565        The created logger is based on the params given by constructor.
566        """
567
568    def update(**options):
569        """Update the logger.
570
571        Updates the logger respecting modified `options` and changed
572        paths.
573        """
[6754]574
575class ILoggerCollector(Interface):
576
577    def getLoggers(site):
578        """Return all loggers registered for `site`.
579        """
580
581    def registerLogger(site, logging_component):
582        """Register a logging component residing in `site`.
583        """
584
585    def unregisterLogger(site, logging_component):
586        """Unregister a logger.
587        """
Note: See TracBrowser for help on using the repository browser.