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

Last change on this file since 6087 was 6087, checked in by Henrik Bettermann, 14 years ago

Adding some attributes to ApplicantsContainer?. Generate title and code of containers automatically.

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