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

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

Revert last changes. Translate 'Term'.

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