source: main/waeup.aaue/trunk/src/waeup/aaue/students/browser.py @ 13808

Last change on this file since 13808 was 13795, checked in by Henrik Bettermann, 9 years ago

Add application_number to student base data.

  • Property svn:keywords set to Id
File size: 22.7 KB
RevLine 
[8911]1## $Id: browser.py 13795 2016-04-03 06:30:33Z henrik $
2##
3## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import grok
19from zope.i18n import translate
[9914]20from zope.component import getUtility
[13523]21from zope.security import checkPermission
[13351]22from zope.formlib.textwidgets import BytesDisplayWidget
[11597]23from waeup.kofa.browser.layout import UtilityView
[8911]24from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
[9914]25from waeup.kofa.interfaces import IKofaUtils
[11597]26from waeup.kofa.students.interfaces import IStudentsUtils, IStudent
[13038]27from waeup.kofa.students.workflow import PAID, REGISTERED
[9914]28from waeup.kofa.students.browser import (
[11846]29    StartClearancePage,
[9914]30    StudentBasePDFFormPage,
31    CourseTicketAddFormPage,
32    StudyLevelDisplayFormPage,
[13059]33    ExportPDFTranscriptSlip,
34    ExportPDFAdmissionSlip,
[13353]35    BedTicketAddPage,
[13380]36    StudentFilesUploadPage,
[13523]37    PaymentsManageFormPage,
[13770]38    CourseTicketDisplayFormPage,
39    CourseTicketManageFormPage,
40    EditScoresPage
[10269]41    )
[8911]42from kofacustom.nigeria.students.browser import (
43    NigeriaOnlinePaymentDisplayFormPage,
44    NigeriaOnlinePaymentAddFormPage,
[13059]45    NigeriaExportPDFPaymentSlip,
46    NigeriaExportPDFCourseRegistrationSlip,
47    NigeriaExportPDFClearanceSlip,
[13351]48    NigeriaStudentPersonalDisplayFormPage,
49    NigeriaStudentPersonalEditFormPage,
50    NigeriaStudentPersonalManageFormPage,
[13362]51    NigeriaStudentClearanceEditFormPage,
[13462]52    NigeriaAccommodationManageFormPage,
[13795]53    NigeriaStudentBaseDisplayFormPage,
54    NigeriaStudentBaseManageFormPage
[10269]55    )
[9496]56from waeup.aaue.students.interfaces import (
[9914]57    ICustomStudentOnlinePayment,
[11607]58    ICustomStudentStudyLevel,
[13351]59    ICustomStudent,
60    ICustomStudentPersonal,
[13362]61    ICustomStudentPersonalEdit,
[13770]62    ICustomUGStudentClearance,
[13795]63    ICustomCourseTicket,
64    ICustomStudentBase)
[13414]65from waeup.aaue.interswitch.browser import gateway_net_amt
[9914]66from waeup.aaue.interfaces import MessageFactory as _
[8911]67
[13795]68class CustomStudentBaseDisplayFormPage(NigeriaStudentBaseDisplayFormPage):
69    """ Page to display student base data
70    """
71    form_fields = grok.AutoFields(ICustomStudentBase).omit(
72        'password', 'suspended', 'suspended_comment', 'flash_notice')
73    form_fields[
74        'financial_clearance_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
75
76class CustomStudentBaseManageFormPage(NigeriaStudentBaseManageFormPage):
77    """ View to manage student base data
78    """
79    form_fields = grok.AutoFields(ICustomStudentBase).omit(
80        'student_id', 'adm_code', 'suspended',
81        'financially_cleared_by', 'financial_clearance_date')
82
[13351]83class CustomStudentPersonalDisplayFormPage(NigeriaStudentPersonalDisplayFormPage):
84    """ Page to display student personal data
85    """
86    form_fields = grok.AutoFields(ICustomStudentPersonal)
87    form_fields['perm_address'].custom_widget = BytesDisplayWidget
88    form_fields['father_address'].custom_widget = BytesDisplayWidget
89    form_fields['mother_address'].custom_widget = BytesDisplayWidget
90    form_fields['next_kin_address'].custom_widget = BytesDisplayWidget
91    form_fields[
92        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
93
94class CustomStudentPersonalEditFormPage(NigeriaStudentPersonalEditFormPage):
95    """ Page to edit personal data
96    """
97    form_fields = grok.AutoFields(ICustomStudentPersonalEdit).omit('personal_updated')
98
99class CustomStudentPersonalManageFormPage(NigeriaStudentPersonalManageFormPage):
100    """ Page to edit personal data
101    """
102    form_fields = grok.AutoFields(ICustomStudentPersonal)
103    form_fields['personal_updated'].for_display = True
104    form_fields[
105        'personal_updated'].custom_widget = FriendlyDatetimeDisplayWidget('le')
106
[13362]107class CustomStudentClearanceEditFormPage(NigeriaStudentClearanceEditFormPage):
108    """ View to edit student clearance data by student
109    """
110
111    @property
112    def form_fields(self):
113        if self.context.is_postgrad:
114            form_fields = grok.AutoFields(ICustomPGStudentClearance).omit(
115            'clearance_locked', 'nysc_location', 'clr_code', 'officer_comment',
116            'physical_clearance_date')
117        else:
118            form_fields = grok.AutoFields(ICustomUGStudentClearance).omit(
119            'clearance_locked', 'clr_code', 'officer_comment',
120            'physical_clearance_date')
121        return form_fields
122
[11846]123class CustomStartClearancePage(StartClearancePage):
[13360]124    with_ac = False
[11846]125
[13351]126    @property
127    def all_required_fields_filled(self):
128        if not self.context.email:
129            return _("Email address is missing."), 'edit_base'
130        if not self.context.phone:
131            return _("Phone number is missing."), 'edit_base'
132        if not self.context.father_name:
133            return _("Personal data form is not properly filled."), 'edit_personal'
134        return
135
[8911]136class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
137    """ Page to view an online payment ticket
138    """
139    grok.context(ICustomStudentOnlinePayment)
[9853]140    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]141        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]142    form_fields[
143        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
144    form_fields[
145        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
146
147class CustomOnlinePaymentAddFormPage(NigeriaOnlinePaymentAddFormPage):
148    """ Page to add an online payment ticket
149    """
150    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).select(
151        'p_category')
152
[13523]153class CustomPaymentsManageFormPage(PaymentsManageFormPage):
154    """ Page to manage the student payments.
155
156    This manage form page is for both students and students officers.
157    """
158    @property
159    def manage_payments_allowed(self):
160        return checkPermission('waeup.manageStudent', self.context)
161
[13059]162class CustomExportPDFPaymentSlip(NigeriaExportPDFPaymentSlip):
[8911]163    """Deliver a PDF slip of the context.
164    """
165    grok.context(ICustomStudentOnlinePayment)
[9853]166    form_fields = grok.AutoFields(ICustomStudentOnlinePayment).omit(
[9990]167        'provider_amt', 'gateway_amt', 'thirdparty_amt', 'p_item')
[8911]168    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9496]169    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
[9914]170
[11625]171    @property
172    def note(self):
[13408]173        p_session = self.context.p_session
[13405]174        try:
[13408]175            academic_session = grok.getSite()['configuration'][str(p_session)]
[13405]176        except KeyError:
177            academic_session = None
[13425]178        text =  '\n\n The Amount Authorized is inclusive of: '
[13512]179        if self.context.p_category in ('schoolfee_incl', 'schoolfee_1') \
180            and academic_session:
[13414]181            welfare_fee = gateway_net_amt(academic_session.welfare_fee)
182            union_fee = gateway_net_amt(academic_session.union_fee)
[13437]183            text += ('School Fee, '
[13463]184                     '%s Naira Student Union Dues, '
[13437]185                     '%s Naira Student Welfare Assurance Fee and '
[13425]186                     % (union_fee, welfare_fee))
[13410]187        elif self.context.p_category in (
188            'clearance_incl', 'clearance_medical_incl') and academic_session:
[13414]189            matric_gown_fee = gateway_net_amt(academic_session.matric_gown_fee)
190            lapel_fee = gateway_net_amt(academic_session.lapel_fee)
[13437]191            text += ('Acceptance Fee, '
192                     '%s Naira Matriculation Gown Fee, '
193                     '%s Naira Lapel/File Fee and '
[13408]194                     % (matric_gown_fee, lapel_fee))
[13437]195        return text + '250.0 Naira Transaction Charge.'
[11625]196
[9914]197class CustomStudyLevelDisplayFormPage(StudyLevelDisplayFormPage):
198    """ Page to display student study levels
199    """
200    grok.context(ICustomStudentStudyLevel)
[10480]201    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[12876]202        'total_credits', 'gpa', 'level')
[9914]203    form_fields[
204        'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
205
[13059]206class CustomExportPDFCourseRegistrationSlip(
207    NigeriaExportPDFCourseRegistrationSlip):
[9914]208    """Deliver a PDF slip of the context.
209    """
210    grok.context(ICustomStudentStudyLevel)
211    form_fields = grok.AutoFields(ICustomStudentStudyLevel).omit(
[10102]212        'level_session', 'level_verdict',
[12876]213        'validated_by', 'validation_date', 'gpa', 'level')
[9914]214
[10269]215    omit_fields = ('password', 'suspended', 'suspended_comment',
[10689]216        'phone', 'adm_code', 'sex', 'email', 'date_of_birth',
[13713]217        'department', 'current_mode', 'current_level', 'flash_notice')
[10269]218
[13038]219    def update(self):
220        if self.context.student.state != REGISTERED \
[13051]221            and self.context.student.current_level == self.context.level:
[13038]222            self.flash(_('Forbidden'), type="warning")
223            self.redirect(self.url(self.context))
224
[9914]225    @property
226    def label(self):
227        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
228        lang = self.request.cookies.get('kofa.language', portal_language)
229        level_title = translate(self.context.level_title, 'waeup.kofa',
230            target_language=lang)
231        line0 = ''
[13788]232        if self.context.student.is_postgrad:
233            line0 = 'SCHOOL OF POSTGRADUATE STUDIES\n'
234        elif self.context.student.current_mode.endswith('_pt'):
[9914]235            line0 = 'DIRECTORATE OF PART-TIME DEGREE PROGRAMMES\n'
236        line1 = translate(_('Course Registration Slip'),
237            'waeup.kofa', target_language=portal_language) \
238            + ' %s' % level_title
239        line2 = translate(_('Session'),
240            'waeup.kofa', target_language=portal_language) \
241            + ' %s' % self.context.getSessionString
242        return '%s%s\n%s' % (line0, line1, line2)
243
244    @property
245    def title(self):
246        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
247        return translate(_('Units Registered'), 'waeup.kofa',
248            target_language=portal_language)
249
250    def _signatures(self):
[13647]251        if self.context.student.current_mode.endswith('_pt') \
252            or self.context.student.current_mode == 'found':
253            return (
254                [('I have selected the course on the advise of my Head of '
255                 'Department. <br>', _('Student\'s Signature'), '<br>')],
256                [('This student has satisfied the department\'s requirements. '
257                 'I recommend to approve the course registration. <br>',
258                 _('Head of Department\'s Signature'), '<br>')],
259                [('' , _('Principal Assistant Registrar\'s Signature'), '<br>')],
260                [('', _('Director\'s Signature'))]
261                )
262        if self.context.student.current_mode in (
263            'de_ft', 'ug_ft', 'dp_ft', 'transfer'):
[13649]264            return ([_('Academic Adviser\'s Signature'),
265                _('Faculty Officer\'s Signature'),
266                _('Student\'s Signature')],)
267
[13647]268        if self.context.student.current_mode in ('special_pg_ft', 'special_pg_pt'):
269            return (
[13676]270                [('I declare that all items of information supplied above are correct:' ,
[13680]271                    _('Student\'s Signature'), '<br>')],
[13676]272                [('We approved the above registration:',
[13680]273                    _('Major Supervisor (Name / Signature)'), '')],
274                [('', _('Co-Supervisor (Name / Signature)'), '')],
[13676]275                [('', _('Head of Department'), '<br>')],
276                [('The student has satisfied the conditions for renewal of '
277                  'registration for graduate school programme in this university:',
[13680]278                  _('Secretary <br /> (School of Postgraduate Studies)'), '')],
279                [('', _('Dean <br /> (School of Postgraduate Studies)'), '')],
[13647]280                )
281        return None
[9914]282
[13647]283
[9914]284    def render(self):
285        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
286        Sem = translate('Sem.', 'waeup.kofa', target_language=portal_language)
287        Code = translate('Code', 'waeup.kofa', target_language=portal_language)
288        Title = translate('Title', 'waeup.kofa', target_language=portal_language)
289        Cred = translate('Cred.', 'waeup.kofa', target_language=portal_language)
290        Score = translate('Score', 'waeup.kofa', target_language=portal_language)
291        Grade = translate('Grade', 'waeup.kofa', target_language=portal_language)
[10269]292        Signature = translate(_('Lecturer\'s Signature'), 'waeup.aaue',
[9914]293            target_language=portal_language)
294        studentview = StudentBasePDFFormPage(self.context.student,
295            self.request, self.omit_fields)
296        students_utils = getUtility(IStudentsUtils)
[10442]297
298        tabledata = []
299        tableheader = []
300        contenttitle = []
301        for i in range(1,7):
302            tabledata.append(sorted(
303                [value for value in self.context.values() if value.semester == i],
304                key=lambda value: str(value.semester) + value.code))
305            tableheader.append([(Code,'code', 2.0),
306                               (Title,'title', 7),
307                               (Cred, 'credits', 1.5),
308                               (Score, 'score', 1.4),
309                               (Grade, 'grade', 1.4),
310                               (Signature, 'dummy', 3),
311                               ])
[9914]312        if len(self.label.split('\n')) == 3:
313            topMargin = 1.9
314        elif len(self.label.split('\n')) == 2:
315            topMargin = 1.7
316        else:
317            topMargin = 1.5
318        return students_utils.renderPDF(
319            self, 'course_registration_slip.pdf',
320            self.context.student, studentview,
[10442]321            tableheader=tableheader,
322            tabledata=tabledata,
[9914]323            signatures=self._signatures(),
[10269]324            topMargin=topMargin,
325            omit_fields=self.omit_fields
[9914]326            )
[10566]327
[13059]328class CustomExportPDFTranscriptSlip(ExportPDFTranscriptSlip):
[10566]329    """Deliver a PDF slip of the context.
330    """
331
332    def _sigsInFooter(self):
333        return []
334
335    def _signatures(self):
336        return ([(
[11555]337            'Akhimien Felicia O. (MANUPA) <br /> Principal Assistant Registrar  <br /> '
338            'Exams, Records and Data Processing Division <br /> For: Registrar')],)
[10922]339
[13059]340class CustomExportPDFAdmissionSlip(ExportPDFAdmissionSlip):
[10922]341    """Deliver a PDF Admission slip.
342    """
343
344    @property
345    def label(self):
346        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
347        return translate(_('e-Admission Slip \n'),
348            'waeup.kofa', target_language=portal_language) \
349            + ' %s' % self.context.display_fullname
[11597]350
[13059]351class CustomExportPDFClearanceSlip(NigeriaExportPDFClearanceSlip):
[11606]352    """Deliver a PDF slip of the context.
353    """
354
355    @property
356    def label(self):
357        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
358        return translate(_('Clearance Slip\n'),
359            'waeup.kofa', target_language=portal_language) \
360            + ' %s' % self.context.display_fullname
361
[11597]362class StudentGetMatricNumberPage(UtilityView, grok.View):
363    """ Construct and set the matriculation number.
364    """
365    grok.context(IStudent)
366    grok.name('get_matric_number')
367    grok.require('waeup.viewStudent')
368
369    def update(self):
370        students_utils = getUtility(IStudentsUtils)
371        msg, mnumber = students_utils.setMatricNumber(self.context)
372        if msg:
373            self.flash(msg, type="danger")
374        else:
375            self.flash(_('Matriculation number %s assigned.' % mnumber))
[11602]376            self.context.writeLogMessage(self, '%s assigned' % mnumber)
[11597]377        self.redirect(self.url(self.context))
378        return
379
380    def render(self):
[11607]381        return
382
[13059]383class ExportPDFMatricNumberSlip(UtilityView, grok.View):
[11607]384    """Deliver a PDF notification slip.
385    """
386    grok.context(ICustomStudent)
387    grok.name('matric_number_slip.pdf')
388    grok.require('waeup.viewStudent')
389    prefix = 'form'
390
391    form_fields = grok.AutoFields(ICustomStudent).select(
392        'student_id', 'matric_number')
[13713]393    omit_fields = ('date_of_birth', 'current_level', 'flash_notice')
[11607]394
395    @property
[13489]396    def title(self):
397        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
398        return translate(_('Matriculation Number'), 'waeup.kofa',
399            target_language=portal_language)
400
401    @property
[11607]402    def label(self):
403        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
404        return translate(_('Matriculation Number Slip\n'),
405            'waeup.kofa', target_language=portal_language) \
406            + ' %s' % self.context.display_fullname
407
408    def render(self):
409        if self.context.state not in (PAID,) or not self.context.is_fresh \
410            or not self.context.matric_number:
411            self.flash('Not allowed.', type="danger")
412            self.redirect(self.url(self.context))
413            return
414        students_utils = getUtility(IStudentsUtils)
[11609]415        pre_text = _('Congratulations! Your acceptance fee and school fees ' +
416                     'payments have been received and your matriculation ' +
417                     'number generated with details as follows.')
[11607]418        return students_utils.renderPDFAdmissionLetter(self,
419            self.context.student, omit_fields=self.omit_fields,
[13353]420            pre_text=pre_text, post_text='')
421
[13489]422class ExportPersonalDataSlip(UtilityView, grok.View):
423    """Deliver a PDF notification slip.
424    """
425    grok.context(ICustomStudent)
426    grok.name('personal_data_slip.pdf')
427    grok.require('waeup.viewStudent')
428    prefix = 'form'
429    note = None
430
431    form_fields = grok.AutoFields(ICustomStudentPersonal).omit('personal_updated')
[13713]432    omit_fields = ('suspended', 'suspended_comment', 'adm_code',
433                   'certificate', 'flash_notice')
[13489]434
435    @property
436    def title(self):
437        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
438        return translate(_('Personal Data'), 'waeup.kofa',
439            target_language=portal_language)
440
441    @property
442    def label(self):
443        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
444        return translate(_('Personal Data Slip\n'),
445            'waeup.kofa', target_language=portal_language) \
446            + ' %s' % self.context.display_fullname
447
448    def render(self):
449        studentview = StudentBasePDFFormPage(self.context.student,
450            self.request, self.omit_fields)
451        students_utils = getUtility(IStudentsUtils)
452        return students_utils.renderPDF(self, 'personal_data_slip.pdf',
453            self.context.student, studentview, note=self.note,
454            omit_fields=self.omit_fields)
455
[13462]456class CustomAccommodationManageFormPage(NigeriaAccommodationManageFormPage):
457    """ Page to manage bed tickets.
458    This manage form page is for both students and students officers.
459    """
460    with_hostel_selection = True
461
[13353]462class CustomBedTicketAddPage(BedTicketAddPage):
[13360]463    with_ac = False
[13380]464
465class CustomStudentFilesUploadPage(StudentFilesUploadPage):
466    """ View to upload files by student. Inherit from same class in
467    base package, not from kofacustom.nigeria which
468    requires that no application slip exists.
[13770]469    """
470
471class CustomCourseTicketDisplayFormPage(CourseTicketDisplayFormPage):
472    """ Page to display course tickets
473    """
474    form_fields = grok.AutoFields(ICustomCourseTicket)
475
476class CustomCourseTicketManageFormPage(CourseTicketManageFormPage):
477    """ Page to manage course tickets
478    """
479    form_fields = grok.AutoFields(ICustomCourseTicket)
480    form_fields['title'].for_display = True
481    form_fields['fcode'].for_display = True
482    form_fields['dcode'].for_display = True
483    form_fields['semester'].for_display = True
484    form_fields['passmark'].for_display = True
485    form_fields['credits'].for_display = True
486    form_fields['mandatory'].for_display = False
487    form_fields['automatic'].for_display = True
488    form_fields['carry_over'].for_display = True
489
490class CustomEditScoresPage(EditScoresPage):
491    """Page that filters and lists students.
492    """
493    grok.template('editscorespage')
494
495    def update(self,  *args, **kw):
496        form = self.request.form
497        ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
498        self.current_academic_session = grok.getSite()[
499            'configuration'].current_academic_session
500        if self.context.__parent__.__parent__.score_editing_disabled:
501            self.flash(_('Score editing disabled.'), type="warning")
502            self.redirect(self.url(self.context))
503            return
504        if not self.current_academic_session:
505            self.flash(_('Current academic session not set.'), type="warning")
506            self.redirect(self.url(self.context))
507            return
508        self.tickets = self._searchCatalog(self.current_academic_session)
509        editable_tickets = [
510            ticket for ticket in self.tickets if ticket.editable_by_lecturer]
511        if not self.tickets:
512            self.flash(_('No student found.'), type="warning")
513            self.redirect(self.url(self.context))
514            return
515        if 'UPDATE' in form:
516            tno = 0
517            error = ''
518            if not editable_tickets:
519                return
520            scores = form['scores']
521            cas = form['cas']
522            if isinstance(scores, basestring):
523                scores = [scores]
524            if isinstance(cas, basestring):
525                cas = [cas]
526            for ticket in editable_tickets:
527                ticket_error = False
528                score = ticket.score
529                ca = ticket.ca
530                if scores[tno] == '':
531                    score = None
532                if cas[tno] == '':
533                    ca = None
534                try:
535                    if scores[tno]:
536                        score = int(scores[tno])
537                    if cas[tno]:
538                        ca = int(cas[tno])
539                except ValueError:
540                    error += '%s, ' % ticket.student.display_fullname
541                    ticket_error = True
542                if not ticket_error and ticket.score != score:
543                    ticket.score = score
544                    ticket.student.__parent__.logger.info(
545                        '%s - %s %s/%s score updated (%s)' %
546                        (ob_class, ticket.student.student_id,
547                         ticket.level, ticket.code, score))
548                if not ticket_error and ticket.ca != ca:
549                    ticket.ca = ca
550                    ticket.student.__parent__.logger.info(
551                        '%s - %s %s/%s ca updated (%s)' %
552                        (ob_class, ticket.student.student_id,
553                         ticket.level, ticket.code, ca))
554                tno += 1
555            if error:
556                self.flash(_('Error: Score(s) and CA(s) of %s have not be updated. '
557                  'Only integers are allowed.' % error.strip(', ')),
558                  type="danger")
559        return
Note: See TracBrowser for help on using the repository browser.