bijoe/bijoe/views.py

160 lines
6.2 KiB
Python

# bijoe - BI dashboard
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import datetime
import decimal
from django.views.generic import TemplateView, FormView
from django.http import Http404, HttpResponse
from django.contrib.auth.decorators import login_required
from .utils import get_warehouses
from .engine import Engine
from .forms import CubeForm
from .ods import Workbook
class HomepageView(TemplateView):
template_name = 'bijoe/homepage.html'
def get_context_data(self, **kwargs):
ctx = super(HomepageView, self).get_context_data(**kwargs)
ctx['warehouses'] = sorted((Engine(w) for w in get_warehouses(self.request)),
key=lambda w: w.label)
return ctx
class WarehouseView(TemplateView):
template_name = 'bijoe/warehouse.html'
def get_context_data(self, **kwargs):
ctx = super(WarehouseView, self).get_context_data(**kwargs)
try:
warehouse = [warehouse for warehouse in get_warehouses(self.request)
if warehouse.name == self.kwargs['warehouse']][0]
except IndexError:
raise Http404
ctx['warehouse'] = Engine(warehouse)
return ctx
class CubeMixin(object):
def get_data(self, cleaned_data, stringify=True):
cleaned_data = cleaned_data
filters = []
for kw, values in cleaned_data.iteritems():
if values and kw.startswith('filter__'):
dimension_name = kw[8:]
filters.append((dimension_name, values))
measures = cleaned_data.get('measures', [])
drilldown = cleaned_data.get('drilldown', [])
data = []
for row in self.cube.query(filters, drilldown, measures):
data_row = []
for cell, value in row:
if stringify:
if cell.type is float:
# FIXME find how to format decimal number using locale with Django
value = ('%05.2f' % float(value)).replace('.', ',') + u' %'
if isinstance(value, datetime.timedelta):
s = ''
if value.days:
s += '%d jour(s)' % value.days
if value.seconds / 3600:
s += ' %d heure(s)' % (value.seconds / 3600)
if not s:
s = 'moins d\'1 heure'
value = s
data_row.append(value)
data.append(data_row)
return data
class CubeView(CubeMixin, FormView):
template_name = 'bijoe/cube.html'
form_class = CubeForm
def dispatch(self, request, *args, **kwargs):
try:
self.warehouse = Engine([warehouse for warehouse in get_warehouses(self.request)
if warehouse.name == self.kwargs['warehouse']][0])
except IndexError:
raise Http404
try:
self.cube = self.warehouse[self.kwargs['cube']]
except KeyError:
raise Http404
return super(CubeView, self).dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
kwargs = super(CubeView, self).get_form_kwargs()
kwargs['cube'] = self.cube
return kwargs
def form_valid(self, form):
if 'ods' in self.request.POST:
return self.ods(form)
else:
return self.form_invalid(form)
def ods(self, form):
workbook = Workbook()
sheet = workbook.add_sheet(self.cube.label)
ctx = self.get_context_data(form=form)
for j, m in enumerate(ctx['drilldown'] + ctx['measures']):
sheet.write(0, j, m.label)
for i, row in enumerate(ctx['data']):
for j, cell in enumerate(row):
sheet.write(i + 1, j, unicode(cell))
response = HttpResponse(content_type='application/vnd.oasis.opendocument.spreadsheet')
response['Content-Disposition'] = 'attachment; filename=%s.ods' % self.cube.name
workbook.save(response)
return response
def get_context_data(self, **kwargs):
ctx = super(CubeView, self).get_context_data(**kwargs)
ctx['warehouse'] = self.warehouse
ctx['cube'] = self.cube
form = ctx['form']
if form.is_valid():
ctx['data'] = self.get_data(form.cleaned_data)
ctx['measures'] = [self.cube.measures[measure] for measure in
form.cleaned_data['measures']]
ctx['drilldown'] = [self.cube.dimensions[dimension] for dimension in
form.cleaned_data['drilldown']]
json_data = []
for row in self.get_data(form.cleaned_data, stringify=False):
coords = []
for dimension, cell in zip(ctx['drilldown'], row):
coords.append({'label': dimension.label, 'value': cell})
measures = []
for measure, cell in zip(ctx['measures'], row[len(ctx['drilldown']):]):
if isinstance(cell, datetime.timedelta):
cell = cell.days + cell.seconds / 86400.
if isinstance(cell, decimal.Decimal):
cell = float(cell)
measures.append({'label': measure.label, 'value': cell})
json_data.append({'coords': coords, 'measures': measures})
ctx['json'] = json.dumps(json_data, indent=2)
return ctx
homepage = login_required(HomepageView.as_view())
warehouse = login_required(WarehouseView.as_view())
cube = login_required(CubeView.as_view())