diff --git a/events/common.py b/events/common.py
index 0581b6b5..f2fce56b 100644
--- a/events/common.py
+++ b/events/common.py
@@ -6,32 +6,32 @@ from django.utils.translation import ugettext as _
from functions.model_utils import EnumChoices
-members_mapping = OrderedDict(
- N200={'min': None, 'max': 200, 'value': 1, 'label': _(u'до 200')},
- N200500={'min': 201, 'max': 500, 'value': 2, 'label': _(u'201-500')},
- N5001000={'min': 501, 'max': 1000, 'value': 3, 'label': _(u'501-1000')},
- N10002000={'min': 1001, 'max': 2000, 'value': 4, 'label': _(u'1001-2000')},
- N2000={'min': 2001, 'max': None, 'value': 5, 'label': _(u'более 2000')},
-)
-
-visitors_mapping = OrderedDict(
- N5={'min': None, 'max': 5000, 'value': 1, 'label': _(u'до 5 000')},
- N510={'min': 5001, 'max': 10000, 'value': 2, 'label': _(u'5 001 - 10 000')},
- N1030={'min': 10001, 'max': 30000, 'value': 3, 'label': _(u'10 001 - 30 000')},
- N3050={'min': 30001, 'max': 50000, 'value': 4, 'label': _(u'30 001 - 50 000')},
- N50100={'min': 50001, 'max': 100000, 'value': 5, 'label': _(u'50 001 - 100 000')},
- N100={'min': 100001, 'max': None, 'value': 6, 'label': _(u'более 100 000')},
-)
-
-price_mapping = OrderedDict(
- N1={'min': None, 'max': 100, 'value': 1, 'label': _(u'до 100 евро')},
- N12={'min': 101, 'max': 200, 'value': 2, 'label': _(u'100-200 евро')},
- N24={'min': 201, 'max': 400, 'value': 3, 'label': _(u'200-400 евро')},
- N4={'min': 401, 'max': None, 'value': 4, 'label': _(u'более 400 евро')},
-)
+members_mapping = OrderedDict([
+ ('N200', {'min': None, 'max': 200, 'value': 1, 'label': _(u'до 200')}),
+ ('N200500', {'min': 200, 'max': 400, 'value': 2, 'label': _(u'200-500')}),
+ ('N5001000', {'min': 500, 'max': 500, 'value': 3, 'label': _(u'500-1000')}),
+ ('N10002000', {'min': 1000, 'max': 2000, 'value': 4, 'label': _(u'1000-2000')}),
+ ('N2000', {'min': 2000, 'max': None, 'value': 5, 'label': _(u'более 2000')}),
+])
+
+visitors_mapping = OrderedDict([
+ ('N5', {'min': None, 'max': 5000, 'value': 1, 'label': _(u'до 5 000')}),
+ ('N510', {'min': 5000, 'max': 10000, 'value': 2, 'label': _(u'5 000 - 10 000')}),
+ ('N1030', {'min': 10000, 'max': 30000, 'value': 3, 'label': _(u'10 000 - 30 000')}),
+ ('N3050', {'min': 30000, 'max': 50000, 'value': 4, 'label': _(u'30 000 - 50 000')}),
+ ('N50100', {'min': 50000, 'max': 100000, 'value': 5, 'label': _(u'50 000 - 100 000')}),
+ ('N100', {'min': 100000, 'max': None, 'value': 6, 'label': _(u'более 100 000')}),
+])
+
+price_mapping = OrderedDict([
+ ('N1', {'min': None, 'max': 100, 'value': 1, 'label': _(u'до 100 евро')}),
+ ('N12', {'min': 100, 'max': 200, 'value': 2, 'label': _(u'100-200 евро')}),
+ ('N24', {'min': 200, 'max': 400, 'value': 3, 'label': _(u'200-400 евро')}),
+ ('N4', {'min': 400, 'max': None, 'value': 4, 'label': _(u'более 400 евро')}),
+])
def get_choices_kwargs(mapping):
- kwargs = {}
+ kwargs = OrderedDict()
for key, val in mapping.iteritems():
kwargs[key] = (val.get('value'), val.get('label'))
return kwargs
diff --git a/events/forms.py b/events/forms.py
index 7867505c..e52143ba 100644
--- a/events/forms.py
+++ b/events/forms.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from itertools import chain
from collections import namedtuple
+from datetime import datetime
try:
from collections import ChainMap
@@ -13,8 +14,10 @@ from django.utils.encoding import smart_text, force_text
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.db.models import Count, Sum, Q, ForeignKey, ManyToManyField
+from django.db.models.sql.where import ExtraWhere, AND, OR
from django.db import connection
from django.core.exceptions import ValidationError
+
from haystack.query import SearchQuerySet, RelatedSearchQuerySet, SQ
from functions.model_utils import EnumChoices
@@ -47,20 +50,27 @@ class FilterCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
cb = forms.CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
option_value = force_text(option_value)
rendered_cb = cb.render(name, option_value)
- option_label = force_text(option_label)
- output.append(format_html(u'
{1}',
+ option_label = mark_safe(force_text(option_label))
+ output.append(format_html(u'{1}',
label_for, rendered_cb, option_label))
output.append(u'')
return mark_safe('\n'.join(output))
-class CountModelMultipleChoiceField(forms.ModelMultipleChoiceField):
+class WidgetDefaultMixin(object):
+ def widget_attrs(self, widget):
+ attrs = super(WidgetDefaultMixin, self).widget_attrs(widget)
+ attrs['class'] = 'default'
+ return attrs
+
+
+class CountModelMultipleChoiceField(WidgetDefaultMixin, forms.ModelMultipleChoiceField):
# widget = forms.CheckboxSelectMultiple
widget = FilterCheckboxSelectMultiple
def label_from_instance(self, obj):
if obj.get('count', None) is None:
return smart_text(obj.get('name'))
- return u'{label} ({count})'.format(label=smart_text(obj.get('name')), count=obj.get('count'))
+ return u'{label} ({count})'.format(label=smart_text(obj.get('name')), count=obj.get('count'))
def prepare_value(self, value):
if isinstance(value, dict):
@@ -90,13 +100,9 @@ class CountModelMultipleChoiceField(forms.ModelMultipleChoiceField):
self.run_validators(value)
return pks
- def widget_attrs(self, widget):
- """
- Given a Widget instance (*not* a Widget class), returns a dictionary of
- any HTML attributes that should be added to the Widget, based on this
- Field.
- """
- return {'class': 'default'}
+
+class FilterTypedMultipleChoiceField(WidgetDefaultMixin, forms.TypedMultipleChoiceField):
+ pass
fields_mapping = {
@@ -121,7 +127,7 @@ class FilterForm(forms.Form):
# MEMBERS = MEMBERS
# VISITORS = VISITORS
# PRICE = PRICE
- model = forms.TypedMultipleChoiceField(
+ model = FilterTypedMultipleChoiceField(
label=_(u'Тип события'), coerce=int,
choices=TYPES, required=False, widget=FilterCheckboxSelectMultiple())
theme = CountModelMultipleChoiceField(
@@ -136,16 +142,16 @@ class FilterForm(forms.Form):
city = CountModelMultipleChoiceField(
label=_(u'Города'), required=False,
queryset=City.objects.language().values('pk', 'name'))
- price = forms.TypedMultipleChoiceField(
+ price = FilterTypedMultipleChoiceField(
label=_(u'Стоимость'), coerce=int,
choices=PRICE,
required=False, widget=FilterCheckboxSelectMultiple(),
help_text=_(u'За 1 м2 необорудованной площади'))
- members = forms.TypedMultipleChoiceField(
+ members = FilterTypedMultipleChoiceField(
label=_(u'Участники'), coerce=int,
choices=MEMBERS,
required=False, widget=FilterCheckboxSelectMultiple())
- visitors = forms.TypedMultipleChoiceField(
+ visitors = FilterTypedMultipleChoiceField(
label=_(u'Посетители'), coerce=int,
choices=VISITORS,
required=False, widget=FilterCheckboxSelectMultiple())
@@ -171,6 +177,7 @@ class FilterForm(forms.Form):
# нам нужно сбрасывать сохраненный выбор моделей,
# т.к. после валидации нужно вернуть только выбранные
self._models = None
+ self.cleaned_data = getattr(self, 'cleaned_data', {})
return self._is_valid
@property
@@ -213,16 +220,6 @@ class FilterForm(forms.Form):
self._lookup_kwargs['price_choice__in'] = d.get('price')
return self._lookup_kwargs
- # @property
- # def lookup_args(self):
- # if self._lookup_args is None:
- # d = self.cleaned_data
- # self._lookup_args = {}
- # self._local_fields = []
- # if d.get('members'):
- # self._local_fields.append('city')
- # return self._lookup_args
-
def filter(self, qs=None):
qs = qs or self.default_filter()
# lookup_kwargs = dict(ChainMap({}, *(lookup_kwargs or self.lookup_kwargs).values()))
@@ -234,38 +231,43 @@ class FilterForm(forms.Form):
qs = qs.load_all()
for model in self.models:
qs = qs.load_all_queryset(model, model.enable.all())
+ qs = qs.filter(data_end__gte=datetime.now())
return qs
def recalculate_choices(self):
print(self._is_valid)
- if self._is_valid and self.lookup_kwargs:
- for field in ['theme', 'tag', 'city', 'country']:
- # field_qs = self.default_filter(load_all=False)
- # if not field_lookup_kwargs:
- # continue
- # field_qs = (x.id.split('.')[1:] for x in self.filter(qs=field_qs, lookup_kwargs=field_lookup_kwargs) if x.id)
- # if field == 'theme':
- # self.fields[field].queryset = self.get_theme_choices(field_qs)
- values = ['pk', 'name']
- order_by = []
- select = self.make_count_select(field)
- qs = self.fields[field].queryset
- for key in select.iterkeys():
- values.append(key)
- if key == 'selected':
- order_by.insert(0, '-' + key)
- else:
- order_by.append('-' + key)
-
- qs = qs.extra(select=select)
- qs = qs.values(*values).order_by(*order_by)
- # if 'count' in values:
- # qs = qs.exclude(count=0)
- self.fields[field].queryset = qs
- print(self.fields[field].queryset.query)
-
- for field in ['members', 'visitors', 'price']:
- self.fields[field].choices = self.make_local_field_count(field) or self.fields[field].choices
+ # if self._is_valid and self.lookup_kwargs:
+
+ for field in ['theme', 'tag', 'city', 'country']:
+ # field_qs = self.default_filter(load_all=False)
+ # if not field_lookup_kwargs:
+ # continue
+ # field_qs = (x.id.split('.')[1:] for x in self.filter(qs=field_qs, lookup_kwargs=field_lookup_kwargs) if x.id)
+ # if field == 'theme':
+ # self.fields[field].queryset = self.get_theme_choices(field_qs)
+ values = ['pk', 'name']
+ order_by = []
+ select = self.make_count_select(field)
+ qs = self.fields[field].queryset
+ for key in select.iterkeys():
+ values.append(key)
+ if key == 'selected':
+ order_by.insert(0, '-' + key)
+ else:
+ order_by.append('-' + key)
+
+ qs = qs.extra(select=select)
+ if 'count' in values:
+ having = [''' `count` > 0 ''']
+ if 'selected' in values:
+ having.append(''' `selected` = 1 ''')
+ qs.query.having.add(ExtraWhere(having, []), OR)
+ qs = qs.values(*values).order_by(*order_by)
+ self.fields[field].queryset = qs
+ print(self.fields[field].queryset.query)
+
+ for field in ['members', 'visitors', 'price']:
+ self.fields[field].choices = self.make_local_field_count(field) or self.fields[field].choices
# for field in self.fields:
# field = self.fields[field]
@@ -326,7 +328,6 @@ class FilterForm(forms.Form):
return joins, where
def make_local_field_count(self, field):
- # if 'members' not in self.lookup_kwargs:
sql = ''
selects = []
l_field = fields_mapping.get(field, field)
@@ -366,6 +367,7 @@ class FilterForm(forms.Form):
selects.append(select + ''.join(joins) + ' where ' + ' and '.join(where) + group_by)
sql = ' union '.join(selects)
+ print(sql)
choices = []
if sql:
with connection.cursor() as c:
@@ -374,18 +376,15 @@ class FilterForm(forms.Form):
data = [mapper(*raw) for raw in c.fetchall()]
for key, val in _values_mapping:
count = sum([getattr(x, key, 0) or 0 for x in data])
- choices.append((val.get('value'), val.get('label') + ' ({count})'.format(count=count)))
+ choices.append((val.get('value'), val.get('label') + ' ({count})'.format(count=count)))
return choices
- # cursor.execute()
-
- #""" SELECT sum(case when (`exposition_exposition`.`members` < 200) then 1 else 0 end) as 'N200', sum(case when (`exposition_exposition`.`members` >= 200 AND `exposition_exposition`.`members` <= 500) then 2 else 0 end) as 'N200500', sum(case when (`exposition_exposition`.`members` >= 500 AND `exposition_exposition`.`members` <= 1000) then 3 else 0 end) as 'N5001000', sum(case when (`exposition_exposition`.`members` >= 1000 AND `exposition_exposition`.`members` <= 2000) then 4 else 0 end) as 'N10002000', sum(case when (`exposition_exposition`.`members` >= 2000) then 5 else 0 end) as 'N2000'FROM `exposition_exposition_translation` INNER JOIN `exposition_exposition` ON (`exposition_exposition_translation`.`master_id` = `exposition_exposition`.`id`) WHERE `exposition_exposition_translation`.`language_code` = 'ru'union SELECT sum(case when (`conference_conference`.`members` < 200) then 1 else 0 end) as 'N200', sum(case when (`conference_conference`.`members` >= 200 AND `conference_conference`.`members` <= 500) then 1 else 0 end) as 'N200500', sum(case when (`conference_conference`.`members` >= 500 AND `conference_conference`.`members` <= 1000) then 1 else 0 end) as 'N5001000', sum(case when (`conference_conference`.`members` >= 1000 AND `conference_conference`.`members` <= 2000) then 1 else 0 end) as 'N10002000', sum(case when (`conference_conference`.`members` >= 2000) then 1 else 0 end) as 'N2000'FROM `conference_conference_translation` INNER JOIN `conference_conference` ON (`conference_conference_translation`.`master_id` = `conference_conference`.`id`) WHERE `conference_conference_translation`.`language_code` = 'ru' """
-
def make_default_where(self, **kwargs):
- return ''' `{db_table}`.`is_published` = True '''.format(**kwargs)
+ return ''' (`{db_table}`.`is_published` = True) AND (`{db_table}`.`data_end` >= '{date_today}') '''\
+ .format(date_today=datetime.now().strftime('%Y-%m-%d'), **kwargs)
def make_count_select(self, field):
- count_selects = []
+ selects = []
case = None
count = None
print('looking {} {}'.format(field, self.lookup_kwargs))
@@ -447,12 +446,12 @@ class FilterForm(forms.Form):
where.extend(_where)
where.append(self.make_default_where(db_table=model._meta.db_table))
- count_selects.append(select + ''.join(joins) + ' where ' + ' and '.join(where) + group_by)
+ selects.append(select + ''.join(joins) + ' where ' + ' and '.join(where) + group_by)
- if len(count_selects) == 2:
- count = '({}) + ({})'.format(*count_selects)
- elif len(count_selects) == 1:
- count = count_selects[0]
+ if len(selects) == 2:
+ count = ''' IFNULL(({0}), 0) + IFNULL(({1}), 0) '''.format(*selects)
+ elif len(selects) == 1:
+ count = selects[0]
d = {}
if case is not None:
d['selected'] = case
diff --git a/events/management/commands/update_events_filter_fields.py b/events/management/commands/update_events_filter_fields.py
index affedc86..3368c5ce 100644
--- a/events/management/commands/update_events_filter_fields.py
+++ b/events/management/commands/update_events_filter_fields.py
@@ -66,6 +66,6 @@ class Command(NoArgsCommand):
if _min:
q &= Q(**{from_field + '__gte': _min})
if _max:
- q &= Q(**{from_field + '__lte': _max})
+ q &= Q(**{from_field + '__lt': _max})
model.objects.filter(q).exclude(**exclude_kwargs).update(**update_kwargs)