/
price.py
164 lines (132 loc) · 5.54 KB
/
price.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from collections import defaultdict
import os
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db.models import Q, QuerySet
from django.template.loader import render_to_string
from django.urls import reverse
from shopelectro.models import Product, Category, Tag
class PriceFilter:
"""Filter offers with individual price requirements."""
FILTERS = defaultdict(
lambda: (lambda qs: qs),
# Yandex Market feed requires picture for every offer
YM=lambda qs: (
qs
.filter(page__images__isnull=False)
.distinct()
),
# Google Merchant feed should not contain offers cheaper then CONST
GM=lambda qs: (
qs
.filter(price__gt=settings.PRICE_GM_LOWER_BOUND)
)
)
def __init__(self, utm: str):
assert utm in settings.UTM_PRICE_MAP
self.utm = utm
def run(self, query_set: QuerySet) -> QuerySet:
return self.FILTERS[self.utm](query_set)
# @todo #661:120m Refactor price command.
# Create Price class with it's own price processing.
class Command(BaseCommand):
"""Generate yml file for a given vendor (YM or price.ru)."""
# price files will be stored at this dir
BASE_DIR = settings.ASSETS_DIR
IGNORED_CATEGORIES = [
'Измерительные приборы', 'Новогодние вращающиеся светодиодные лампы',
'Новогодние лазерные проекторы', 'MP3- колонки', 'Беспроводные звонки',
'Радиоприёмники', 'Фонари', 'Отвертки', 'Весы электронные портативные',
]
# dict keys are url targets for every service
IGNORED_CATEGORIES_MAP = defaultdict(list, {
'GM': ['Усилители звука для слабослышащих'],
})
UTM_MEDIUM_DATA = defaultdict(
lambda: 'cpc',
{'YM': 'cpc-market'}
)
def create_prices(self):
for target in settings.UTM_PRICE_MAP.items():
self.generate_yml(*target)
def handle(self, *args, **options):
self.create_prices()
@classmethod
def get_context_for_yml(cls, utm):
"""Create context dictionary for rendering files."""
def put_utm(product):
"""Put UTM attribute to product."""
utm_marks = [
('utm_source', utm),
('utm_medium', cls.UTM_MEDIUM_DATA[utm]),
('utm_content', product.get_root_category().page.slug),
('utm_term', str(product.vendor_code)),
]
url = reverse('product', args=(product.vendor_code,))
utm_mark_query = '&'.join(f'{k}={v}' for k, v in utm_marks)
product.utm_url = f'{settings.BASE_URL}{url}?{utm_mark_query}'
product.prepared_params = [
(group, tags[0].name)
for (group, tags) in filter(
lambda x: x[0].name != 'Производитель',
product.get_params()
) if tags
]
return product
def put_crumbs(product): # Ignore PyDocStyleBear
"""Crumbs for google merchant. https://goo.gl/b0UJQp"""
product.crumbs = ' > '.join(
product.page.get_ancestors_fields('h1', include_self=False)[1:]
)
return product
def put_brand(product, brands):
product.brand = brands.get(product)
return product
def filter_categories(utm):
categories_to_exclude = (
Category.objects
.filter(
Q(name__in=cls.IGNORED_CATEGORIES)
| Q(name__in=cls.IGNORED_CATEGORIES_MAP[utm])
)
.get_descendants(include_self=True)
)
result_categories = Category.objects.exclude(id__in=categories_to_exclude)
if utm == 'YM':
"""
Yandex Market feed requires items in some categories to have pictures
To simplify filtering we are excluding all categories
which don't contain at least one product with picture
"""
result_categories = result_categories.get_categories_tree_with_pictures()
return result_categories
def prepare_products(categories_, utm):
"""Filter product list and patch it for rendering."""
products = PriceFilter(utm).run(
Product.objects.active().filter_by_categories(categories_)
)
brands = Tag.objects.get_brands(products)
return [
put_brand(put_crumbs(put_utm(product)), brands)
for product in products
]
categories = (
filter_categories(utm) if utm != 'SE78'
else Category.objects.all()
)
products = prepare_products(categories, utm)
return {
'base_url': settings.BASE_URL,
'categories': categories,
'products': products,
'shop': settings.SHOP,
'utm': utm,
}
@classmethod
def generate_yml(cls, utm, file_name):
"""Generate yml file."""
file_to_write = os.path.join(cls.BASE_DIR, file_name)
context = cls.get_context_for_yml(utm)
with open(file_to_write, 'w', encoding='utf-8') as file:
file.write(render_to_string('prices/price.yml', context).strip())
return '{} generated...'.format(file_name)