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

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

Clean up. Document what happens and avoid empty doc strings.

File size: 23.2 KB
Line 
1##
2## interfaces.py
3## Login : <uli@pu.smp.net>
4## Started on  Sun Jan 16 15:30:01 2011 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2011 Uli Fouquet
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""Interfaces regarding student applicants and related components.
23"""
24import os
25import waeup.sirp.browser
26from grokcore.content.interfaces import IContainer
27from zc.sourcefactory.basic import BasicSourceFactory
28from zope import schema
29from zope.component import getUtility, getUtilitiesFor
30from zope.interface import Interface, Attribute
31from zope.pluggableauth.interfaces import IPrincipalInfo
32from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
33from waeup.sirp.image.schema import ImageFile
34from waeup.sirp.image.image import WAeUPImageFile
35from waeup.sirp.interfaces import IWAeUPObject, SimpleWAeUPVocabulary
36
37
38IMAGE_PATH = os.path.join(
39    os.path.dirname(waeup.sirp.browser.__file__),
40    'static'
41    )
42DEFAULT_PASSPORT_IMAGE_MALE = WAeUPImageFile(
43    'passport.jpg',
44    open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg')).read(),
45    )
46DEFAULT_PASSPORT_IMAGE_FEMALE = WAeUPImageFile(
47    'passport.jpg',
48    open(os.path.join(IMAGE_PATH, 'placeholder_f.jpg')).read(),
49    )
50
51#: Categories of applications we support.
52#: Yet not complete nor correct.
53APPLICATION_CATEGORIES = (
54    ('Direct Entry Screening Exam (PDE)', 'pde'),
55    ('Post-UME', 'pume'),
56    ('Post-UDE', 'pude'),
57    ('PCE', 'pce'),
58    ('Common Entry Screening Test (CEST)', 'cest'),
59    )
60
61#: A :class:`waeup.sirp.interfaces.SimpleWAeUPVocabulary` of supported
62#: application categories.
63application_categories_vocab = SimpleWAeUPVocabulary(*APPLICATION_CATEGORIES)
64
65class GenderSource(BasicSourceFactory):
66    """A gender source delivers basically a mapping
67       ``{'m': 'male', 'f': 'female'}``
68
69       Using a source, we make sure that the tokens (which are
70       stored/expected for instance from CSV files) are something one
71       can expect and not cryptic IntIDs.
72    """
73    def getValues(self):
74        return ['m', 'f']
75
76    def getToken(self, value):
77        return value[0].lower()
78       
79    def getTitle(self, value):
80        if value == 'm':
81            return 'male'
82        if value == 'f':
83            return 'female'
84           
85class ApplicantContainerProviderSource(BasicSourceFactory):
86    """A source offering all available applicants container types.
87
88    The values returned by this source are names of utilities that can
89    create :class:`ApplicantContainer` instances. So, if you get a
90    name like ``'myactype'`` from this source, then you can do:
91
92      >>> from zope.component import getUtility
93      >>> p = getUtility(IApplicantsContainerProvider, name=myactype)
94      >>> my_applicants_container = p.factory()
95
96    Or you can access class-attributes like
97
98      >>> my_applicants_container.container_title
99      'Pretty'
100   
101    """
102    def getValues(self):
103        """Returns a list of ``(<name>, <provider>)`` tuples.
104
105        Here ``<name>`` is the name under which an
106        :class:``ApplicantContainerProvider`` was registered as a
107        utility and ``<provider>`` is the utility itself.
108        """
109        return getUtilitiesFor(IApplicantsContainerProvider)
110
111    def getToken(self, value):
112        """Return the name of the ``(<name>, <provider>)`` tuple.
113        """
114        return value[0]
115
116    def getTitle(self, value):
117        """Get a 'title - description' string for a container type.
118        """
119        factory = value[1].factory
120        return "%s - %s" % (
121            factory.container_title, factory.container_description)
122
123class IResultEntry(IWAeUPObject):
124    subject = schema.TextLine(
125        title = u'Subject',
126        description = u'The subject',
127        required=False,
128        )
129    score = schema.TextLine(
130        title = u'Score',
131        description = u'The score',
132        required=False,
133        )
134
135class IApplicantsRoot(IWAeUPObject, IContainer):
136    """A container for university applicants containers.
137    """
138    pass
139   
140
141class IApplicantsContainer(IWAeUPObject):
142    """An applicants container contains university applicants.
143
144    """
145
146    container_title = Attribute(
147        u'classattribute: title for type of container')
148    container_description = Attribute(
149        u'classattribute: description for type of container')
150
151   
152    code = schema.TextLine(
153        title = u'Code',
154        description = u'Abbreviated code of applicants container',
155        default = u'NA',
156        required = True,
157        readonly = True,
158        )   
159   
160    provider = schema.Choice(
161        title = u'Applicants container type',
162        required = True,
163        default = None,
164        source = ApplicantContainerProviderSource(),
165        )       
166   
167    title = schema.TextLine(
168        title = u'Title of the type of applicants stored here',
169        required = True,
170        default = u'Untitled',
171        )
172   
173    description = schema.Text(
174        title = u'Human readable description in reST format',
175        required = False,
176        default = u'No description yet.'
177        )
178
179    startdate = schema.Date(
180        title = u'Date when the application period starts',
181        required = False,
182        default = None,
183        )
184
185    enddate = schema.Date(
186        title = u'Date when the application period ends',
187        required = False,
188        default = None,
189        )
190
191    strict_deadline = schema.Bool(
192        title = u'Forbid additions after deadline (enddate)',
193        required = True,
194        default = True,
195        )
196
197    def archive(id=None):
198        """Create on-dist archive of applicants stored in this term.
199
200        If id is `None`, all applicants are archived.
201
202        If id contains a single id string, only the respective
203        applicants are archived.
204
205        If id contains a list of id strings all of the respective
206        applicants types are saved to disk.
207        """
208
209    def clear(id=None, archive=True):
210        """Remove applicants of type given by 'id'.
211
212        Optionally archive the applicants.
213       
214        If id is `None`, all applicants are archived.
215
216        If id contains a single id string, only the respective
217        applicants are archived.
218
219        If id contains a list of id strings all of the respective
220        applicant types are saved to disk.
221
222        If `archive` is ``False`` none of the archive-handling is done
223        and respective applicants are simply removed from the
224        database.
225        """
226
227class IApplicantsContainerAdd(IApplicantsContainer):
228    """An applicants container contains university applicants.
229    """
230    code = schema.TextLine(
231        title = u'Code',
232        description = u'Abbreviated code of applicants container',
233        default = u'NA',
234        required = True,
235        readonly = False,
236        )   
237
238IApplicantsContainerAdd['code'].order =  IApplicantsContainer['code'].order
239
240class IApplicantBaseData(IWAeUPObject):
241    """The data for an applicant.
242
243    This is a base interface with no field (except ``reg_no``)
244    required. For use with importers, forms, etc., please use one of
245    the derived interfaces below, which set more fields to required
246    state, depending on use-case.
247    """
248    reg_no = schema.TextLine(
249        title = u'JAMB Registration Number',
250        )
251    access_code = schema.TextLine(
252        title = u'Access Code',
253        required = False,
254        )
255    serial = schema.TextLine(
256        title = u'Serial Number',
257        required = False,
258        )
259    course1 = schema.TextLine(
260        title = u'1st Choice Course of Study',
261        required = False,
262        )
263    course2 = schema.TextLine(
264        title = u'2nd Choice Course of Study',
265        required = False,
266        )
267    course3 = schema.TextLine(
268        title = u'3rd Choice Course of Study',
269        required = False,
270        )
271    firstname = schema.TextLine(
272        title = u'First Name',
273        required = False,
274        )
275    middlenames = schema.TextLine(
276        title = u'Middle Names',
277        required = False,
278        )
279    lastname = schema.TextLine(
280        title = u'Surname/Full Name',
281        required = False,
282        )
283    date_of_birth = schema.Date(
284        title = u'Date of Birth',
285        required = False,
286        )
287    lga = schema.TextLine(
288        # XXX: should be choice
289        title = u'State/LGA (confirmed by applicant)',
290        required = False,
291        )
292    sex = schema.Choice(
293        title = u'Sex',
294        source = GenderSource(),
295        default = u'm',
296        required = False,
297        )
298    email = schema.TextLine(
299        title = u'Email',
300        required = False,
301        )
302    phone = schema.TextLine(
303        title = u'Phone',
304        required = False,
305        )
306    passport = ImageFile(
307        title = u'Passport Photograph',
308        default = DEFAULT_PASSPORT_IMAGE_MALE,
309        required = True,
310        )
311    aos = schema.TextLine(
312        # XXX: should be choice
313        title = u'Area of Specialisation',
314        required = False,
315        )
316    subj1 = schema.TextLine(
317        # XXX: should be choice
318        title = u'1st Choice of Study',
319        required = False,
320        )
321    subj2 = schema.TextLine(
322        # XXX: should be choice
323        title = u'2nd Choice of Study',
324        required = False,
325        )
326    subj3 = schema.TextLine(
327        # XXX: should be choice
328        title = u'3rd Choice of Study',
329        required = False,
330        )
331    #
332    # Higher Educational Data
333    #
334    hq_matric_no = schema.TextLine(
335        title = u'Former Matric Number',
336        required = False,
337        )
338    hq_type = schema.TextLine(
339        title = u'Higher Qualification',
340        required = False,
341        )
342    hq_grade = schema.TextLine(
343        title = u'Higher Qualification Grade',
344        required = False,
345        )
346    hq_school = schema.TextLine(
347        title = u'School Attended',
348        required = False,
349        )
350    hq_session = schema.TextLine(
351        title = u'Session Obtained',
352        required = False,
353        )
354    hq_disc = schema.TextLine(
355        title = u'Discipline',
356        required = False,
357        )
358    #
359    # First sitting data
360    #
361    fst_sit_fname = schema.TextLine(
362        title = u'Full Name',
363        required = False,
364        )
365    fst_sit_no = schema.TextLine(
366        title = u'Exam Number',
367        required = False,
368        )
369    fst_sit_date = schema.Date(
370        title = u'Exam Date (dd/mm/yyyy)',
371        required = False,
372        )
373    fst_sit_type = schema.TextLine(
374        # XXX: Should be choice
375        title = u'Exam Type',
376        required = False,
377        )
378    fst_sit_results = schema.List(
379        title = u'Results',
380        required = False,
381        value_type = schema.Object(
382            title = u'Entries',
383            schema = IResultEntry,
384            required = False,
385            )
386        )
387    scd_sit_fname = schema.TextLine(
388        title = u'Full Name',
389        required = False,
390        )
391    scd_sit_no = schema.TextLine(
392        title = u'Exam Number',
393        required = False,
394        )
395    scd_sit_date = schema.Date(
396        title = u'Exam Date (dd/mm/yyyy)',
397        required = False,
398        )
399    scd_sit_type = schema.TextLine(
400        # XXX: Should be choice
401        title = u'Exam Type',
402        required = False,
403        )
404    scd_sit_results = schema.TextLine(
405        # XXX: Should be nested list of choices
406        title = u'Results',
407        required = False,
408        )
409    #
410    # JAMB scores
411    #
412    eng_score = schema.TextLine(
413        title = u"'English' score",
414        required = False,
415        )
416    subj1score = schema.TextLine(
417        title = u'1st Choice of Study Score',
418        required = False,
419        )
420    subj2score = schema.TextLine(
421        title = u'2nd Choice of Study Score',
422        required = False,
423        )
424    subj3score = schema.TextLine(
425        title = u'3rd Choice of Study Score',
426        required = False,
427        )
428    # XXX: Total score???
429   
430    #
431    # Application Data
432    #
433    application_date = schema.Date(
434        title = u'Application Date',
435        required = False,
436        )
437    status = schema.TextLine(
438        # XXX: should be 'status' type
439        title = u'Application Status',
440        required = False,
441        )
442    screening_date = schema.Date(
443        title = u'Screening Date',
444        required = False,
445        )
446    screening_type = schema.TextLine(
447        # XXX: schould be choice
448        title = u'Screening Type',
449        required = False,
450        )
451    screening_score = schema.TextLine(
452        title = u'Screening Score',
453        required = False,
454        )
455    screening_venue = schema.TextLine(
456        title = u'Screening Venue',
457        required = False,
458        )
459    total_score = schema.TextLine(
460        title = u'Total Score',
461        required = False,
462        )
463    course_admitted = schema.TextLine(
464        # XXX: should be choice
465        title = u'Admitted Course of Study',
466        required = False,
467        )
468    department = schema.TextLine(
469        # XXX: if we have a course, dept. is not necessary
470        title = u'Department',
471        required = False,
472        )
473    faculty = schema.TextLine(
474        # XXX: if we have a course, faculty is not necessary
475        title = u'Faculty',
476        required = False,
477        )
478    entry_session = schema.TextLine(
479        # XXX: should be choice, should have sensible default: upcoming session
480        title = u'Entry Session',
481        required = False,
482        )
483    notice = schema.Text(
484        title = u'Notice',
485        required = False,
486        )
487    student_id = schema.TextLine(
488        title = u'Student ID',
489        required = False,
490        )
491    import_record_no = schema.TextLine(
492        title = u'Import Record No.',
493        required = False,
494        )
495    imported_by = schema.TextLine(
496        title = u'Imported By',
497        required = False,
498        )
499    import_date = schema.Datetime(
500        title = u'Import Date',
501        required = False,
502        )
503    import_from = schema.TextLine(
504        title = u'Import Source',
505        required = False,
506        )
507    confirm_passport = schema.Bool(
508        title = u"Confirmation that photograph represents applicant ticked.",
509        default = False,
510        required = True,
511        )
512
513   
514class IApplicant(IApplicantBaseData):
515    """An applicant.
516
517    This is basically the applicant base data. Here we repeat the
518    fields from base data only with the `required` attribute of
519    required attributes set to True (which is the default).
520    """
521    locked = schema.Bool(
522        title = u'Form locked',
523        default = False,
524        readonly = True,
525        )
526    access_code = schema.TextLine(
527        title = u'Access Code',
528        )
529    course1 = schema.TextLine(
530        title = u'1st Choice Course of Study',
531        )
532    firstname = schema.TextLine(
533        title = u'First Name',
534        )
535    middlenames = schema.TextLine(
536        title = u'Middle Names',
537        )
538    lastname = schema.TextLine(
539        title = u'Surname/Full Name',
540        )
541    date_of_birth = schema.Date(
542        title = u'Date of Birth',
543        )
544    jamb_state = schema.TextLine(
545        title = u'State (provided by JAMB)',
546        )
547    jamb_lga = schema.TextLine(
548        title = u'LGA (provided by JAMB)',
549        )
550    lga = schema.TextLine(
551        # XXX: should be choice
552        title = u'State/LGA (confirmed by applicant)',
553        )
554    sex = schema.Choice(
555        title = u'Sex',
556        source = GenderSource(),
557        default = u'm',
558        )
559    #passport = schema.Bool(
560    #    title = u'Passport Photograph',
561    #    default = True,
562    #    )
563    passport = ImageFile(
564        title = u'Passport Photograph',
565        default = DEFAULT_PASSPORT_IMAGE_MALE,
566        required = True ,
567        )
568    #
569    # Higher Educational Data
570    #
571
572    #
573    # First sitting data
574    #
575    fst_sit_fname = schema.TextLine(
576        title = u'Full Name',
577        )
578
579    #
580    # Second sitting data
581    #
582    scd_sit_fname = schema.TextLine(
583        title = u'Full Name',
584        )
585    #
586    # Application Data
587    #
588    application_date = schema.Date(
589        title = u'Application Date',
590        )
591    status = schema.TextLine(
592        # XXX: should be 'status' type
593        title = u'Application Status',
594        )
595    screening_date = schema.Date(
596        title = u'Screening Date',
597        )
598    screening_type = schema.TextLine(
599        # XXX: schould be choice
600        title = u'Screening Type',
601        )
602    screening_score = schema.TextLine(
603        title = u'Screening Score',
604        )
605    entry_session = schema.TextLine(
606        # XXX: should be choice
607        # XXX: should have sensible default: upcoming session
608        title = u'Entry Session',
609        )
610    import_record_no = schema.TextLine(
611        title = u'Import Record No.',
612        )
613    imported_by = schema.TextLine(
614        title = u'Imported By',
615        )
616    import_date = schema.Datetime(
617        title = u'Import Date',
618        )
619    import_from = schema.TextLine(
620        title = u'Import Source',
621        )
622
623class IApplicantPDEEditData(IWAeUPObject):
624    """The data set presented to PDE applicants.
625    """
626    locked = schema.Bool(
627        title = u'Form locked',
628        default = False,
629        readonly = True,
630        )
631    access_code = schema.TextLine(
632        title = u'Access Code',
633        readonly = True,
634        )
635    course1 = schema.TextLine(
636        title = u'1st Choice Course of Study',
637        readonly = True,
638        )
639    course2 = schema.TextLine(
640        title = u'2nd Choice Course of Study',
641        required = False,
642        )
643    course3 = schema.TextLine(
644        title = u'3rd Choice Course of Study',
645        required = False,
646        )
647    lastname = schema.TextLine(
648        title = u'Name',
649        readonly = True,
650        )
651    date_of_birth = schema.Date(
652        title = u'Date of Birth',
653        required = True,
654        )
655    lga = schema.TextLine(
656        # XXX: should be choice
657        title = u'State/LGA (confirmed by applicant)',
658        required = False,
659        )
660    email = schema.TextLine(
661        title = u'Email',
662        required = False,
663        )
664    phone = schema.TextLine(
665        title = u'Phone',
666        required = False,
667        )
668    aos = schema.TextLine(
669        # XXX: should be choice
670        title = u'Area of Specialisation',
671        required = False,
672        )
673    subj1 = schema.TextLine(
674        # XXX: should be choice
675        title = u'1st Choice of Study',
676        readonly = True,
677        )
678    subj2 = schema.TextLine(
679        # XXX: should be choice
680        title = u'2nd Choice of Study',
681        required = False,
682        )
683    subj3 = schema.TextLine(
684        # XXX: should be choice
685        title = u'3rd Choice of Study',
686        required = False,
687        )
688   
689    #
690    # Application Data
691    #
692    application_date = schema.Date(
693        title = u'Application Date',
694        readonly = True,
695        )
696    status = schema.TextLine(
697        # XXX: should be 'status' type
698        title = u'Application Status',
699        readonly = True,
700        )
701    screening_date = schema.Date(
702        title = u'Screening Date',
703        readonly = True,
704        )
705    passport = ImageFile(
706        title = u'Passport Photograph',
707        default = DEFAULT_PASSPORT_IMAGE_MALE,
708        required = True,
709        )
710    confirm_passport = schema.Bool(
711        title = u"Confirmation that photograph represents applicant ticked.",
712        default = False,
713        required = True,
714        )
715
716   
717class IApplicantPrincipalInfo(IPrincipalInfo):
718    """Infos about principals that are applicants.
719    """
720    access_code = Attribute("The Access Code the user purchased")
721
722class IApplicantPrincipal(IPrincipal):
723    """A principal that is an applicant.
724
725    This interface extends zope.security.interfaces.IPrincipal and
726    requires also an `id` and other attributes defined there.
727    """
728    access_code = schema.TextLine(
729        title = u'Access Code',
730        description = u'The access code purchased by the user.',
731        required = True,
732        readonly = True)
733
734class IApplicantsFormChallenger(Interface):
735    """A challenger that uses a browser form to collect applicant
736       credentials.
737    """
738    loginpagename = schema.TextLine(
739        title = u'Loginpagename',
740        description = u"""Name of the login form used by challenger.
741
742        The form must provide an ``access_code`` input field.
743        """)
744
745    accesscode_field = schema.TextLine(
746        title = u'Access code field',
747        description = u'''Field of the login page which is looked up for
748                          access_code''',
749        default = u'access_code',
750        )
751
752
753class IApplicantSessionCredentials(Interface):
754    """Interface for storing and accessing applicant credentials in a
755       session.
756    """
757
758    def __init__(access_code):
759        """Create applicant session credentials."""
760
761    def getAccessCode():
762        """Return the access code."""
763
764
765class IApplicantsContainerProvider(Interface):
766    """A provider for applicants containers.
767
768    Applicants container providers are meant to be looked up as
769    utilities. This way we can find all applicant container types
770    defined somewhere.
771
772    Each applicants container provider registered as utility provides
773    one container type and one should be able to call the `factory`
774    attribute to create an instance of the requested container type.
775
776    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
777   
778    Samples
779    *******
780   
781    Given, you had an IApplicantsContainer implementation somewhere
782    and you would like to make it findable on request, then you would
783    normally create an appropriate provider utility like this::
784
785      import grok
786      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
787
788      class MyContainerProvider(grok.GlobalUtility):
789          grok.implements(IApplicantsContainerProvider)
790          grok.name('MyContainerProvider') # Must be unique
791          factory = MyContainer # A class implementing IApplicantsContainer
792                                # or derivations thereof.
793
794    This utility would be registered on startup and could then be used
795    like this:
796
797      >>> from zope.component import getAllUtilitiesRegisteredFor
798      >>> from waeup.sirp.applicants.interfaces import (
799      ...     IApplicantsContainerProvider)
800      >>> all_providers = getAllUtilitiesRegisteredFor(
801      ...     IApplicantsContainerProvider)
802      >>> all_providers
803      [<MyContainerProvider object at 0x...>]
804
805    You could look up this specific provider by name:
806
807      >>> from zope.component import getUtility
808      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
809      >>> p
810      <MyContainerProvider object at 0x...>
811     
812    An applicants container would then be created like this:
813
814      >>> provider = all_providers[0]
815      >>> container = provider.factory()
816      >>> container
817      <MyContainer object at 0x...>
818
819    """
820    factory = Attribute("A class that can create instances of the "
821                        "requested container type")
Note: See TracBrowser for help on using the repository browser.