160 lines
6.2 KiB
Python
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())
|