source: main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/interfaces.py @ 7051

Last change on this file since 7051 was 7051, checked in by uli, 13 years ago

Update docs.
Add new method for external file storage.

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