bijoe/bijoe/visualization/views.py

245 lines
9.6 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 hashlib
import json
from django.conf import settings
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.views.generic.list import MultipleObjectMixin
from django.views.generic import DetailView, ListView, View, TemplateView
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
from django.http import HttpResponse, Http404
from django.core.exceptions import PermissionDenied
from django.views.decorators.clickjacking import xframe_options_exempt
from ..utils import get_warehouses
from ..engine import Engine
from . import models, forms, signature
from .utils import Visualization
from .. import views
class WarehouseView(views.AuthorizationMixin, 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)
ctx['cubes'] = sorted(ctx['warehouse'].cubes, key=lambda cube: cube.label.strip().lower())
return ctx
class CubeDisplayMixin(object):
def get_context_data(self, **kwargs):
ctx = super(CubeDisplayMixin, self).get_context_data(**kwargs)
ctx['warehouse'] = self.warehouse
ctx['cube'] = self.cube
if self.visualization:
ctx['visualization'] = self.visualization
ctx['measures_json'] = json.dumps(
[measure.to_json() for measure in self.visualization.measures])
ctx['drilldown_json'] = json.dumps(
[dim.to_json() for dim in self.visualization.drilldown])
ctx['json'] = json.dumps(self.visualization.json_data(), indent=2)
return ctx
class CubeMixin(object):
def visualization(self, request, cube):
self.form = forms.CubeForm(cube=self.cube, data=request.GET or request.POST)
if self.form.is_valid():
return Visualization.from_form(self.cube, self.form)
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
self.visualization = self.visualization(request, cube)
return super(CubeMixin, self).dispatch(request, *args, **kwargs)
class ODSMixin(object):
def ods(self, visualization):
response = HttpResponse(content_type='application/vnd.oasis.opendocument.spreadsheet')
response['Content-Disposition'] = 'attachment; filename=%s.ods' % self.cube.name
workbook = visualization.ods()
workbook.save(response)
return response
class CubeView(CubeDisplayMixin, ODSMixin, CubeMixin, TemplateView):
template_name = 'bijoe/cube.html'
def post(self, request, *args, **kwargs):
if 'ods' in self.request.POST:
return self.ods(self.visualization)
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super(CubeView, self).get_context_data(**kwargs)
ctx['form'] = self.form
return ctx
class CreateVisualizationView(views.AuthorizationMixin, CubeMixin, CreateView):
model = models.Visualization
form_class = forms.VisualizationForm
template_name = 'bijoe/create_visualization.html'
success_url = '/visualization/%(id)s/'
def get(self, request, *args, **kwargs):
if not self.visualization:
return redirect('homepage')
return super(CreateVisualizationView, self).get(request, *args, **kwargs)
def form_valid(self, form):
if not self.visualization:
return redirect('homepage')
form.instance.parameters = self.visualization.to_json()
return super(CreateVisualizationView, self).form_valid(form)
class VisualizationView(views.AuthorizationMixin, ODSMixin, CubeDisplayMixin,
DetailView):
model = models.Visualization
template_name = 'bijoe/visualization.html'
def get_object(self):
named_visualization = super(VisualizationView, self).get_object()
self.visualization = Visualization.from_json(named_visualization.parameters, request=self.request)
self.cube = self.visualization.cube
self.warehouse = self.cube.engine
return named_visualization
def post(self, request, *args, **kwargs):
named_visualization = self.get_object()
if 'ods' in self.request.POST:
return self.ods(self.visualization)
form = forms.CubeForm(cube=self.cube, data=request.POST)
if form.is_valid():
self.visualization = Visualization.from_form(self.cube, form)
named_visualization.parameters = self.visualization.to_json()
named_visualization.save()
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super(VisualizationView, self).get_context_data(**kwargs)
initial = {
'representation': self.visualization.representation,
'measures': [m.name for m in self.visualization.measures],
'drilldown': [d.name for d in self.visualization.drilldown],
}
for key, value in self.visualization.filters.iteritems() or []:
if isinstance(value, list):
value = tuple(value)
initial['filter__%s' % key] = value
ctx['form'] = forms.CubeForm(cube=self.cube, initial=initial)
path = reverse('visualization-iframe', args=self.args, kwargs=self.kwargs)
signature = hashlib.sha1(path + settings.SECRET_KEY).hexdigest()
path += '?signature=' + signature
ctx['iframe_url'] = path
return ctx
class SignatureAuthorizationMixin(views.AuthorizationMixin):
def authorize(self, request):
if request.user.is_authenticated() and request.user.is_superuser:
return True
if 'signature' in request.GET:
signature = hashlib.sha1(request.path + settings.SECRET_KEY).hexdigest()
if request.GET['signature'] == signature:
return True
return False
class VisualizationIframeView(SignatureAuthorizationMixin, VisualizationView):
template_name = 'bijoe/cube_raw.html'
class DeleteVisualizationView(views.AuthorizationMixin, DeleteView):
model = models.Visualization
template_name = 'bijoe/delete_visualization.html'
success_url = 'homepage'
class VisualizationsView(views.AuthorizationMixin, ListView):
template_name = 'bijoe/visualizations.html'
model = models.Visualization
class RenameVisualization(views.AuthorizationMixin, UpdateView):
model = models.Visualization
form_class = forms.VisualizationForm
template_name = 'bijoe/rename_visualization.html'
success_url = '/visualization/%(id)s/'
class VisualizationsJSONView(MultipleObjectMixin, View):
model = models.Visualization
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated() or not request.user.is_superuser:
known_services = getattr(settings, 'KNOWN_SERVICES', [])
if known_services:
key = None
for l in known_services.itervalues():
for service in l.itervalues():
if service['verif_orig'] == request.GET.get('orig', ''):
key = service['secret']
if key is None or not signature.check_query(request.META['QUERY_STRING'], key):
raise PermissionDenied('signature is missing or wrong')
data = []
for visualization in self.get_queryset():
path = reverse('visualization-iframe', kwargs={'pk': visualization.pk})
sig = hashlib.sha1(path + settings.SECRET_KEY).hexdigest()
path += '?signature=' + sig
data.append({
'name': visualization.name,
'path': request.build_absolute_uri(path),
})
response = HttpResponse(content_type='application/json')
response.write(json.dumps(data))
return response
class CubeIframeView(CubeView):
template_name = 'bijoe/cube_raw.html'
warehouse = WarehouseView.as_view()
cube = CubeView.as_view()
cube_iframe = xframe_options_exempt(CubeIframeView.as_view())
visualizations = VisualizationsView.as_view()
visualizations_json = VisualizationsJSONView.as_view()
create_visualization = CreateVisualizationView.as_view()
delete_visualization = DeleteVisualizationView.as_view()
rename_visualization = RenameVisualization.as_view()
visualization = VisualizationView.as_view()
visualization_iframe = xframe_options_exempt(VisualizationIframeView.as_view())