## $Id: utils.py 7993 2012-03-27 20:59:30Z henrik $
##
## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
"""General helper functions and utilities for the student section.
"""
import grok
from random import SystemRandom as r
from datetime import datetime
from zope.i18n import translate
from zope.component import getUtility
from reportlab.pdfgen import canvas
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_RIGHT
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import (Frame, Paragraph, Image,
Table, Spacer)
from reportlab.platypus.tables import TableStyle
from reportlab.platypus.flowables import PageBreak
from zope.component import getUtility
from zope.formlib.form import setUpEditWidgets
from waeup.kofa.interfaces import IExtFileStore, IKofaUtils
from waeup.kofa.interfaces import MessageFactory as _
from waeup.kofa.students.interfaces import IStudentsUtils
SLIP_STYLE = [
('VALIGN',(0,0),(-1,-1),'TOP'),
#('FONT', (0,0), (-1,-1), 'Helvetica', 11),
]
CONTENT_STYLE = [
('VALIGN',(0,0),(-1,-1),'TOP'),
#('FONT', (0,0), (-1,-1), 'Helvetica', 8),
#('TEXTCOLOR',(0,0),(-1,0),colors.white),
('BACKGROUND',(0,0),(-1,0),colors.black),
]
FONT_SIZE = 10
FONT_COLOR = 'black'
def formatted_label(color=FONT_COLOR, size=FONT_SIZE):
tag1 ='' % (color, size)
return tag1 + '%s:'
def formatted_text(text, color=FONT_COLOR, size=FONT_SIZE):
"""Turn `text`, `color` and `size` into an HTML snippet.
The snippet is suitable for use with reportlab and generating PDFs.
Wraps the `text` into a ```` tag with passed attributes.
Also non-strings are converted. Raw strings are expected to be
utf-8 encoded (usually the case for widgets etc.).
Finally, a br tag is added if widgets contain div tags
which are not supported by reportlab.
The returned snippet is unicode type.
"""
if not isinstance(text, unicode):
if isinstance(text, basestring):
text = text.decode('utf-8')
else:
text = unicode(text)
text = text.replace('', '
')
tag1 = u'' % (color, size)
return tag1 + u'%s' % text
def generate_student_id(students,letter):
if letter == '?':
letter= r().choice('ABCDEFGHKLMNPQRSTUVWXY')
sid = u"%c%d" % (letter,r().randint(99999,1000000))
while sid in students.keys():
sid = u"%c%d" % (letter,r().randint(99999,1000000))
return sid
def set_up_widgets(view, ignore_request=False):
view.adapters = {}
view.widgets = setUpEditWidgets(
view.form_fields, view.prefix, view.context, view.request,
adapters=view.adapters, for_display=True,
ignore_request=ignore_request
)
def render_student_data(studentview):
"""Render student table for an existing frame.
"""
width, height = A4
set_up_widgets(studentview, ignore_request=True)
data_left = []
data_right = []
style = getSampleStyleSheet()
img = getUtility(IExtFileStore).getFileByContext(
studentview.context, attr='passport.jpg')
if img is None:
from waeup.kofa.browser import DEFAULT_PASSPORT_IMAGE_PATH
img = open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb')
doc_img = Image(img.name, width=4*cm, height=4*cm, kind='bound')
data_left.append([doc_img])
#data.append([Spacer(1, 12)])
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
for widget in studentview.widgets:
if widget.name == 'form.adm_code':
continue
f_label = formatted_label(size=12) % translate(
widget.label.strip(), 'waeup.kofa',
target_language=portal_language)
f_label = Paragraph(f_label, style["Normal"])
f_text = formatted_text(widget(), size=12)
f_text = Paragraph(f_text, style["Normal"])
data_right.append([f_label,f_text])
table_left = Table(data_left,style=SLIP_STYLE)
table_right = Table(data_right,style=SLIP_STYLE, colWidths=[6*cm, 8*cm])
table = Table([[table_left, table_right],],style=SLIP_STYLE)
return table
def render_table_data(tableheader,tabledata):
"""Render children table for an existing frame.
"""
data = []
#data.append([Spacer(1, 12)])
line = []
style = getSampleStyleSheet()
for element in tableheader:
field = formatted_text(element[0], color='white')
field = Paragraph(field, style["Normal"])
line.append(field)
data.append(line)
for ticket in tabledata:
line = []
for element in tableheader:
field = formatted_text(getattr(ticket,element[1],u' '))
field = Paragraph(field, style["Normal"])
line.append(field)
data.append(line)
table = Table(data,colWidths=[
element[2]*cm for element in tableheader], style=CONTENT_STYLE)
return table
def insert_footer(pdf,width,style,text=None, number_of_pages=1):
"""Render the whole footer frame.
"""
story = []
frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
left_text = '%s' % timestamp
story.append(Paragraph(left_text, style["Normal"]))
frame_footer.addFromList(story,pdf)
story = []
frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
right_text = translate(_('${a} Page ${b} of ${c}',
mapping = {'a':text, 'b':pdf.getPageNumber(), 'c':number_of_pages}),
'waeup.kofa', target_language=portal_language)
story.append(Paragraph(right_text, style["Right"]))
frame_footer.addFromList(story,pdf)
class StudentsUtils(grok.GlobalUtility):
"""A collection of methods subject to customization.
"""
grok.implements(IStudentsUtils)
def setReturningData(self, student):
""" This method defines what happens after school fee payment
depending on the student's senate verdict.
In the base configuration current level is always increased
by 100 no matter which verdict has been assigned.
"""
student['studycourse'].current_level += 100
student['studycourse'].current_session += 1
verdict = student['studycourse'].current_verdict
student['studycourse'].current_verdict = '0'
student['studycourse'].previous_verdict = verdict
return
def getPaymentDetails(self, category, student):
"""Get the payment dates of a student for the payment category
specified.
"""
d = {}
d['p_item'] = u''
d['amount'] = 0.0
d['error'] = u''
d['p_session'] = student['studycourse'].current_session
session = str(d['p_session'])
try:
academic_session = grok.getSite()['configuration'][session]
except KeyError:
d['error'] = u'Session configuration object is not available.'
return d
d['surcharge_1'] = academic_session.surcharge_1
d['surcharge_2'] = academic_session.surcharge_2
d['surcharge_3'] = academic_session.surcharge_3
if category == 'schoolfee':
d['amount'] = academic_session.school_fee_base
d['p_item'] = student['studycourse'].certificate.code
elif category == 'clearance':
d['p_item'] = student['studycourse'].certificate.code
d['amount'] = academic_session.clearance_fee
d['surcharge_1'] = 0.0 # no portal fee
elif category == 'bed_allocation':
d['p_item'] = self.getAccommodationDetails(student)['bt']
d['amount'] = academic_session.booking_fee
d['surcharge_1'] = 0.0 # no portal fee
return d
def getAccommodationDetails(self, student):
"""Determine the accommodation dates of a student.
"""
d = {}
d['error'] = u''
site_configuration = grok.getSite()['configuration']
d['booking_session'] = site_configuration.accommodation_session
d['allowed_states'] = site_configuration.accommodation_states
# Determine bed type
studycourse = student['studycourse']
certificate = getattr(studycourse,'certificate',None)
entry_session = studycourse.entry_session
current_level = studycourse.current_level
if not (entry_session and current_level and certificate):
return
end_level = certificate.end_level
if entry_session == grok.getSite()['configuration'].accommodation_session:
bt = 'fr'
elif current_level >= end_level:
bt = 'fi'
else:
bt = 're'
if student.sex == 'f':
sex = 'female'
else:
sex = 'male'
special_handling = 'regular'
d['bt'] = u'%s_%s_%s' % (special_handling,sex,bt)
return d
def selectBed(self, available_beds):
"""Select a bed from a list of available beds.
In the base configuration we select the first bed found,
but can also randomize the selection if we like.
"""
return available_beds[0]
def renderPDF(self, view, filename='slip.pdf',
student=None, studentview=None, tableheader=None, tabledata=None):
"""Render pdf slips for various pages.
"""
# (0,0),(-1,-1) = whole table
# (0,0),(0,-1) = first column
# (-1,0),(-1,-1) = last column
# (0,0),(-1,0) = first row
# (0,-1),(-1,-1) = last row
pdf = canvas.Canvas(filename,pagesize=A4)
pdf.setTitle(view.label)
pdf.setSubject(view.title)
pdf.setAuthor('%s (%s)' % (view.request.principal.title,
view.request.principal.id))
pdf.setCreator('WAeUP Kofa')
width, height = A4
footer_text = view.label
if getattr(student, 'student_id', None) is not None:
footer_text = "%s - %s - " % (student.student_id, footer_text)
style = getSampleStyleSheet()
style.add(ParagraphStyle(name='Right', alignment=TA_RIGHT))
pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
story = []
frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
header_title = getattr(grok.getSite(), 'name', u'Sample University')
story.append(Paragraph(header_title, style["Heading1"]))
frame_header.addFromList(story,pdf)
# Insert student data table
story = []
frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
story.append(Paragraph(view.label, style["Heading2"]))
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
if student is not None:
#story.append(Spacer(1, 12))
bd_translation = translate(_('Base Data'),
'waeup.kofa', target_language=portal_language)
story.append(Paragraph(bd_translation, style["Heading3"]))
studenttable = render_student_data(studentview)
story.append(studenttable)
# Insert widgets
story.append(Paragraph(view.title, style["Heading3"]))
set_up_widgets(view)
data = []
portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
for widget in view.widgets:
f_label = '%s:' % translate(
widget.label.strip(), 'waeup.kofa',
target_language=portal_language)
f_label = Paragraph(f_label, style["Normal"])
f_text = formatted_text(widget(), size=12)
f_text = Paragraph(f_text, style["Normal"])
data.append([f_label,f_text])
table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
story.append(table)
# Import browser components locally
# to avoid circular import
from waeup.kofa.students.viewlets import FileManager
from waeup.kofa.browser import DEFAULT_IMAGE_PATH
data = []
# Collect viewlets
fm = FileManager(view.context, view.request, view)
fm.update()
if fm.viewlets:
sc_translation = translate(_('Scanned Documents'), 'waeup.kofa',
target_language=portal_language)
story.append(Paragraph(sc_translation, style["Heading3"]))
# Insert list of scanned documents
for viewlet in fm.viewlets:
f_label = formatted_label(size=12) % translate(
viewlet.label, 'waeup.kofa',
target_language=portal_language)
f_label = Paragraph(f_label, style["Normal"])
if viewlet.template.__grok_name__ == 'imagedisplay':
# In case we define FileDisplay viewlets with
# an imagedisplay template in the customization package
img = getUtility(IExtFileStore).getFileByContext(
view.context, attr=viewlet.download_name)
if img is None:
img = open(DEFAULT_IMAGE_PATH, 'rb')
doc_img = Image(img.name, width=2*cm, height=1*cm, kind='bound')
data.append([f_label,doc_img])
else:
f_text = formatted_text(viewlet.title, size=12)
f_text = Paragraph(f_text, style["Normal"])
data.append([f_label,f_text])
table = Table(data,style=SLIP_STYLE, colWidths=[5*cm, 13.5*cm])
story.append(table)
try:
frame_body.addFromList(story,pdf)
except IOError:
view.flash('Error in image file.')
return view.redirect(view.url(view.context))
if tabledata and tableheader:
insert_footer(pdf,width,style,footer_text,number_of_pages=2)
else:
insert_footer(pdf,width,style,footer_text)
# Insert content table on second page
if tabledata and tableheader:
pdf.showPage()
frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(2*cm))
story = []
story.append(Paragraph(view.content_title, style["Heading3"]))
#story.append(Spacer(1, 12))
contenttable = render_table_data(tableheader,tabledata)
story.append(contenttable)
frame_body.addFromList(story,pdf)
insert_footer(pdf,width,style,footer_text,number_of_pages=2)
view.response.setHeader(
'Content-Type', 'application/pdf')
return pdf.getpdfdata()
VERDICTS_DICT = {
'0': _('(not yet)'),
'A': 'Successful student',
'B': 'Student with carryover courses',
'C': 'Student on probation',
}