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

Last change on this file since 5972 was 5952, checked in by uli, 14 years ago

Add a confirm_passport attribute to applicants and reflect that change in form.

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