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

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

Add clearance payment category.

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