source: main/kofacustom.edocons/trunk/src/kofacustom/edocons/applicants/interfaces.py @ 17930

Last change on this file since 17930 was 17683, checked in by Henrik Bettermann, 11 months ago

Modify application form. Upon application, the candidate must be seventeen years old (17).

  • Property svn:keywords set to Id
File size: 23.1 KB
Line 
1# -*- coding: utf-8 -*-
2## $Id: interfaces.py 17683 2024-01-29 11:38:51Z henrik $
3##
4## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
5## This program is free software; you can redistribute it and/or modify
6## it under the terms of the GNU General Public License as published by
7## the Free Software Foundation; either version 2 of the License, or
8## (at your option) any later version.
9##
10## This program is distributed in the hope that it will be useful,
11## but WITHOUT ANY WARRANTY; without even the implied warranty of
12## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13## GNU General Public License for more details.
14##
15## You should have received a copy of the GNU General Public License
16## along with this program; if not, write to the Free Software
17## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18##
19"""Customized interfaces of the university application package.
20"""
21
22from datetime import datetime
23from zope import schema
24from zope.interface import invariant, Invalid
25from zope.component import getUtility
26from zope.catalog.interfaces import ICatalog
27from zc.sourcefactory.basic import BasicSourceFactory
28from waeup.kofa.university.vocabularies import StudyModeSource
29from waeup.kofa.applicants.interfaces import (
30    IApplicantBaseData,
31    AppCatCertificateSource, CertificateSource)
32from waeup.kofa.schoolgrades import ResultEntryField
33from waeup.kofa.interfaces import (
34    SimpleKofaVocabulary, academic_sessions_vocab, validate_email,
35    IKofaObject, MonthSource)
36from waeup.kofa.schema import FormattedDate, TextLineChoice, PhoneNumber
37from waeup.kofa.students.vocabularies import nats_vocab, GenderSource
38from waeup.kofa.applicants.interfaces import (
39    contextual_reg_num_source,
40    year_range,
41    IApplicantBaseData)
42from kofacustom.nigeria.applicants.interfaces import (
43    LGASource, high_qual, high_grade, exam_types,
44    #jambsubjects,
45    INigeriaUGApplicant, INigeriaPGApplicant,
46    INigeriaApplicantOnlinePayment,
47    INigeriaUGApplicantEdit, INigeriaPGApplicantEdit,
48    INigeriaApplicantUpdateByRegNo,
49    IPUTMEApplicantEdit,
50    )
51from kofacustom.nigeria.interfaces import (
52    LGASource, DisabilitiesSource,
53    high_qual, high_grade, exam_types, validate_jamb_reg_number)
54
55from kofacustom.edocons.interfaces import MessageFactory as _
56from kofacustom.edocons.payments.interfaces import ICustomOnlinePayment
57
58def entry_year_range():
59    curr_year = datetime.now().year
60    return range(curr_year - 50, curr_year)
61
62jambsubjects = SimpleKofaVocabulary(
63    (_('English'),'english_language'),
64    #(_('Agricultural Science'),'agricultural_science'),
65    #(_('Arabic'),'arabic'),
66    (_('Biology'),'biology'),
67    #(_('Book Keeping'),'book_keeping'),
68    (_('Chemistry'),'chemistry'),
69    #(_('Christian Religious Studies'),'christian_religious_studies'),
70    #(_('Commerce'),'commerce'),
71    #(_('Economics'),'economics'),
72    #(_('Financial Accounting'),'financial_accounting'),
73    #(_('Fine Art'),'fine_art'),
74    #(_('Food and Nutrition'),'food_and_nutrition'),
75    #(_('French'),'french'),
76    #(_('Geography'),'geography'),
77    #(_('German'),'german'),
78    #(_('Government'),'government'),
79    #(_('Hausa'),'hausa'),
80    #(_('Home Economics'),'home_economics'),
81    #(_('History'),'history'),
82    #(_('Igbo'),'igbo'),
83    #(_('Literature in English'),'literature_in_english'),
84    #(_('Literature in Nigerian Languages'),'literature_in_nigerian_languages'),
85    #(_('Mathematics'),'mathematics'),
86    #(_('Music'),'music'),
87    (_('Physics'),'physics'),
88    #(_('Yoruba'),'yoruba'),
89    )
90
91programme_types_vocab = SimpleKofaVocabulary(
92    (_('Post UTME'), 'putme'),
93    (_('Post DE'), 'pude'),
94    (_('Admission Screening Exercise'), 'ase'),
95    (_('not applicable'), 'na'),
96    )
97
98DESTINATION_COST = {
99    #'none': ('To the moon', 1000000000.0, 1),
100    'australia': ('Australia / India', 62500.0, 1),
101    'china': ('Arab Emirate / China', 62400.0, 2),
102    'france': ('France / Germany / Netherland / Norway / Sweden', 45300.0, 3),
103    'canada': ('Canada / USA', 56600.0, 4),
104    'finland': ('Finland / Jamaica', 69600.0, 5),
105    'israel': ('Israel', 52000, 6),
106    'turkey': ('Turkey', 57000.0, 7),
107    'uk': ('UK / Ireland', 43000.0, 8),
108    'ghana': ('Ghana / South Africa / Saudi Arabia',  57200.0, 9),
109    'beninrep': ('Republic of Benin', 48600.0, 10),
110    'north': ('Nigeria North', 23000.0, 11),
111    'south': ('Nigeria South East / South / South West', 20100.0, 12),
112    'abuja': ('Abuja', 20400.0, 13),
113    'lokoja': ('Lokoja', 23000.0, 14),
114    'auchi': ('Auchi / Epkoma / Usen / Irrua / Ekhiadolo / Uromi', 19500, 15),
115    'benincity': ('Benin City', 18200, 16),
116    }
117
118class DestinationCostSource(BasicSourceFactory):
119    """A source that delivers continents and shipment costs.
120    """
121    def getValues(self):
122        sorted_items = sorted(DESTINATION_COST.items(),
123                              key=lambda element: element[1][2])
124        return [item[0] for item in sorted_items]
125
126    def getToken(self, value):
127        return value
128
129    def getTitle(self, value):
130        return u"%s (₦ %s)" % (
131            DESTINATION_COST[value][0],
132            DESTINATION_COST[value][1])
133
134class OrderSource(BasicSourceFactory):
135    """
136    """
137    def getValues(self):
138        return ['c',]
139
140    def getToken(self, value):
141        return value
142
143    def getTitle(self, value):
144        #if value == 'o':
145        #    return _('Student Transcript')
146        if value == 'c':
147            return _('Certified Transcript')
148
149class TranscriptCertificateSource(CertificateSource):
150    """Include Department and Faculty in Title.
151    """
152    def getValues(self, context):
153        catalog = getUtility(ICatalog, name='certificates_catalog')
154        resultset = catalog.searchResults(code=(None, None))
155        resultlist = sorted(resultset, key=lambda
156            value: value.__parent__.__parent__.__parent__.code +
157            value.__parent__.__parent__.code +
158            value.code)
159        return resultlist
160
161    def getTitle(self, context, value):
162        """
163        """
164        try: title = "%s / %s / %s (%s)" % (
165            value.__parent__.__parent__.__parent__.title,
166            value.__parent__.__parent__.title,
167            value.title, value.code)
168        except AttributeError:
169            title = "NA / %s (%s)" % (value.title, value.code)
170        return title
171
172class ICustomUGApplicant(INigeriaUGApplicant):
173    """An undergraduate applicant.
174    """
175
176    disabilities = schema.Choice(
177        title = _(u'Disability'),
178        source = DisabilitiesSource(),
179        required = False,
180        )
181    nationality = schema.Choice(
182        source = nats_vocab,
183        title = _(u'Nationality'),
184        required = False,
185        )
186    lga = schema.Choice(
187        source = LGASource(),
188        title = _(u'State/LGA (Nigerians only)'),
189        required = True,
190        )
191    perm_address = schema.Text(
192        title = _(u'Permanent Address'),
193        required = False,
194        )
195
196    next_kin_name = schema.TextLine(
197        title = _(u'Next of Kin Name'),
198        required = False,
199        readonly = False,
200        )
201
202    next_kin_relation = schema.TextLine(
203        title = _(u'Next of Kin Relationship'),
204        required = False,
205        readonly = False,
206        )
207
208    next_kin_address = schema.Text(
209        title = _(u'Next of Kin Address'),
210        required = False,
211        readonly = False,
212        )
213
214    next_kin_phone = PhoneNumber(
215        title = _(u'Next of Kin Phone'),
216        required = False,
217        readonly = False,
218        )
219
220    course1 = schema.Choice(
221        title = _(u'1st Choice Course of Study'),
222        source = AppCatCertificateSource(),
223        required = True,
224        )
225    course2 = schema.Choice(
226        title = _(u'2nd Choice Course of Study'),
227        source = AppCatCertificateSource(),
228        required = False,
229        )
230
231    fst_sit_fname = schema.TextLine(
232        title = _(u'Full Name'),
233        required = False,
234        readonly = False,
235        )
236
237    fst_sit_no = schema.TextLine(
238        title = _(u'Exam Number'),
239        required = False,
240        readonly = False,
241        )
242
243    fst_sit_date = FormattedDate(
244        title = _(u'Exam Date'),
245        required = False,
246        readonly = False,
247        show_year = True,
248        )
249
250    fst_sit_type = schema.Choice(
251        title = _(u'Exam Type'),
252        required = False,
253        readonly = False,
254        vocabulary = exam_types,
255        )
256
257    fst_sit_results = schema.List(
258        title = _(u'Exam Results'),
259        value_type = ResultEntryField(),
260        required = False,
261        readonly = False,
262        defaultFactory=list,
263        )
264
265    scd_sit_fname = schema.TextLine(
266        title = _(u'Full Name'),
267        required = False,
268        readonly = False,
269        )
270
271    scd_sit_no = schema.TextLine(
272        title = _(u'Exam Number'),
273        required = False,
274        readonly = False,
275        )
276
277    scd_sit_date = FormattedDate(
278        title = _(u'Exam Date'),
279        required = False,
280        readonly = False,
281        show_year = True,
282        )
283
284    scd_sit_type = schema.Choice(
285        title = _(u'Exam Type'),
286        required = False,
287        readonly = False,
288        vocabulary = exam_types,
289        )
290
291    scd_sit_results = schema.List(
292        title = _(u'Exam Results'),
293        value_type = ResultEntryField(),
294        required = False,
295        readonly = False,
296        defaultFactory=list,
297        )
298
299    programme_type = schema.Choice(
300        title = _(u'Programme Type'),
301        vocabulary = programme_types_vocab,
302        required = False,
303        )
304
305    hq_type = schema.Choice(
306        title = _(u'Qualification Obtained'),
307        required = False,
308        readonly = False,
309        vocabulary = high_qual,
310        )
311    hq_matric_no = schema.TextLine(
312        title = _(u'Former Matric Number'),
313        required = False,
314        readonly = False,
315        )
316    hq_degree = schema.Choice(
317        title = _(u'Class of Degree'),
318        required = False,
319        readonly = False,
320        vocabulary = high_grade,
321        )
322    hq_school = schema.TextLine(
323        title = _(u'Institution Attended'),
324        required = False,
325        readonly = False,
326        )
327    hq_session = schema.TextLine(
328        title = _(u'Years Attended'),
329        required = False,
330        readonly = False,
331        )
332    hq_disc = schema.TextLine(
333        title = _(u'Discipline'),
334        required = False,
335        readonly = False,
336        )
337    jamb_reg_number = schema.TextLine(
338        title = _(u'JAMB Registration Number'),
339        required = False,
340        constraint=validate_jamb_reg_number,
341        )
342    jamb_subjects = schema.Text(
343        title = _(u'Subjects and Scores'),
344        required = False,
345        )
346    jamb_subjects_list = schema.List(
347        title = _(u'JAMB Subjects'),
348        required = False,
349        defaultFactory=list,
350        value_type = schema.Choice(
351            vocabulary = jambsubjects
352            #source = JAMBSubjectSource(),
353            ),
354        )
355    jamb_score = schema.Int(
356        title = _(u'Total JAMB Score'),
357        required = False,
358        )
359    #jamb_age = schema.Int(
360    #    title = _(u'Age (provided by JAMB)'),
361    #    required = False,
362    #    )
363    cbt_score = schema.Int(
364        title = _(u'CBT Score'),
365        required = False,
366        )
367    cbt_venue = schema.TextLine(
368        title = _(u'CBT Venue'),
369        required = False,
370        )
371    cbt_date = FormattedDate(
372        title = _(u'CBT Date'),
373        required = False,
374        )
375    notice = schema.Text(
376        title = _(u'Notice'),
377        required = False,
378        )
379    screening_venue = schema.TextLine(
380        title = _(u'Screening Venue'),
381        required = False,
382        )
383    screening_date = schema.TextLine(
384        title = _(u'Screening Date'),
385        required = False,
386        )
387    screening_score = schema.Int(
388        title = _(u'Screening Score (%)'),
389        required = False,
390        )
391    aggregate = schema.Int(
392        title = _(u'Aggregate Score (%)'),
393        description = _(u'(average of relative JAMB and PUTME scores)'),
394        required = False,
395        )
396    result_uploaded = schema.Bool(
397        title = _(u'Result uploaded'),
398        default = False,
399        required = False,
400        )
401    student_id = schema.TextLine(
402        title = _(u'Student Id'),
403        required = False,
404        readonly = False,
405        )
406    course_admitted = schema.Choice(
407        title = _(u'Admitted Course of Study'),
408        source = CertificateSource(),
409        required = False,
410        )
411    locked = schema.Bool(
412        title = _(u'Form locked'),
413        default = False,
414        required = False,
415        )
416
417    @invariant
418    def date_of_birth(applicant):
419        today = datetime.today()
420        age = today.year - applicant.date_of_birth.year - ((today.month, today.day) < (applicant.date_of_birth.month, applicant.date_of_birth.day))
421        if age < 17:
422            raise Invalid(_("Upon application, the candidate must be seventeen years old (17)."))
423
424class ICustomPGApplicant(INigeriaPGApplicant):
425    """A postgraduate applicant.
426
427    This interface defines the least common multiple of all fields
428    in pg application forms. In customized forms, fields can be excluded by
429    adding them to the PG_OMIT* tuples.
430    """
431
432class ITranscriptApplicant(IKofaObject):
433    """A transcript applicant.
434    """
435
436    suspended = schema.Bool(
437        title = _(u'Account suspended'),
438        default = False,
439        required = False,
440        )
441
442    locked = schema.Bool(
443        title = _(u'Form locked'),
444        default = False,
445        required = False,
446        )
447
448    applicant_id = schema.TextLine(
449        title = _(u'Transcript Application Id'),
450        required = False,
451        readonly = False,
452        )
453
454    student_id = schema.TextLine(
455        title = _(u'Kofa Student Id'),
456        required = False,
457        readonly = False,
458        )
459
460    matric_number = schema.TextLine(
461        title = _(u'Matriculation Number'),
462        readonly = False,
463        required = False,
464        )
465
466    firstname = schema.TextLine(
467        title = _(u'First Name in School'),
468        required = True,
469        )
470
471    middlename = schema.TextLine(
472        title = _(u'Middle Name in School'),
473        required = False,
474        )
475
476    lastname = schema.TextLine(
477        title = _(u'Surname in School'),
478        required = True,
479        )
480
481    date_of_birth = FormattedDate(
482        title = _(u'Date of Birth'),
483        required = False,
484        #date_format = u'%d/%m/%Y', # Use grok-instance-wide default
485        show_year = True,
486        )
487
488    sex = schema.Choice(
489        title = _(u'Gender'),
490        source = GenderSource(),
491        required = True,
492        )
493
494    nationality = schema.Choice(
495        vocabulary = nats_vocab,
496        title = _(u'Nationality'),
497        required = False,
498        )
499
500    email = schema.ASCIILine(
501        title = _(u'Email Address'),
502        required = True,
503        constraint=validate_email,
504        )
505
506    phone = PhoneNumber(
507        title = _(u'Phone'),
508        description = u'',
509        required = False,
510        )
511
512    #perm_address = schema.Text(
513    #    title = _(u'Current Local Address'),
514    #    required = False,
515    #    readonly = False,
516    #    )
517
518    collected = schema.Bool(
519        title = _(u'Have you collected transcript before?'),
520        default = False,
521        required = False,
522        )
523
524    entry_year = schema.Choice(
525        title = _(u'Year of Entrance'),
526        required = False,
527        values = entry_year_range(),
528        readonly = False,
529        )
530
531    entry_month = schema.Choice(
532        title = _(u'Month of Entry'),
533        source = MonthSource(),
534        required = False,
535        )
536
537    end_year = schema.Choice(
538        title = _(u'Year of Graduation'),
539        required = False,
540        values = entry_year_range(),
541        readonly = False,
542        )
543
544    end_month = schema.Choice(
545        title = _(u'Month of Graduation'),
546        source = MonthSource(),
547        required = False,
548        )
549
550    entry_mode = schema.Choice(
551        title = _(u'Course of Study'),
552        source = StudyModeSource(),
553        required = False,
554        readonly = False,
555        )
556
557    #course_studied = schema.Choice(
558    #    title = _(u'Course of Study'),
559    #    source = TranscriptCertificateSource(),
560    #    description = u'Faculty / Department / Course',
561    #    required = True,
562    #    readonly = False,
563    #    )
564
565    #course_changed = schema.Choice(
566    #    title = _(u'Change of Study Course / Transfer'),
567    #    description = u'If yes, select previous course of study.',
568    #    source = TranscriptCertificateSource(),
569    #    readonly = False,
570    #    required = False,
571    #    )
572
573    spillover_level = schema.Choice(
574        title = _(u'Spill-over'),
575        description = u'Any spill-over? If yes, select session of spill-over.',
576        source = academic_sessions_vocab,
577        required = False,
578        readonly = False,
579        )
580
581    purpose = schema.TextLine(
582        title = _(u'Transcript Purpose'),
583        required = False,
584        )
585
586    #order = schema.Choice(
587    #    source = OrderSource(),
588    #    title = _(u'Type of Order'),
589    #    required = True,
590    #    )
591
592    org_name = schema.TextLine(
593        title = _(u'Name of Organization'),
594        required = False,
595        )
596
597    dispatch_address = schema.Text(
598        title = _(u'Recipient Address'),
599        description = u'Addresses (including email address and phone number) '
600                       'to which transcripts should be posted. '
601                       'All addresses must involve same courier charges.',
602        required = True,
603        readonly = False,
604        )
605
606    charge = schema.Choice(
607        title = _(u'Transcript Charge'),
608        source = DestinationCostSource(),
609        required = True,
610        readonly = False,
611        )
612       
613    curriculum = schema.Bool(
614        title = _(u'Including curriculum'),
615        description = u'₦ 5000 will be added to transcript charge.',
616        default = False,
617        required = False,
618        )
619
620    no_copies = schema.Choice(
621        title = _(u'Number of Copies'),
622        description = u'Must correspond with the number of dispatch addresses above.',
623        values=[1, 2, 3, 4],
624        required = False,
625        readonly = False,
626        default = 1,
627        )
628
629    courier_tno = schema.TextLine(
630        title = _(u'Courier Tracking Number'),
631        required = False,
632        )
633
634    #proc_date = FormattedDate(
635    #    title = _(u'Processing Date'),
636    #    required = False,
637    #    #date_format = u'%d/%m/%Y', # Use grok-instance-wide default
638    #    show_year = True,
639    #    )
640
641    #@invariant
642    #def type_of_order(applicant):
643    #    if not applicant.collected and applicant.order != 'o':
644    #        raise Invalid(_("If you haven't collected transcript before, type of order must be 'Student Transcript'."))
645    #    if applicant.order == 'o' and applicant.charge.startswith('cert_'):
646    #        raise Invalid(_("You've selected the wrong transcript charge."))
647    #    if applicant.order == 'c' and not applicant.charge.startswith('cert_'):
648    #        raise Invalid(_("You've selected the wrong transcript charge."))
649
650
651class ICustomApplicant(ICustomUGApplicant, ICustomPGApplicant,
652                       ITranscriptApplicant):
653    """An interface for both types of applicants.
654
655    Attention: The ICustomPGApplicant field seetings will be overwritten
656    by ICustomPGApplicant field settings. If a field is defined
657    in both interfaces zope.schema validates only against the
658    constraints in ICustomUGApplicant. This does not affect the forms
659    since they are build on either ICustomUGApplicant or ICustomPGApplicant.
660    """
661
662    def writeLogMessage(view, comment):
663        """Adds an INFO message to the log file
664        """
665
666    def createStudent():
667        """Create a student object from applicant data
668        and copy applicant object.
669        """
670
671class ICustomUGApplicantEdit(ICustomUGApplicant):
672    """An undergraduate applicant interface for edit forms.
673
674    Here we can repeat the fields from base data and set the
675    `required` and `readonly` attributes to True to further restrict
676    the data access. Or we can allow only certain certificates to be
677    selected by choosing the appropriate source.
678
679    We cannot omit fields here. This has to be done in the
680    respective form page.
681    """
682
683    email = schema.ASCIILine(
684        title = _(u'Email Address'),
685        required = True,
686        constraint=validate_email,
687        )
688
689    phone = PhoneNumber(
690        title = _(u'Phone'),
691        description = u'',
692        required = True,
693        )
694
695    perm_address = schema.Text(
696        title = _(u'Permanent Address'),
697        required = True,
698        )
699
700    next_kin_name = schema.TextLine(
701        title = _(u'Next of Kin Name'),
702        required = True,
703        readonly = False,
704        )
705
706    next_kin_relation = schema.TextLine(
707        title = _(u'Next of Kin Relationship'),
708        required = True,
709        readonly = False,
710        )
711
712    next_kin_address = schema.Text(
713        title = _(u'Next of Kin Address'),
714        required = True,
715        readonly = False,
716        )
717
718    next_kin_phone = PhoneNumber(
719        title = _(u'Next of Kin Phone'),
720        required = True,
721        readonly = False,
722        )
723
724    jamb_reg_number = schema.TextLine(
725        title = _(u'JAMB Registration Number'),
726        required = True,
727        constraint=validate_jamb_reg_number,
728        )
729
730    jamb_subjects = schema.Text(
731        title = _(u'Subjects and Scores'),
732        required = True,
733        )
734
735    jamb_score = schema.Int(
736        title = _(u'Total JAMB Score'),
737        required = True,
738        )
739
740ICustomUGApplicantEdit[
741    'email'].order = ICustomUGApplicant['email'].order
742ICustomUGApplicantEdit[
743    'phone'].order = ICustomUGApplicant['phone'].order
744ICustomUGApplicantEdit[
745    'perm_address'].order = ICustomUGApplicant['perm_address'].order
746ICustomUGApplicantEdit[
747    'next_kin_name'].order = ICustomUGApplicant['next_kin_name'].order
748ICustomUGApplicantEdit[
749    'next_kin_relation'].order = ICustomUGApplicant['next_kin_relation'].order
750ICustomUGApplicantEdit[
751    'next_kin_address'].order = ICustomUGApplicant['next_kin_address'].order
752ICustomUGApplicantEdit[
753    'next_kin_phone'].order = ICustomUGApplicant['next_kin_phone'].order
754
755class ICustomPGApplicantEdit(INigeriaPGApplicantEdit):
756    """A postgraduate applicant interface for editing.
757
758    Here we can repeat the fields from base data and set the
759    `required` and `readonly` attributes to True to further restrict
760    the data access. Or we can allow only certain certificates to be
761    selected by choosing the appropriate source.
762
763    We cannot omit fields here. This has to be done in the
764    respective form page.
765    """
766
767class ICustomApplicantOnlinePayment(INigeriaApplicantOnlinePayment):
768    """An applicant payment via payment gateways.
769
770    """
771
772class IPUTMEApplicantEdit(IPUTMEApplicantEdit):
773    """An undergraduate applicant interface for editing.
774
775    Here we can repeat the fields from base data and set the
776    `required` and `readonly` attributes to True to further restrict
777    the data access. Or we can allow only certain certificates to be
778    selected by choosing the appropriate source.
779
780    We cannot omit fields here. This has to be done in the
781    respective form page.
782    """
783
784class ICustomApplicantUpdateByRegNo(INigeriaApplicantUpdateByRegNo):
785    """Representation of an applicant.
786
787    Skip regular reg_number validation if reg_number is used for finding
788    the applicant object.
789    """
Note: See TracBrowser for help on using the repository browser.