source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/reports/contract_statistics.py @ 12659

Last change on this file since 12659 was 12659, checked in by Henrik Bettermann, 10 years ago

Implement simple contract statistics report.

  • Property svn:keywords set to Id
File size: 8.1 KB
Line 
1## $Id: contract_statistics.py 12659 2015-03-03 08:58:38Z henrik $
2##
3## Copyright (C) 2015 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, Attribute
22from waeup.ikoba.interfaces import (
23    IIkobaUtils, CREATED, APPROVED, SUBMITTED, EXPIRED, REJECTED)
24from waeup.ikoba.customers.interfaces import ICustomersUtils
25from waeup.ikoba.interfaces import MessageFactory as _
26from waeup.ikoba.reports import IReport
27
28class IContractStatisticsReport(IReport):
29
30    creation_dt_string = Attribute('Human readable report creation datetime')
31
32def get_contract_stats():
33    """Get contracts
34
35    Returns a table ordered by contract type and
36    contract state (cols). The result is a 3-tuple representing
37    ((<TYPES>), (<STATES>), (<NUM_OF_CUSTOMERS>)). The
38    (<NUM_OF_CUSTOMERS>) is an n-tuple with each entry containing the
39    number of contracts found for that type and with the respective
40    state.
41
42    Sample result:
43
44      >>> ((u'Sample Contract 1', u'Sample Contract 2'),
45      ...  ('created', 'submitted for approval', 'approved'),
46      ...  ((12, 10, 1), (0, 5, 25)))
47
48    This result means: there are 5 contracts of type 'Sample Contract 2'
49    are in state 'submitted'.
50    """
51    site = grok.getSite()
52    TYPES = getUtility(ICustomersUtils).CONTYPES_DICT
53    types = TYPES.keys()
54    type_names = tuple(TYPES.values()) + (u'All',)
55    states = (CREATED, SUBMITTED, APPROVED, EXPIRED, REJECTED)
56    STATES = getUtility(ICustomersUtils).TRANSLATED_CONTRACT_STATES
57    state_names = tuple([STATES[state] for state in states]) + (u'Total',)
58    # find all contracts
59    cat = queryUtility(ICatalog, name="contracts_catalog")
60    contracts = cat.searchResults(contract_id=(None, None))
61    # create empty table
62    table = [[0 for x in xrange(len(STATES)+1)] for y in xrange(len(TYPES)+1)]
63    # fill table
64    for contract in contracts:
65        row = types.index(contract.class_name)
66        col = states.index(contract.state)
67        table[row][col] += 1
68        table[-1][col] += 1
69        table[row][-1] += 1
70        table[-1][-1] += 1
71    # turn lists into tuples
72    table = tuple([tuple(row) for row in table])
73    return (type_names, state_names, table)
74
75
76from reportlab.lib import colors
77from reportlab.lib.styles import getSampleStyleSheet
78from reportlab.lib.units import cm
79from reportlab.platypus import Paragraph, Table, Spacer
80from waeup.ikoba.reports import IReport, IReportGenerator
81from waeup.ikoba.reports import Report
82from waeup.ikoba.browser.interfaces import IPDFCreator
83
84STYLE = getSampleStyleSheet()
85
86def tbl_data_to_table(row_names, col_names, data):
87    result = []
88    new_col_names = []
89    for name in col_names:
90        new_col_names.append(name.replace(' ', '\n'))
91    head = [''] + list(new_col_names)
92    result = [head]
93    for idx, row_name in enumerate(row_names):
94        row = [row_name] + list(data[idx])
95        result.append(row)
96    return result
97
98TABLE_STYLE = [
99    ('FONT', (0,0), (-1,-1), 'Helvetica', 8),
100    ('FONT', (0,0), (0,-1), 'Helvetica-Bold', 8),
101    ('FONT', (0,0), (-1,0), 'Helvetica-Bold', 8),
102    ('FONT', (0,-1), (-1,-1), 'Helvetica-Bold', 8),
103    ('FONT', (-1,0), (-1,-1), 'Helvetica-Bold', 8),
104    ('ALIGN', (1,1), (-1,-1), 'RIGHT'),
105    ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
106    ('LINEBELOW', (0,-1), (-1,-1), 0.25, colors.black),
107    ('LINEAFTER', (-1,0), (-1,-1), 0.25, colors.black),
108    ('LINEBEFORE', (-1,0), (-1,-1), 1.0, colors.black),
109    #('LINEABOVE', (0,-1), (-1,-1), 1.0, colors.black),
110    #('LINEABOVE', (0,0), (-1,0), 0.25, colors.black),
111    ]
112
113@implementer(IContractStatisticsReport)
114class ContractStatisticsReport(Report):
115
116    data = None
117
118    def __init__(self, author='System'):
119        super(ContractStatisticsReport, self).__init__(
120            args=[], kwargs={'author':author})
121        self.author = author
122        self.creation_dt_string = self.creation_dt.astimezone(
123            getUtility(IIkobaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
124        self.data = get_contract_stats()
125
126    def create_pdf(self):
127        creator = getUtility(IPDFCreator, name='landscape')
128        table_data = tbl_data_to_table(*self.data)
129        col_widths = [None,] + [1.6*cm] * len(self.data[1]) + [None,]
130        pdf_data = [Paragraph('<b>%s</b>' % self.creation_dt_string,
131                              STYLE["Normal"]),
132                    Spacer(1, 12),]
133        pdf_data += [
134            Table(table_data, style=TABLE_STYLE, colWidths=col_widths)]
135        doc_title = 'Contracts'
136        pdf = creator.create_pdf(
137            pdf_data, None, doc_title, self.author, 'Contracts'
138            )
139        return pdf
140
141@implementer(IReportGenerator)
142class ContractStatisticsReportGenerator(grok.GlobalUtility):
143
144    title = _('Contract Statistics')
145    grok.name('contract_stats')
146
147    def generate(self, site, author=None):
148        result = ContractStatisticsReport(
149            author=author)
150        return result
151
152###############################################################
153## Browser related stuff
154##
155## XXX: move to local browser module
156###############################################################
157from waeup.ikoba.browser.layout import IkobaPage
158from waeup.ikoba.reports import get_generators
159from waeup.ikoba.browser.breadcrumbs import Breadcrumb
160grok.templatedir('browser_templates')
161class ContractStatisticsReportGeneratorPage(IkobaPage):
162
163    grok.context(ContractStatisticsReportGenerator)
164    grok.name('index.html')
165    grok.require('waeup.manageReports')
166
167    label = _('Create contract statistics report')
168
169    @property
170    def generator_name(self):
171        for name, gen in get_generators():
172            if gen == self.context:
173                return name
174        return None
175
176    def update(self, CREATE=None):
177        self.parent_url = self.url(self.context.__parent__)
178        if CREATE:
179            # create a new report job for contracts
180            container = self.context.__parent__
181            user_id = self.request.principal.id
182            kw = dict()
183            self.flash(_('New report is being created in background'))
184            job_id = container.start_report_job(
185                self.generator_name, user_id, kw=kw)
186            ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')
187            grok.getSite().logger.info(
188                '%s - report %s created: %s' % (
189                ob_class, job_id, self.context.title))
190            self.redirect(self.parent_url)
191            return
192        return
193
194class ContractStatisticsReportPDFView(grok.View):
195
196    grok.context(IContractStatisticsReport)
197    grok.name('pdf')
198    grok.require('waeup.Public')
199    prefix = ''
200
201    def _filename(self):
202        return 'ContractStatisticsReport.pdf'
203
204    def render(self):
205        filename = self._filename().replace(
206            '/', '_').replace(' ','_').replace(':', '-')
207        self.response.setHeader(
208            'Content-Type', 'application/pdf')
209        self.response.setHeader(
210            'Content-Disposition:', 'attachment; filename="%s' % filename)
211        pdf_stream = self.context.create_pdf()
212        ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')
213        grok.getSite().logger.info('%s - report %s downloaded: %s' % (
214            ob_class, self.context.__name__, filename))
215        return pdf_stream
216
217class ContractStatsBreadcrumb(Breadcrumb):
218    """A breadcrumb for reports.
219    """
220    grok.context(ContractStatisticsReportGenerator)
221    title = _(u'Contract Statistics')
222    target = None
Note: See TracBrowser for help on using the repository browser.