Ignore:
Timestamp:
20 Sep 2012, 08:49:37 (12 years ago)
Author:
uli
Message:

Merge changes from update branch (includes trunk changes until r9107).

Location:
main/waeup.kofa/branches/uli-zc-async
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.kofa/branches/uli-zc-async

  • main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/utils.py

    r8708 r9209  
    1919"""
    2020import grok
    21 from random import SystemRandom as r
    2221from time import time
    23 from datetime import datetime
    24 from zope.i18n import translate
    25 from zope.component import getUtility, createObject
    26 from reportlab.pdfgen import canvas
    2722from reportlab.lib import colors
    2823from reportlab.lib.units import cm
    29 from reportlab.lib.enums import TA_RIGHT
    3024from reportlab.lib.pagesizes import A4
    31 from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
    32 from reportlab.platypus import (Frame, Paragraph, Image, PageBreak, Table,
    33                                 Spacer)
    34 from reportlab.platypus.tables import TableStyle
    35 from reportlab.platypus.flowables import PageBreak
    36 from zope.component import getUtility
     25from reportlab.lib.styles import getSampleStyleSheet
     26from reportlab.platypus import Paragraph, Image, Table, Spacer
     27from zope.component import getUtility, createObject
    3728from zope.formlib.form import setUpEditWidgets
    38 
     29from zope.i18n import translate
    3930from waeup.kofa.interfaces import (
    4031    IExtFileStore, IKofaUtils, RETURNING, PAID, CLEARED)
    4132from waeup.kofa.interfaces import MessageFactory as _
    4233from waeup.kofa.students.interfaces import IStudentsUtils
    43 from waeup.kofa.utils.helpers import now
    4434
    4535SLIP_STYLE = [
     
    127117    #data.append([Spacer(1, 12)])
    128118    portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
     119
     120    f_label = formatted_label(size=12) % _('Name')
     121    f_label = Paragraph(f_label, style["Normal"])
     122    f_text = formatted_text(studentview.context.display_fullname, size=12)
     123    f_text = Paragraph(f_text, style["Normal"])
     124    data_right.append([f_label,f_text])
     125
    129126    for widget in studentview.widgets:
    130         if widget.name == 'form.adm_code':
     127        if 'name' in widget.name:
    131128            continue
    132129        f_label = formatted_label(size=12) % translate(
     
    137134        f_text = Paragraph(f_text, style["Normal"])
    138135        data_right.append([f_label,f_text])
     136
     137    if hasattr(studentview.context, 'certcode'):
     138        f_label = formatted_label(size=12) % _('Study Course')
     139        f_label = Paragraph(f_label, style["Normal"])
     140        f_text = formatted_text(
     141            studentview.context['studycourse'].certificate.longtitle(), size=12)
     142        f_text = Paragraph(f_text, style["Normal"])
     143        data_right.append([f_label,f_text])
     144
     145        f_label = formatted_label(size=12) % _('Department')
     146        f_label = Paragraph(f_label, style["Normal"])
     147        f_text = formatted_text(
     148            studentview.context[
     149            'studycourse'].certificate.__parent__.__parent__.longtitle(),
     150            size=12)
     151        f_text = Paragraph(f_text, style["Normal"])
     152        data_right.append([f_label,f_text])
     153
     154        f_label = formatted_label(size=12) % _('Faculty')
     155        f_label = Paragraph(f_label, style["Normal"])
     156        f_text = formatted_text(
     157            studentview.context[
     158            'studycourse'].certificate.__parent__.__parent__.__parent__.longtitle(),
     159            size=12)
     160        f_text = Paragraph(f_text, style["Normal"])
     161        data_right.append([f_label,f_text])
     162
    139163    table_left = Table(data_left,style=SLIP_STYLE)
    140164    table_right = Table(data_right,style=SLIP_STYLE, colWidths=[5*cm, 6*cm])
     
    165189    return table
    166190
     191def get_signature_table(signatures, lang='en'):
     192    """Return a reportlab table containing signature fields (with date).
     193    """
     194    style = getSampleStyleSheet()
     195    space_width = 0.4  # width in cm of space between signatures
     196    table_width = 16.0 # supposed width of signature table in cms
     197    # width of signature cells in cm...
     198    sig_col_width = table_width - ((len(signatures) - 1) * space_width)
     199    sig_col_width = sig_col_width / len(signatures)
     200    data = []
     201    col_widths = [] # widths of columns
     202
     203    sig_style = [
     204        ('VALIGN',(0,-1),(-1,-1),'TOP'),
     205        ('FONT', (0,0), (-1,-1), 'Helvetica-BoldOblique', 12),
     206        ('BOTTOMPADDING', (0,0), (-1,0), 36),
     207        ('TOPPADDING', (0,-1), (-1,-1), 0),
     208        ]
     209    for num, elem in enumerate(signatures):
     210        # draw a line above each signature cell (not: empty cells in between)
     211        sig_style.append(
     212            ('LINEABOVE', (num*2,-1), (num*2, -1), 1, colors.black))
     213
     214    row = []
     215    for signature in signatures:
     216        row.append(trans(_('Date:'), lang))
     217        row.append('')
     218        if len(signatures) > 1:
     219            col_widths.extend([sig_col_width*cm, space_width*cm])
     220        else:
     221            col_widths.extend([sig_col_width/2*cm, sig_col_width/2*cm])
     222            row.append('') # empty spaceholder on right
     223    data.append(row[:-1])
     224    data.extend(([''],)*3) # insert 3 empty rows...
     225    row = []
     226    for signature in signatures:
     227        row.append(Paragraph(trans(signature, lang), style["Normal"]))
     228        row.append('')
     229    data.append(row[:-1])
     230    table = Table(data, style=sig_style, repeatRows=len(data),
     231                  colWidths=col_widths)
     232    return table
     233
    167234def docs_as_flowables(view, lang='en'):
    168235    """Create reportlab flowables out of scanned docs.
     
    190257            if img_path is None:
    191258                pass
    192             elif not img_path.endswith('.jpg'):
     259            elif not img_path[-4:] in ('.jpg', '.JPG'):
    193260                # reportlab requires jpg images, I think.
    194                 f_text = Paragraph('%s (Not displayable)' % (
     261                f_text = Paragraph('%s (not displayable)' % (
    195262                    viewlet.title,), ENTRY1_STYLE)
    196263            else:
     
    202269    return data
    203270
    204 def insert_footer(pdf,width,style,text=None, number_of_pages=1):
    205       """Render the whole footer frame.
    206       """
    207       story = []
    208       frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
    209       tz = getUtility(IKofaUtils).tzinfo
    210       timestamp = now(tz).strftime("%d/%m/%Y %H:%M:%S %Z")
    211       left_text = '<font size=10>%s</font>' % timestamp
    212       story.append(Paragraph(left_text, style["Normal"]))
    213       frame_footer.addFromList(story,pdf)
    214       story = []
    215       frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
    216       portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
    217       right_text = translate(_('<font size=10>${a} Page ${b} of ${c}</font>',
    218           mapping = {'a':text, 'b':pdf.getPageNumber(), 'c':number_of_pages}),
    219           'waeup.kofa', target_language=portal_language)
    220       story.append(Paragraph(right_text, style["Right"]))
    221       frame_footer.addFromList(story,pdf)
    222 
    223271class StudentsUtils(grok.GlobalUtility):
    224272    """A collection of methods subject to customization.
     
    227275
    228276    def getReturningData(self, student):
    229         """ This method defines what happens after school fee payment
     277        """ Define what happens after school fee payment
    230278        depending on the student's senate verdict.
    231279
     
    238286
    239287    def setReturningData(self, student):
    240         """ This method defines what happens after school fee payment
    241         depending on the student's senate verdict. It folllows
    242         the same algorithm as getReturningData but it also sets the new
    243         values
    244 
    245         In the base configuration current level is always increased
    246         by 100 no matter which verdict has been assigned.
     288        """ Define what happens after school fee payment
     289        depending on the student's senate verdict.
     290
     291        This method folllows the same algorithm as getReturningData but
     292        it also sets the new values.
    247293        """
    248294        new_session, new_level = self.getReturningData(student)
     
    250296        student['studycourse'].current_session = new_session
    251297        verdict = student['studycourse'].current_verdict
    252         student['studycourse'].current_verdict = 'NY'
     298        student['studycourse'].current_verdict = '0'
    253299        student['studycourse'].previous_verdict = verdict
    254300        return
    255301
    256     def setPaymentDetails(self, category, student):
     302    def setPaymentDetails(self, category, student,
     303            previous_session, previous_level):
    257304        """Create Payment object and set the payment data of a student for
    258305        the payment category specified.
    259306
    260307        """
    261         details = {}
    262308        p_item = u''
    263309        amount = 0.0
    264         error = u''
    265         p_session = student['studycourse'].current_session
    266         p_level = student['studycourse'].current_level
     310        if previous_session:
     311            p_session = previous_session
     312            p_level = previous_level
     313            p_current = False
     314        else:
     315            p_session = student['studycourse'].current_session
     316            p_level = student['studycourse'].current_level
     317            p_current = True
    267318        session = str(p_session)
    268319        try:
     
    276327            except (AttributeError, TypeError):
    277328                return _('Study course data are incomplete.'), None
    278             if student.state == CLEARED:
    279                 amount = getattr(certificate, 'school_fee_1', 0.0)
    280             elif student.state == RETURNING:
    281                 # In case of returning school fee payment the payment session
    282                 # and level contain the values of the session the student
    283                 # has paid for.
    284                 p_session, p_level = self.getReturningData(student)
    285                 amount = getattr(certificate, 'school_fee_2', 0.0)
    286             elif student.is_postgrad and student.state == PAID:
    287                 # Returning postgraduate students also pay for the next session
    288                 # but their level always remains the same.
    289                 p_session += 1
    290                 amount = getattr(certificate, 'school_fee_2', 0.0)
     329            if previous_session:
     330                if previous_session < student['studycourse'].entry_session:
     331                    return _('The previous session must not fall below '
     332                             'your entry session.'), None
     333                if previous_session > student['studycourse'].current_session - 1:
     334                    return _('This is not a previous session.'), None
     335                if previous_level == 100:
     336                    amount = getattr(certificate, 'school_fee_1', 0.0)
     337                else:
     338                    amount = getattr(certificate, 'school_fee_2', 0.0)
     339            else:
     340                if student.state == CLEARED:
     341                    amount = getattr(certificate, 'school_fee_1', 0.0)
     342                elif student.state == RETURNING:
     343                    # In case of returning school fee payment the payment session
     344                    # and level contain the values of the session the student
     345                    # has paid for.
     346                    p_session, p_level = self.getReturningData(student)
     347                    amount = getattr(certificate, 'school_fee_2', 0.0)
     348                elif student.is_postgrad and student.state == PAID:
     349                    # Returning postgraduate students also pay for the next session
     350                    # but their level always remains the same.
     351                    p_session += 1
     352                    amount = getattr(certificate, 'school_fee_2', 0.0)
    291353        elif category == 'clearance':
    292             p_item = student['studycourse'].certificate.code
     354            try:
     355                p_item = student['studycourse'].certificate.code
     356            except (AttributeError, TypeError):
     357                return _('Study course data are incomplete.'), None
    293358            amount = academic_session.clearance_fee
    294359        elif category == 'bed_allocation':
     
    296361            amount = academic_session.booking_fee
    297362        if amount in (0.0, None):
    298             return _(u'Amount could not be determined.'), None
     363            return _('Amount could not be determined.' +
     364                     ' Would you like to pay for a previous session?'), None
    299365        for key in student['payments'].keys():
    300366            ticket = student['payments'][key]
     
    303369               ticket.p_item == p_item and \
    304370               ticket.p_session == p_session:
    305                   return _('This type of payment has already been made.'), None
     371                  return _('This type of payment has already been made.' +
     372                           ' Would you like to pay for a previous session?'), None
    306373        payment = createObject(u'waeup.StudentOnlinePayment')
    307         timestamp = "%d" % int(time()*1000)
     374        timestamp = ("%d" % int(time()*10000))[1:]
    308375        payment.p_id = "p%s" % timestamp
    309376        payment.p_category = category
     
    311378        payment.p_session = p_session
    312379        payment.p_level = p_level
     380        payment.p_current = p_current
    313381        payment.amount_auth = amount
    314382        return None, payment
     
    330398        entry_session = studycourse.entry_session
    331399        current_level = studycourse.current_level
    332         if not (entry_session and current_level and certificate):
    333             return
     400        if None in (entry_session, current_level, certificate):
     401            return d
    334402        end_level = certificate.end_level
    335         if entry_session == grok.getSite()['hostels'].accommodation_session:
     403        if current_level == 10:
     404            bt = 'pr'
     405        elif entry_session == grok.getSite()['hostels'].accommodation_session:
    336406            bt = 'fr'
    337407        elif current_level >= end_level:
     
    355425        return available_beds[0]
    356426
     427    def renderPDFAdmissionLetter(self, view, student=None):
     428        """Render pdf admission letter.
     429        """
     430        # XXX: we have to fix the import problems here.
     431        from waeup.kofa.browser.interfaces import IPDFCreator
     432        from waeup.kofa.browser.pdf import format_html, NOTE_STYLE
     433        if student is None:
     434            return
     435        style = getSampleStyleSheet()
     436        creator = getUtility(IPDFCreator)
     437        data = []
     438        doc_title = view.label
     439        author = '%s (%s)' % (view.request.principal.title,
     440                              view.request.principal.id)
     441        footer_text = view.label
     442        if getattr(student, 'student_id', None) is not None:
     443            footer_text = "%s - %s - " % (student.student_id, footer_text)
     444
     445        # Admission text
     446        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
     447        inst_name = grok.getSite()['configuration'].name
     448        text = trans(_(
     449            'This is to inform you that you have been provisionally'
     450            ' admitted into ${a} as follows:', mapping = {'a': inst_name}),
     451            portal_language)
     452        html = format_html(text)
     453        data.append(Paragraph(html, NOTE_STYLE))
     454        data.append(Spacer(1, 20))
     455
     456        # Student data
     457        data.append(render_student_data(view))
     458
     459        # Insert history
     460        data.append(Spacer(1, 20))
     461        datelist = student.history.messages[0].split()[0].split('-')
     462        creation_date = u'%s/%s/%s' % (datelist[2], datelist[1], datelist[0])
     463        text = trans(_(
     464            'Your Kofa student record was created on ${a}.',
     465            mapping = {'a': creation_date}),
     466            portal_language)
     467        html = format_html(text)
     468        data.append(Paragraph(html, NOTE_STYLE))
     469
     470        # Create pdf stream
     471        view.response.setHeader(
     472            'Content-Type', 'application/pdf')
     473        pdf_stream = creator.create_pdf(
     474            data, None, doc_title, author=author, footer=footer_text,
     475            note=None)
     476        return pdf_stream
     477
    357478    def renderPDF(self, view, filename='slip.pdf', student=None,
    358479                  studentview=None, tableheader=None, tabledata=None,
    359                   note=None):
     480                  note=None, signatures=None):
    360481        """Render pdf slips for various pages.
    361482        """
     
    373494            footer_text = "%s - %s - " % (student.student_id, footer_text)
    374495
     496        # Insert history
     497        if not filename.startswith('payment'):
     498            data.extend(creator.fromStringList(student.history.messages))
     499
    375500        # Insert student data table
    376501        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
     
    381506
    382507        # Insert widgets
    383         data.append(Paragraph(view.title, style["Heading3"]))
    384         portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
    385         separators = getattr(self, 'SEPARATORS_DICT', {})
    386         table = creator.getWidgetsTable(
    387             view.form_fields, view.context, None, lang=portal_language,
    388             separators=separators)
    389         data.append(table)
     508        if view.form_fields:
     509            data.append(Paragraph(view.title, style["Heading3"]))
     510            portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
     511            separators = getattr(self, 'SEPARATORS_DICT', {})
     512            table = creator.getWidgetsTable(
     513                view.form_fields, view.context, None, lang=portal_language,
     514                separators=separators)
     515            data.append(table)
    390516
    391517        # Insert scanned docs
     
    399525            contenttable = render_table_data(tableheader,tabledata)
    400526            data.append(contenttable)
     527
     528        # Insert signatures
     529        if signatures:
     530            data.append(Spacer(1, 20))
     531            signaturetable = get_signature_table(signatures)
     532            data.append(signaturetable)
    401533
    402534        view.response.setHeader(
     
    412544
    413545    VERDICTS_DICT = {
    414         'NY': _('(not yet)'),
     546        '0': _('(not yet)'),
    415547        'A': 'Successful student',
    416548        'B': 'Student with carryover courses',
Note: See TracChangeset for help on using the changeset viewer.