source: main/waeup.kofa/trunk/src/waeup/kofa/students/reports.py @ 9633

Last change on this file since 9633 was 9633, checked in by uli, 12 years ago

Basic statistics for kofa. Still the functionality is hidden - but available :-)

File size: 7.8 KB
Line 
1## $Id$
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.catalog.interfaces import ICatalog
20from zope.component import queryUtility, getUtility
21from zope.interface import implementer, Interface
22from waeup.kofa.interfaces import (
23     academic_sessions_vocab, registration_states_vocab)
24from waeup.kofa.interfaces import MessageFactory as _
25from waeup.kofa.reports import IReport
26
27class IStudentReport(IReport):
28    pass
29
30def get_students_by_session(session):
31    """Get students of a certain session.
32
33    Returns a table ordered by faculty code (one per row) and
34    registration state (cols). The result is a 3-tuple representing
35    ((<FACULTY_CODES>), (<STATES>), (<NUM_OF_STUDENTS>)). The
36    (<NUM_OF_STUDENTS>) is an n-tuple with each entry containing the
37    number of students found in that faculty and with the respective
38    state.
39
40    Sample result:
41
42      >>> ((u'FAC1', u'FAC2'),
43      ...  ('created', 'accepted', 'registered'),
44      ...  ((12, 10, 1), (0, 5, 25)))
45
46    This result means: there are 5 students in FAC2 in state
47    'accepted' while 12 students in 'FAC1' are in state 'created'.
48    """
49    site = grok.getSite()
50    states = tuple([x.value for x in registration_states_vocab])
51    states = states + (u'Total',)
52    fac_codes = tuple(sorted([x for x in site['faculties'].keys()],
53                             key=lambda x: x.lower()))
54    fac_codes = fac_codes + ('Total',)
55    # XXX: Here we do _one_ query and then examine the single
56    #   students. One could also do multiple queries and just look for
57    #   the result length (not introspecting the delivered students
58    #   further).
59    cat = queryUtility(ICatalog, name="students_catalog")
60    result = cat.searchResults(current_session=(session, session))
61    table = [[0 for x in xrange(len(states))] for y in xrange(len(fac_codes))]
62    for stud in result:
63        row = fac_codes.index(stud.faccode)
64        col = states.index(stud.state)
65        table[row][col] += 1
66        table[-1][col] += 1
67        table[row][-1] += 1
68        table[-1][-1] += 1
69    # turn lists into tuples
70    table = tuple([tuple(row) for row in table])
71    return (fac_codes, states, table)
72
73from reportlab.lib import colors
74from reportlab.lib.styles import getSampleStyleSheet
75from reportlab.lib.units import cm
76from reportlab.platypus import Paragraph, Table, Spacer
77from waeup.kofa.reports import IReport, IReportGenerator
78from waeup.kofa.reports import Report
79from waeup.kofa.browser.interfaces import IPDFCreator
80
81STYLE = getSampleStyleSheet()
82
83def tbl_data_to_table(row_names, col_names, data):
84    result = []
85    new_col_names = []
86    for name in col_names:
87        new_col_names.append(name.replace(' ', '\n'))
88    head = [''] + list(new_col_names)
89    result = [head]
90    for idx, row_name in enumerate(row_names):
91        row = [row_name] + list(data[idx])
92        result.append(row)
93    return result
94
95TABLE_STYLE = [
96    ('FONT', (0,0), (-1,-1), 'Helvetica', 8),
97    ('FONT', (0,0), (0,-1), 'Helvetica-Bold', 8),
98    ('FONT', (0,0), (-1,0), 'Helvetica-Bold', 8),
99    ('FONT', (0,-1), (-1,-1), 'Helvetica-Bold', 8),
100    ('FONT', (-1,0), (-1,-1), 'Helvetica-Bold', 8),
101    ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
102    ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
103    ('LINEBELOW', (0,-1), (-1,-1), 0.25, colors.black),
104    ('LINEAFTER', (-1,0), (-1,-1), 0.25, colors.black),
105    ('LINEBEFORE', (-1,0), (-1,-1), 1.0, colors.black),
106    ('LINEABOVE', (0,-1), (-1,-1), 1.0, colors.black),
107    ('LINEABOVE', (0,0), (-1,0), 0.25, colors.black),
108    ]
109
110@implementer(IStudentReport)
111class StudentReport(Report):
112    data = None
113    session = None
114
115    def __init__(self, session, author='System'):
116        super(StudentReport, self).__init__(
117            args=[session], kwargs={'author':author})
118        self.session = session
119        self.author = author
120        self.data = get_students_by_session(session)
121
122    def __repr__(self):
123        return 'StudentReport(session=%s, author=%r)' % (
124            self.session, self.author)
125
126    def get_data(self):
127        self.data = get_students_by_session(self.session)
128
129    def create_pdf(self):
130        creator = getUtility(IPDFCreator)
131        table_data = tbl_data_to_table(*self.data)
132        col_widths = [None,] + [1.6*cm] * len(self.data[1]) + [None,]
133        pdf_data = [Paragraph('<b>Session:</b> %s' % self.session,
134                              STYLE["Normal"]),
135                    Spacer(1, 12),]
136        pdf_data += [
137            Table(table_data, style=TABLE_STYLE, colWidths=col_widths)]
138        doc_title = 'Report: Students of Session %s\nby faculty and status' % (
139            self.session,)
140        pdf = creator.create_pdf(
141            pdf_data, None, doc_title, self.author,
142            'Session %s Students,' % self.session)
143        open('/tmp/result.pdf', 'wb').write(pdf)
144        return pdf
145
146@implementer(IReportGenerator)
147class StudentReportGenerator(grok.GlobalUtility):
148
149    title = _('Students by Session')
150    grok.name('students_by_session')
151
152    @property
153    def description(self):
154        return self.title
155
156    def generate(self, site, session=None, author=None):
157        result = StudentReport(session=session, author=author)
158        return result
159
160###############################################################
161## Browser related stuff
162##
163## XXX: move to local browser module
164###############################################################
165from waeup.kofa.browser.layout import KofaPage
166from waeup.kofa.interfaces import academic_sessions_vocab
167from waeup.kofa.reports import get_generators
168grok.templatedir('browser_templates')
169class StudentReportGeneratorPage(KofaPage):
170
171    grok.context(StudentReportGenerator)
172    grok.name('index.html')
173    grok.require('waeup.manageReports')
174
175    @property
176    def generator_name(self):
177        for name, gen in get_generators():
178            if gen == self.context:
179                return name
180        return None
181
182    def update(self, CREATE=None, session=None):
183        self.parent_url = self.url(self.context.__parent__)
184        self._set_session_values()
185        if CREATE and session:
186            # create a new report job for students by session
187            container = self.context.__parent__
188            user_id = self.request.principal.id
189            kw = dict(session=self._value_dict[session])
190            self.flash(_('New report is being created in background'))
191            container.start_report_job(
192                self.generator_name, user_id, kw=kw)
193            self.redirect(self.parent_url)
194            return
195        return
196
197    def _set_session_values(self):
198        vocab_terms = academic_sessions_vocab.by_value.values()
199        self.sessions = [(x.title, x.token) for x in vocab_terms]
200        self._value_dict = dict([(x.token, x.value) for x in vocab_terms])
201        return
202
203
204class StudentReportPDFView(grok.View):
205
206    grok.context(IStudentReport)
207    grok.name('pdf')
208    grok.require('waeup.Public')
209
210    def render(self):
211        filename = 'report.pdf'
212        self.response.setHeader(
213            'Content-Type', 'application/pdf')
214        self.response.setHeader(
215            'Content-Disposition:', 'attachment; filename="%s' % filename)
216        pdf_stream = self.context.create_pdf()
217        return pdf_stream
Note: See TracBrowser for help on using the repository browser.