You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

345 lines
12 KiB

# from exposition.models import Exposition
import json
import operator
from datetime import date, timedelta
import numpy
import pandas
from city.models import City
# -*- coding: utf-8 -*-
# from conference.models import Conference
from country.models import Country
from django.contrib.contenttypes.models import ContentType
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Q
from django.http import HttpResponse
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, TemplateView
from theme.models import Tag, Theme
from .forms import (
ByContentObjectsFilter,
DateFilter,
EventsDateFilter,
EventsParamsFilter,
event_choices,
kind_choices
)
from .models import ObjectStats, SectionStats
kind_choices_dict = dict(kind_choices)
class CustomJSEncoder(DjangoJSONEncoder):
def default(self, o):
# See "Date Time String Format" in the ECMA-262 specification.
if isinstance(o, date):
return 'Date({y}, {js_m}, {day})'.format(
y=o.year, js_m=o.month - 1, day=o.day)
else:
return super(CustomJSEncoder, self).default(o)
def pydate_to_js(o):
return 'Date({y}, {js_m}, {day})'.format(
y=o.year, js_m=o.month - 1, day=o.day)
class StatBaseView(FormView):
def get_template_names(self):
if self.request.is_ajax():
return self.ajax_template_name
return super(StatBaseView, self).get_template_names()
def after_get(self):
pass
def get(self, request, *args, **kwargs):
today = timezone.now().date()
self.qs = self.qs.filter(
created_at__gte=today - timedelta(days=7),
created_at__lt=today
)
self.after_get()
return super(StatBaseView, self).get(request, *args, **kwargs)
def form_valid(self, form):
if form.cleaned_data['date_begin']:
self.qs = self.qs.filter(created_at__gte=form.cleaned_data['date_begin'])
if form.cleaned_data['date_end']:
self.qs = self.qs.filter(created_at__lte=form.cleaned_data['date_end'])
def make_json_response(self, data):
return HttpResponse(json.dumps(data), content_type='application/json')
def get_dataframe(self):
if self.df is not None:
return self.df
self.df = pandas.DataFrame(list(self.qs))
return self.df
def get_pivot_table(self):
if self.pt is not None:
return self.pt
df = self.get_dataframe()
self.pt = df.pivot_table(values='value', index='created_at', columns=self.pt_columns, aggfunc=numpy.sum)
return self.pt
class StatSectionsView(StatBaseView):
template_name = 'admin/stats/section_stat.html'
ajax_template_name = 'admin/stats/content_type_form.html'
form_class = DateFilter
content_form_class = ByContentObjectsFilter
def get_queryset(self):
return SectionStats.objects\
.all()\
.values('created_at', 'section', 'kind', 'value', 'content_type', 'object_id')
def dispatch(self, request, *args, **kwargs):
self.qs = self.get_queryset()
self.pt = self.df = self.content_form = self.ct_choices = None
self.pt_columns = 'kind'
return super(StatSectionsView, self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
super(StatSectionsView, self).form_valid(form)
if form.cleaned_data['section']:
self.qs = self.qs.filter(section=form.cleaned_data['section'])
if form.cleaned_data['kind']:
self.qs = self.qs.filter(kind=form.cleaned_data['kind'])
self.get_and_check_content_form()
if self.request.is_ajax():
data = {
'success': True,
'html': render_to_string(self.ajax_template_name, self.get_context_data())
}
return self.make_json_response(data)
return self.render_to_response(self.get_context_data(form=form))
def form_invalid(self, form):
if self.request.is_ajax():
data = {
'success': False,
'errors': form.errors
}
return self.make_json_response(data)
return super(StatSectionsView, self).form_invalid(form)
def get_ct_form(self):
form = None
check = False
pairs = self.qs\
.filter(content_type__isnull=False, object_id__isnull=False)\
.values_list('content_type', 'object_id')
if pairs:
ct_id, object_ids = zip(*pairs)
ct_id = list(set(ct_id))
object_ids = set(object_ids)
if len(ct_id) == 1:
ct_id = ct_id[0]
form_kwargs = {}
post_data = self.request.POST
ct_model = ContentType.objects.get_for_id(ct_id)
self.ct_choices = ct_model.model_class().objects.language().filter(pk__in=object_ids).values_list('pk', 'name').order_by('name')
self.ct_choices_dict = dict(self.ct_choices)
# detect if we need to validate form
content_type = post_data.get('content_type', None)
if content_type and int(content_type) == ct_id:
check = True
form_kwargs.update({
'data': post_data,
})
form_kwargs.update({
'initial': {'content_type': ct_id},
'choices': self.ct_choices,
})
form = self.content_form_class(**form_kwargs)
return form, check
def get_and_check_content_form(self):
self.content_form, check = self.get_ct_form()
if check and self.content_form.is_valid() and self.content_form.cleaned_data['ct_objects']:
self.qs = self.qs.filter(
content_type=self.content_form.cleaned_data['content_type'],
object_id__in=self.content_form.cleaned_data['ct_objects']
)
self.pt_columns = 'object_id'
def get_col_map(self):
if self.pt_columns == 'kind':
return kind_choices_dict
else:
return self.ct_choices_dict
def get_column_names(self):
col_map = self.get_col_map()
columns = [force_text(col_map[x]) for x in self.pt.columns.tolist()]
return json.dumps(columns).replace('[', '').replace(']', '')
def get_context_data(self, **kwargs):
context = super(StatSectionsView, self).get_context_data(**kwargs)
context['content_form'] = self.content_form
if self.request.is_ajax():
return context
if self.qs:
pt = self.get_pivot_table()
# change numpy.nan values to python's None (js null with json)
# result = pt.where(pt.notnull(), None)
result = pt.fillna(value=0)
result_summary = result.sum().to_dict()
context['columns'] = self.get_column_names()
# if self.pt_columns != 'kind':
# result.columns = list(map(lambda x: str(int(x)), result.columns.tolist()))
data = list(map(
# lambda x: {'date': pydate_to_js(x[0]), 'attrs': json.dumps(x[1:]).replace('[', '').replace(']', '')},
lambda x: json.dumps(list(x), cls=CustomJSEncoder),
result.itertuples()
))
data_summary = []
for key, val in result_summary.items():
data_summary.append({
'name': self.get_col_map()[key],
'val': val
})
context['data'] = data
context['data_summary'] = data_summary
return context
class EventStatView(StatBaseView):
form_class = EventsDateFilter
params_form_cls = EventsParamsFilter
pt_columns = ['content_type', 'object_id']
template_name = 'admin/stats/event_stat.html'
def get_queryset(self):
return ObjectStats.objects\
.all()\
.select_related()\
.values('created_at', 'value')
def dispatch(self, request, *args, **kwargs):
self.qs = self.get_queryset()
self.pt = self.df = self.params_form = None
return super(EventStatView, self).dispatch(request, *args, **kwargs)
def after_get(self):
self.params_form = self.get_params_form()
def form_valid(self, form):
super(EventStatView, self).form_valid(form)
self.event_types = ['conference', 'exposition']
if form.cleaned_data['event']:
params = {'{attr}_id__isnull'.format(attr=form.cleaned_data['event']): False}
self.qs = self.qs.filter(**params)
self.event_types = [form.cleaned_data['event'], ]
self.params_form = self.get_params_form()
self.check_params_form()
return self.render_to_response(self.get_context_data(form=form))
def get_params_form(self):
form_kwargs = {}
country_ids = set()
city_ids = set()
theme_ids = set()
tag_ids = set()
val_list = [
'exposition__country',
'conference__country',
'exposition__city',
'conference__city',
'exposition__theme',
'conference__theme',
'exposition__tag',
'conference__tag',
]
values_list = self.qs.values_list(*val_list)
if values_list:
e_country_ids, c_country_ids, e_city_ids, c_city_ids, e_theme_ids, c_theme_ids, e_tag_ids, c_tag_ids = \
zip(*values_list)
country_ids.update(e_country_ids)
country_ids.update(c_country_ids)
city_ids.update(e_city_ids)
city_ids.update(c_city_ids)
theme_ids.update(e_theme_ids)
theme_ids.update(c_theme_ids)
tag_ids.update(e_tag_ids)
tag_ids.update(c_tag_ids)
for ids_set in [country_ids, city_ids, theme_ids, tag_ids]:
if None in ids_set:
ids_set.remove(None)
form_kwargs = {
'country_choices': Country.objects.language().filter(pk__in=country_ids).values_list('pk', 'name').order_by('name'),
'city_choices': City.objects.language().filter(pk__in=city_ids).values_list('pk', 'name').order_by('name'),
'theme_choices': Theme.objects.language().filter(pk__in=theme_ids).values_list('pk', 'name').order_by('name'),
'tag_choices': Tag.objects.language().filter(pk__in=tag_ids).values_list('pk', 'name').order_by('name'),
'data': self.request.POST
}
form = self.params_form_cls(**form_kwargs)
return form
def check_params_form(self):
if self.params_form.is_valid():
query = []
for event_type in self.event_types:
params = {}
if self.params_form.cleaned_data['country']:
params.update({
event_type + '__country_id__in': self.params_form.cleaned_data['country']
})
if self.params_form.cleaned_data['city']:
params.update({
event_type + '__city_id__in': self.params_form.cleaned_data['city']
})
if self.params_form.cleaned_data['theme']:
params.update({
event_type + '__theme__in': self.params_form.cleaned_data['theme']
})
if self.params_form.cleaned_data['tag']:
params.update({
event_type + '__tag__in': self.params_form.cleaned_data['tag']
})
query.append(Q(**params))
if len(query) == 1:
self.qs = self.qs.filter(query[0])
else:
self.qs = self.qs.filter(reduce(operator.or_, query))
def get_context_data(self, **kwargs):
context = super(EventStatView, self).get_context_data(**kwargs)
context['params_form'] = self.params_form
if self.qs:
df = self.get_dataframe()
pt = df.pivot_table(values='value', index='created_at', aggfunc=numpy.sum)
result = pt.fillna(value=0)
data = list(map(
lambda x: json.dumps(list(x), cls=CustomJSEncoder),
result.iteritems()
))
context['data'] = data
context['data_summary'] = int(result.sum())
return context