diff --git a/conference/search_indexes.py b/conference/search_indexes.py index 92a0b384..d778e459 100644 --- a/conference/search_indexes.py +++ b/conference/search_indexes.py @@ -27,6 +27,8 @@ class ConferenceIndex(indexes.SearchIndex, indexes.Indexable, ExpoSearchMixin): members_choice = indexes.IntegerField() visitors_choice = indexes.IntegerField() price_choice = indexes.IntegerField() + expohit = indexes.BooleanField(model_attr='expohit') + rating = indexes.MultiValueField() def prepare_form_name(self, obj): return None diff --git a/events/forms.py b/events/forms.py index 59a83ec8..0707fb8e 100644 --- a/events/forms.py +++ b/events/forms.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import re -from itertools import chain +import operator +from itertools import chain, combinations from collections import namedtuple from datetime import datetime from datetime import timedelta @@ -165,12 +166,20 @@ values_mapping = { } RATING = ( - (1, _(u'Топовые (HIT)')), - (2, _(u'Члены РСВЯ')), - (3, _(u'Члены UFI')), - (4, _(u'ExpoRating')), + ('r1', _(u'Топовые (HIT)')), + ('r2', _(u'Члены РСВЯ')), + ('r3', _(u'Члены UFI')), + ('r4', _(u'ExpoRating')), ) +rating_mapping = [ + {'value': 'r1', 'db_value': 1, 'label': _(u'Топовые (HIT)'), 'field': 'expohit'}, + {'value': 'r2', 'db_value': int(Exposition.quality_label.ufi), 'label': _(u'Члены РСВЯ'), 'field': 'quality_label'}, + {'value': 'r3', 'db_value': int(Exposition.quality_label.rsva), 'label': _(u'Члены UFI'), 'field': 'quality_label'}, + {'value': 'r4', 'db_value': int(Exposition.quality_label.exporating), 'label': _(u'ExpoRating'), 'field': 'quality_label'}, +] + + monthes_abr_to_num = {v.lower(): k for k, v in enumerate(calendar.month_abbr)} monthes_num_to_abr = {v: k for k, v in monthes_abr_to_num.iteritems()} year_month_regex = re.compile(r'^((?P\d{4})(?P\w{3}))|((?P\d{1,2})/(?P\d{4}))$') @@ -248,7 +257,7 @@ class FilterForm(forms.Form): choices=VISITORS, required=False, widget=FilterCheckboxSelectMultiple()) rating = FilterTypedMultipleChoiceField( - label=_(u'Рейтинги'), coerce=int, + label=_(u'Рейтинги'), coerce=str, choices=RATING, required=False, widget=FilterCheckboxSelectMultiple()) @@ -418,7 +427,7 @@ class FilterForm(forms.Form): def filter(self, qs=None): qs = qs or self.default_filter() # lookup_kwargs = dict(ChainMap({}, *(lookup_kwargs or self.lookup_kwargs).values())) - qs = self.make_data_begin_filter(qs) + qs = self.make_cleaned_sqs_filter(qs) return qs.filter(**self.lookup_kwargs).order_by('data_begin') def default_filter(self, load_all=True, _models=None): @@ -432,10 +441,13 @@ class FilterForm(forms.Form): qs = qs.filter(data_begin__gte=datetime.now().date()) return qs - def make_data_begin_filter(self, qs): - params = self.make_date_begin_sqs_params() - if params is not None: - qs = qs.filter(params) + def make_cleaned_sqs_filter(self, qs): + date_params = self.make_date_begin_sqs_params() + if date_params is not None: + qs = qs.filter(date_params) + rating_params = self.make_rating_sqs_params() + if rating_params is not None: + qs = qs.filter(rating_params) return qs def recalculate_choices(self): @@ -476,6 +488,7 @@ class FilterForm(forms.Form): self.fields[field].choices = self.make_local_field_count(field) or self.fields[field].choices self.fields['month'].choices = self.make_date_begin_counts() + self.fields['rating'].choices = self.make_rating_counts() self.make_event_type_choices_count() # self.fields['month'].choices = self.month_choices() @@ -489,7 +502,7 @@ class FilterForm(forms.Form): choices = [] for _type, label in TYPES: qs = self.default_filter(load_all=False, _models=[types.get(_type)]) - qs = self.make_data_begin_filter(qs) + qs = self.make_cleaned_sqs_filter(qs) count = qs.filter(**self.lookup_kwargs).count() choices.append((_type, label + ' ({count})'.format(count=count))) self.fields['event_type'].choices = choices @@ -583,10 +596,11 @@ class FilterForm(forms.Form): ) _joins, _where = self.make_joins_from_selected(field, model) - joins.extend(_joins) - where.extend(_where) + joins.extend(_joins or []) + where.extend(_where or []) where.append(self.make_default_where(db_table=model._meta.db_table)) self.make_date_begin_where(where, db_table=model._meta.db_table) + self.make_rating_where(where, db_table=model._meta.db_table) selects.append(select + ''.join(joins) + ' where ' + ' and '.join(where) + group_by) @@ -669,9 +683,10 @@ class FilterForm(forms.Form): ) _joins, _where = self.make_joins_from_selected('data_begin', model) - joins.extend(_joins) - where.extend(_where) + joins.extend(_joins or []) + where.extend(_where or []) where.append(self.make_default_where(db_table=model._meta.db_table)) + self.make_rating_where(where, db_table=model._meta.db_table) selects.append(select + ''.join(joins) + ' where ' + ' and '.join(where) + group_by) @@ -693,6 +708,64 @@ class FilterForm(forms.Form): # with connection.cursor() as c: return choices + def make_rating_counts(self): + choices = [] + sql = '' + selects = [] + for model in self.models: + joins = [] + where = [] + group_by = '' + + format_kwargs = { + 'db_table': model._meta.db_table, + 'lang': self.lang, + } + + cases = [] + for case in filter(lambda x: x['field'] == 'expohit', rating_mapping): + cases.append( + ''' sum(case when (`{db_table}`.`{field}` = {db_value}) then 1 else 0 end) as '{value}' '''\ + .format(value=case['value'], field=case['field'], db_value=case['db_value'], **format_kwargs) + ) + for case in filter(lambda x: x['field'] != 'expohit', rating_mapping): + cases.append( + ''' sum(case when (`{db_table}`.`{field}` = `{db_table}`.`{field}` | {db_value}) then 1 else 0 end) as '{value}' '''\ + .format(value=case['value'], field=case['field'], db_value=case['db_value'], **format_kwargs) + ) + select = \ + ''' SELECT {cases} FROM `{db_table}_translation` INNER JOIN `{db_table}` ON (`{db_table}_translation`.`master_id` = `{db_table}`.`id`) '''\ + .format(cases=', '.join(cases), **format_kwargs) + + where.append( + ''' `{db_table}_translation`.`language_code` = '{lang}' '''\ + .format(**format_kwargs) + ) + + _joins, _where = self.make_joins_from_selected('rating', model) + joins.extend(_joins or []) + where.extend(_where or []) + where.append(self.make_default_where(db_table=model._meta.db_table)) + self.make_date_begin_where(where, db_table=model._meta.db_table) + selects.append(select + ''.join(joins) + ' where ' + ' and '.join(where) + group_by) + + sql = ' union '.join(selects) + choices = [] + if sql: + c = connection.cursor() + try: + c.execute(sql) + mapper = namedtuple('Result', [col[0] for col in c.description]) + data = [mapper(*raw) for raw in c.fetchall()] + for value, label in RATING: + count = sum([getattr(x, value, 0) or 0 for x in data]) + choices.append((value, label + ' ({count})'.format(count=count))) + finally: + c.close() + # some bug with these! AttributeError: __exit__ + # with connection.cursor() as c: + return choices + def get_date_begin_periods(self): periods = getattr(self, '_periods', None) if periods is None: @@ -751,7 +824,7 @@ class FilterForm(forms.Form): return None def make_date_begin_where(self, where, db_table): - key = '_where_date_begin_where_{}'.format(db_table) + key = '_make_date_begin_where_{}'.format(db_table) _where = getattr(self, key, None) if _where is None: _where = [] @@ -791,9 +864,34 @@ class FilterForm(forms.Form): _where = [''' ({}) '''.format(' OR '.join(_where))] setattr(self, key, _where) # print(_where) - where.extend(_where) + where.extend(_where or []) return + def make_rating_where(self, where, db_table): + key = '_make_rating_where_{}'.format(db_table) + _where = getattr(self, key, None) + if _where is None: + cleaned = self.cleaned_data.get('rating', []) + _where = [] + # choices = filter(lambda x: x['value'] in cleaned and x['field'] == 'expohit', rating_mapping) + for choice in filter(lambda x: x['field'] == 'expohit' and x['value'] in cleaned, rating_mapping): + _where.append( + ''' `{db_table}`.`{field}` = {db_value} '''\ + .format(field=choice['field'], db_value=choice['db_value'], db_table=db_table) + ) + quality_label = [] + for choice in filter(lambda x: x['field'] == 'quality_label' and x['value'] in cleaned, rating_mapping): + quality_label.append(choice['db_value']) + if quality_label: + _where.append( + ''' `{db_table}`.`{field}` = `{db_table}`.`{field}` | {computed} '''\ + .format(field=choice['field'], computed=reduce(operator.or_, quality_label), db_table=db_table) + ) + if len(_where) > 1: + _where = [''' ({}) '''.format(' OR '.join(_where))] + setattr(self, key, _where) + where.extend(_where or []) + return # def get_prev_month(self, date): # year = date.year # month = date.month @@ -894,6 +992,19 @@ class FilterForm(forms.Form): params |= SQ(**lookup) return params + def make_rating_sqs_params(self): + params = None + if self.cleaned_data.get('rating'): + for choice in filter(lambda x: x['field'] == 'expohit' and x['value'] in self.cleaned_data.get('rating'), rating_mapping): + params = SQ(**{choice['field']: choice['db_value']}) + quality_label = [x['db_value'] for x in filter(lambda x: x['field'] != 'expohit' and x['value'] in self.cleaned_data.get('rating'), rating_mapping)] + if quality_label: + if params is None: + params = SQ(**{'quality_label__in': quality_label}) + else: + params |= SQ(**{'quality_label__in': quality_label}) + return params + def make_count_select(self, field): selects = [] case = None @@ -953,10 +1064,11 @@ class FilterForm(forms.Form): # FILTER current by other values _joins, _where = self.make_joins_from_selected(field, model) - joins.extend(_joins) - where.extend(_where) + joins.extend(_joins or []) + where.extend(_where or []) where.append(self.make_default_where(db_table=model._meta.db_table)) self.make_date_begin_where(where, db_table=model._meta.db_table) + self.make_rating_where(where, db_table=model._meta.db_table) selects.append(select + ''.join(joins) + ' where ' + ' and '.join(where) + group_by) if len(selects) == 2: diff --git a/exposition/search_indexes.py b/exposition/search_indexes.py index 347253a1..1e9dbd29 100644 --- a/exposition/search_indexes.py +++ b/exposition/search_indexes.py @@ -28,6 +28,8 @@ class ExpositionIndex(indexes.SearchIndex, indexes.Indexable, ExpoSearchMixin): members_choice = indexes.IntegerField() visitors_choice = indexes.IntegerField() price_choice = indexes.IntegerField() + expohit = indexes.BooleanField(model_attr='expohit') + rating = indexes.MultiValueField() def prepare_form_name(self, obj): return None diff --git a/functions/search_mixin.py b/functions/search_mixin.py index 65a78964..6e0f2170 100644 --- a/functions/search_mixin.py +++ b/functions/search_mixin.py @@ -71,3 +71,6 @@ class ExpoSearchMixin(object): def prepare_price_choice(self, obj): return obj.price_choice or 0 + def prepare_rating(self, obj): + q = obj.quality_label + return [x.mask for x in filter(lambda x: x.is_set == True, [getattr(q, key) for key in q.iterkeys()])] diff --git a/schema_22.08.2016.xml b/schema_22.08.2016.xml new file mode 100644 index 00000000..7621ea32 --- /dev/null +++ b/schema_22.08.2016.xml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id + + + text + + + + + diff --git a/schema_new.xml b/schema_new.xml index a5cc8484..5f140474 100644 --- a/schema_new.xml +++ b/schema_new.xml @@ -175,6 +175,8 @@ + + @@ -183,6 +185,8 @@ + +