django-mp-prom 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_mp_prom-0.0.1/MANIFEST.in +7 -0
- django_mp_prom-0.0.1/PKG-INFO +15 -0
- django_mp_prom-0.0.1/README.md +98 -0
- django_mp_prom-0.0.1/django_mp_prom.egg-info/PKG-INFO +15 -0
- django_mp_prom-0.0.1/django_mp_prom.egg-info/SOURCES.txt +10 -0
- django_mp_prom-0.0.1/django_mp_prom.egg-info/dependency_links.txt +1 -0
- django_mp_prom-0.0.1/django_mp_prom.egg-info/top_level.txt +1 -0
- django_mp_prom-0.0.1/prom/__init__.py +0 -0
- django_mp_prom-0.0.1/prom/apps.py +7 -0
- django_mp_prom-0.0.1/prom/feed.py +409 -0
- django_mp_prom-0.0.1/setup.cfg +4 -0
- django_mp_prom-0.0.1/setup.py +19 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-mp-prom
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Django Prom.ua export
|
|
5
|
+
Home-page: https://github.com/pmaigutyak/mp-prom
|
|
6
|
+
Download-URL: https://github.com/pmaigutyak/mp-prom/archive/0.0.1.tar.gz
|
|
7
|
+
Author: Paul Maigutyak
|
|
8
|
+
Author-email: pmaigutyak@gmail.com
|
|
9
|
+
License: MIT
|
|
10
|
+
Dynamic: author
|
|
11
|
+
Dynamic: author-email
|
|
12
|
+
Dynamic: download-url
|
|
13
|
+
Dynamic: home-page
|
|
14
|
+
Dynamic: license
|
|
15
|
+
Dynamic: summary
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# MP-prom
|
|
2
|
+
|
|
3
|
+
Django Prom.ua product export.
|
|
4
|
+
|
|
5
|
+
### Installation
|
|
6
|
+
|
|
7
|
+
Install with pip:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install django-mp-prom
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Add `prom` to `INSTALLED_APPS`.
|
|
14
|
+
|
|
15
|
+
Set `DOMAIN` in settings. The `host` used for absolute links is `https://<DOMAIN>`.
|
|
16
|
+
|
|
17
|
+
### Export
|
|
18
|
+
|
|
19
|
+
Subclass `prom.feed.ExportStrategy`. Implement `get_queryset` and every required product field. Override an optional field when the shop has that information.
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from django.urls import path
|
|
23
|
+
|
|
24
|
+
from prom.feed import ExportStrategy
|
|
25
|
+
from products.models import Product
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ProductExport(ExportStrategy):
|
|
29
|
+
|
|
30
|
+
def get_queryset(self):
|
|
31
|
+
return Product.objects.filter(is_visible=True)
|
|
32
|
+
|
|
33
|
+
def get_id(self, product):
|
|
34
|
+
return product.pk
|
|
35
|
+
|
|
36
|
+
def get_name(self, product):
|
|
37
|
+
return product.name
|
|
38
|
+
|
|
39
|
+
def get_description(self, product):
|
|
40
|
+
return product.description
|
|
41
|
+
|
|
42
|
+
def get_price(self, product):
|
|
43
|
+
return product.price
|
|
44
|
+
|
|
45
|
+
def get_currency(self, product):
|
|
46
|
+
return 'UAH'
|
|
47
|
+
|
|
48
|
+
def get_availability(self, product):
|
|
49
|
+
return '+' if product.is_available else '-'
|
|
50
|
+
|
|
51
|
+
def get_code(self, product):
|
|
52
|
+
return product.code
|
|
53
|
+
|
|
54
|
+
def get_images(self, product):
|
|
55
|
+
if not product.logo:
|
|
56
|
+
return None
|
|
57
|
+
return f'{self.host}{product.logo.url}'
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
urlpatterns = [
|
|
61
|
+
path('prom.xls', ProductExport.as_view(), name='prom'),
|
|
62
|
+
]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Required product fields: `id`, `name`, `description`, `price`, `currency`, `availability`.
|
|
66
|
+
|
|
67
|
+
`availability` is a Prom value: `+` in stock, `-` out of stock, `@` service, `!` ready to ship, or a number of days.
|
|
68
|
+
|
|
69
|
+
Optional product fields stay out of the file until overridden: `code`, `name_ua`, `keywords`, `keywords_ua`, `description_ua`, `product_type`, `price_from`, `measure_unit`, `minimum_order`, `wholesale_price`, `minimum_wholesale_order`, `quantity`, `images`, `weight`, `width`, `height`, `length`, `discount`, `discount_date_start`, `discount_date_end`, `label`, `vendor`, `country`, `html_title`, `html_title_ua`, `html_description`, `html_description_ua`, `gifts`, `related`, `gift_ids`, `related_ids`, `group_number`, `group_name`, `catalog_url`, `packaging`, `notes`, `product_url`, `gtin`, `mpn`, `unique_id`, `catalog_id`, `group_id`, `variation_group_id`, `location`.
|
|
70
|
+
|
|
71
|
+
A list is written as one cell joined with a comma and a space. `get_images` can return a list of URLs. Empty values are written as blank cells.
|
|
72
|
+
|
|
73
|
+
### Groups
|
|
74
|
+
|
|
75
|
+
The workbook always has `Export Products Sheet` and `Export Groups Sheet`. Override `get_groups` to add rows. Required group fields are `get_groups_number`, `get_groups_name` and `get_groups_id`.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
def get_groups(self):
|
|
79
|
+
return Category.objects.all()
|
|
80
|
+
|
|
81
|
+
def get_groups_number(self, group):
|
|
82
|
+
return group.pk
|
|
83
|
+
|
|
84
|
+
def get_groups_name(self, group):
|
|
85
|
+
return group.name
|
|
86
|
+
|
|
87
|
+
def get_groups_id(self, group):
|
|
88
|
+
return group.pk
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Optional group fields stay out of the sheet until overridden: `name_ua`, `parent_number`, `parent_id`, `image`, `html_title`, `html_title_ua`, `html_description`, `html_description_ua`, `description_before`, `description_before_ua`, `description_after`, `description_after_ua`.
|
|
92
|
+
|
|
93
|
+
The file is cached at `tmp/prom.xls` for one hour.
|
|
94
|
+
|
|
95
|
+
### Requirements
|
|
96
|
+
|
|
97
|
+
* Django
|
|
98
|
+
* xlwt
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-mp-prom
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Django Prom.ua export
|
|
5
|
+
Home-page: https://github.com/pmaigutyak/mp-prom
|
|
6
|
+
Download-URL: https://github.com/pmaigutyak/mp-prom/archive/0.0.1.tar.gz
|
|
7
|
+
Author: Paul Maigutyak
|
|
8
|
+
Author-email: pmaigutyak@gmail.com
|
|
9
|
+
License: MIT
|
|
10
|
+
Dynamic: author
|
|
11
|
+
Dynamic: author-email
|
|
12
|
+
Dynamic: download-url
|
|
13
|
+
Dynamic: home-page
|
|
14
|
+
Dynamic: license
|
|
15
|
+
Dynamic: summary
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
prom
|
|
File without changes
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import xlwt
|
|
6
|
+
from django.conf import settings
|
|
7
|
+
from django.http import HttpResponse, HttpResponseNotFound
|
|
8
|
+
from django.views import View
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
FEED_FILE_NAME = 'prom.xls'
|
|
12
|
+
FEED_MAX_AGE = 60 * 60
|
|
13
|
+
PRODUCTS_SHEET = 'Export Products Sheet'
|
|
14
|
+
GROUPS_SHEET = 'Export Groups Sheet'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExportStrategy(View):
|
|
18
|
+
"""Prom.ua Excel export.
|
|
19
|
+
|
|
20
|
+
Subclass this view, implement get_queryset and the required product
|
|
21
|
+
field methods. Override an optional field when the shop has that
|
|
22
|
+
information. Override get_groups and the group field methods to fill
|
|
23
|
+
the groups sheet. The shop domain is read from settings.DOMAIN.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
file_name = FEED_FILE_NAME
|
|
27
|
+
max_age = FEED_MAX_AGE
|
|
28
|
+
content_type = 'application/ms-excel'
|
|
29
|
+
list_separator = ', '
|
|
30
|
+
product_fields = (
|
|
31
|
+
('code', 'Код_товару'),
|
|
32
|
+
('name', 'Назва_позиції'),
|
|
33
|
+
('name_ua', 'Назва_позиції_укр'),
|
|
34
|
+
('keywords', 'Пошукові_запити'),
|
|
35
|
+
('keywords_ua', 'Пошукові_запити_укр'),
|
|
36
|
+
('description', 'Опис'),
|
|
37
|
+
('description_ua', 'Опис_укр'),
|
|
38
|
+
('product_type', 'Тип_товару'),
|
|
39
|
+
('price', 'Ціна'),
|
|
40
|
+
('price_from', 'Ціна_від'),
|
|
41
|
+
('currency', 'Валюта'),
|
|
42
|
+
('measure_unit', 'Одиниця_виміру'),
|
|
43
|
+
('minimum_order', 'Мінімальний_обсяг_замовлення'),
|
|
44
|
+
('wholesale_price', 'Оптова_ціна'),
|
|
45
|
+
('minimum_wholesale_order', 'Мінімальне_замовлення_опт'),
|
|
46
|
+
('quantity', 'Кількість'),
|
|
47
|
+
('images', 'Посилання_зображення'),
|
|
48
|
+
('weight', 'Вага,кг'),
|
|
49
|
+
('width', 'Ширина,см'),
|
|
50
|
+
('height', 'Висота,см'),
|
|
51
|
+
('length', 'Довжина,см'),
|
|
52
|
+
('availability', 'Наявність'),
|
|
53
|
+
('discount', 'Знижка'),
|
|
54
|
+
('discount_date_start', 'Термін_дії_знижки_від'),
|
|
55
|
+
('discount_date_end', 'Термін_дії_знижки_до'),
|
|
56
|
+
('label', 'Ярлик'),
|
|
57
|
+
('vendor', 'Виробник'),
|
|
58
|
+
('country', 'Країна_виробник'),
|
|
59
|
+
('html_title', 'HTML_заголовок'),
|
|
60
|
+
('html_title_ua', 'HTML_заголовок_укр'),
|
|
61
|
+
('html_description', 'HTML_опис'),
|
|
62
|
+
('html_description_ua', 'HTML_опис_укр'),
|
|
63
|
+
('gifts', 'Подарунки'),
|
|
64
|
+
('related', 'Супутні'),
|
|
65
|
+
('gift_ids', 'ID_Подарунків'),
|
|
66
|
+
('related_ids', 'ID_Супутніх'),
|
|
67
|
+
('group_number', 'Номер_групи'),
|
|
68
|
+
('group_name', 'Назва_групи'),
|
|
69
|
+
('catalog_url', 'Посилання_підрозділу'),
|
|
70
|
+
('packaging', 'Спосіб_пакування'),
|
|
71
|
+
('notes', 'Особисті_нотатки'),
|
|
72
|
+
('product_url', 'Продукт_на_сайті'),
|
|
73
|
+
('gtin', 'Код_маркування_(GTIN)'),
|
|
74
|
+
('mpn', 'Номер_пристрою_(MPN)'),
|
|
75
|
+
('id', 'Ідентифікатор_товару'),
|
|
76
|
+
('unique_id', 'Унікальний_ідентифікатор'),
|
|
77
|
+
('catalog_id', 'Ідентифікатор_підрозділу'),
|
|
78
|
+
('group_id', 'Ідентифікатор_групи'),
|
|
79
|
+
('variation_group_id', 'ID_групи_різновидів'),
|
|
80
|
+
('location', 'Де_знаходиться_товар'),
|
|
81
|
+
)
|
|
82
|
+
required_product_fields = (
|
|
83
|
+
'id',
|
|
84
|
+
'name',
|
|
85
|
+
'description',
|
|
86
|
+
'price',
|
|
87
|
+
'currency',
|
|
88
|
+
'availability',
|
|
89
|
+
)
|
|
90
|
+
group_fields = (
|
|
91
|
+
('number', 'Номер_групи'),
|
|
92
|
+
('name', 'Назва_групи'),
|
|
93
|
+
('name_ua', 'Назва_групи_укр'),
|
|
94
|
+
('id', 'Ідентифікатор_групи'),
|
|
95
|
+
('parent_number', 'Номер_батьківської_групи'),
|
|
96
|
+
('parent_id', 'Ідентифікатор_батьківської_групи'),
|
|
97
|
+
('image', 'Посилання_зображення_групи'),
|
|
98
|
+
('html_title', 'HTML_заголовок_групи'),
|
|
99
|
+
('html_title_ua', 'HTML_заголовок_групи_укр'),
|
|
100
|
+
('html_description', 'HTML_опис_групи'),
|
|
101
|
+
('html_description_ua', 'HTML_опис_групи_укр'),
|
|
102
|
+
('description_before', 'Опис_групи_до_списку_товарних_позицій'),
|
|
103
|
+
(
|
|
104
|
+
'description_before_ua',
|
|
105
|
+
'Опис_групи_до_списку_товарних_позицій_укр',
|
|
106
|
+
),
|
|
107
|
+
('description_after', 'Опис_групи_після_списку_товарних_позицій'),
|
|
108
|
+
(
|
|
109
|
+
'description_after_ua',
|
|
110
|
+
'Опис_групи_після_списку_товарних_позицій_укр',
|
|
111
|
+
),
|
|
112
|
+
)
|
|
113
|
+
required_group_fields = ('number', 'name', 'id')
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def host(self):
|
|
117
|
+
return f'https://{settings.DOMAIN}'
|
|
118
|
+
|
|
119
|
+
def get_queryset(self):
|
|
120
|
+
raise NotImplementedError
|
|
121
|
+
|
|
122
|
+
def get_groups(self):
|
|
123
|
+
return ()
|
|
124
|
+
|
|
125
|
+
def get_id(self, product):
|
|
126
|
+
raise NotImplementedError
|
|
127
|
+
|
|
128
|
+
def get_name(self, product):
|
|
129
|
+
raise NotImplementedError
|
|
130
|
+
|
|
131
|
+
def get_description(self, product):
|
|
132
|
+
raise NotImplementedError
|
|
133
|
+
|
|
134
|
+
def get_price(self, product):
|
|
135
|
+
raise NotImplementedError
|
|
136
|
+
|
|
137
|
+
def get_currency(self, product):
|
|
138
|
+
raise NotImplementedError
|
|
139
|
+
|
|
140
|
+
def get_availability(self, product):
|
|
141
|
+
raise NotImplementedError
|
|
142
|
+
|
|
143
|
+
def get_code(self, product):
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
def get_name_ua(self, product):
|
|
147
|
+
return None
|
|
148
|
+
|
|
149
|
+
def get_keywords(self, product):
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
def get_keywords_ua(self, product):
|
|
153
|
+
return None
|
|
154
|
+
|
|
155
|
+
def get_description_ua(self, product):
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
def get_product_type(self, product):
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
def get_price_from(self, product):
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
def get_measure_unit(self, product):
|
|
165
|
+
return None
|
|
166
|
+
|
|
167
|
+
def get_minimum_order(self, product):
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
def get_wholesale_price(self, product):
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
def get_minimum_wholesale_order(self, product):
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
def get_quantity(self, product):
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
def get_images(self, product):
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
def get_weight(self, product):
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
def get_width(self, product):
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
def get_height(self, product):
|
|
189
|
+
return None
|
|
190
|
+
|
|
191
|
+
def get_length(self, product):
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
def get_discount(self, product):
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
def get_discount_date_start(self, product):
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
def get_discount_date_end(self, product):
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
def get_label(self, product):
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
def get_vendor(self, product):
|
|
207
|
+
return None
|
|
208
|
+
|
|
209
|
+
def get_country(self, product):
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
def get_html_title(self, product):
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
def get_html_title_ua(self, product):
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
def get_html_description(self, product):
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
def get_html_description_ua(self, product):
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
def get_gifts(self, product):
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
def get_related(self, product):
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
def get_gift_ids(self, product):
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
def get_related_ids(self, product):
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
def get_group_number(self, product):
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
def get_group_name(self, product):
|
|
240
|
+
return None
|
|
241
|
+
|
|
242
|
+
def get_catalog_url(self, product):
|
|
243
|
+
return None
|
|
244
|
+
|
|
245
|
+
def get_packaging(self, product):
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
def get_notes(self, product):
|
|
249
|
+
return None
|
|
250
|
+
|
|
251
|
+
def get_product_url(self, product):
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
def get_gtin(self, product):
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
def get_mpn(self, product):
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
def get_unique_id(self, product):
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
def get_catalog_id(self, product):
|
|
264
|
+
return None
|
|
265
|
+
|
|
266
|
+
def get_group_id(self, product):
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
def get_variation_group_id(self, product):
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
def get_location(self, product):
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
def get_groups_number(self, group):
|
|
276
|
+
raise NotImplementedError
|
|
277
|
+
|
|
278
|
+
def get_groups_name(self, group):
|
|
279
|
+
raise NotImplementedError
|
|
280
|
+
|
|
281
|
+
def get_groups_id(self, group):
|
|
282
|
+
raise NotImplementedError
|
|
283
|
+
|
|
284
|
+
def get_groups_name_ua(self, group):
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
def get_groups_parent_number(self, group):
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
def get_groups_parent_id(self, group):
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
def get_groups_image(self, group):
|
|
294
|
+
return None
|
|
295
|
+
|
|
296
|
+
def get_groups_html_title(self, group):
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
def get_groups_html_title_ua(self, group):
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
def get_groups_html_description(self, group):
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
def get_groups_html_description_ua(self, group):
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
def get_groups_description_before(self, group):
|
|
309
|
+
return None
|
|
310
|
+
|
|
311
|
+
def get_groups_description_before_ua(self, group):
|
|
312
|
+
return None
|
|
313
|
+
|
|
314
|
+
def get_groups_description_after(self, group):
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
def get_groups_description_after_ua(self, group):
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
def get_feed_path(self):
|
|
321
|
+
return Path(settings.BASE_DIR) / 'tmp' / self.file_name
|
|
322
|
+
|
|
323
|
+
def get(self, request, *args, **kwargs):
|
|
324
|
+
path = self.get_feed_path()
|
|
325
|
+
if self._is_stale(path):
|
|
326
|
+
self.write_feed(path)
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
data = path.read_bytes()
|
|
330
|
+
except OSError:
|
|
331
|
+
return HttpResponseNotFound()
|
|
332
|
+
|
|
333
|
+
response = HttpResponse(data, content_type=self.content_type)
|
|
334
|
+
response['Content-Disposition'] = (
|
|
335
|
+
f'attachment; filename="{self.file_name}"'
|
|
336
|
+
)
|
|
337
|
+
return response
|
|
338
|
+
|
|
339
|
+
def _is_stale(self, path):
|
|
340
|
+
try:
|
|
341
|
+
mtime = path.stat().st_mtime
|
|
342
|
+
except OSError:
|
|
343
|
+
return True
|
|
344
|
+
return time.time() - mtime > self.max_age
|
|
345
|
+
|
|
346
|
+
def write_feed(self, path):
|
|
347
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
348
|
+
path.write_bytes(self.render_feed())
|
|
349
|
+
|
|
350
|
+
def render_feed(self):
|
|
351
|
+
book = xlwt.Workbook(encoding='utf-8')
|
|
352
|
+
self._write_sheet(
|
|
353
|
+
book.add_sheet(PRODUCTS_SHEET),
|
|
354
|
+
self._columns(
|
|
355
|
+
self.product_fields,
|
|
356
|
+
self.required_product_fields,
|
|
357
|
+
'get_',
|
|
358
|
+
),
|
|
359
|
+
self.get_queryset(),
|
|
360
|
+
)
|
|
361
|
+
self._write_sheet(
|
|
362
|
+
book.add_sheet(GROUPS_SHEET),
|
|
363
|
+
self._columns(
|
|
364
|
+
self.group_fields,
|
|
365
|
+
self.required_group_fields,
|
|
366
|
+
'get_groups_',
|
|
367
|
+
),
|
|
368
|
+
self.get_groups(),
|
|
369
|
+
)
|
|
370
|
+
stream = BytesIO()
|
|
371
|
+
book.save(stream)
|
|
372
|
+
return stream.getvalue()
|
|
373
|
+
|
|
374
|
+
def _columns(self, fields, required, prefix):
|
|
375
|
+
columns = []
|
|
376
|
+
for key, header in fields:
|
|
377
|
+
method = prefix + key
|
|
378
|
+
if key in required or self._overridden(method):
|
|
379
|
+
columns.append((method, header))
|
|
380
|
+
return columns
|
|
381
|
+
|
|
382
|
+
def _overridden(self, method):
|
|
383
|
+
return getattr(type(self), method) is not getattr(
|
|
384
|
+
ExportStrategy, method
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
def _write_sheet(self, sheet, columns, rows):
|
|
388
|
+
for index, (_method, header) in enumerate(columns):
|
|
389
|
+
sheet.write(0, index, header)
|
|
390
|
+
|
|
391
|
+
for row_index, item in enumerate(rows, start=1):
|
|
392
|
+
for index, (method, _header) in enumerate(columns):
|
|
393
|
+
sheet.write(
|
|
394
|
+
row_index,
|
|
395
|
+
index,
|
|
396
|
+
self._cell(getattr(self, method)(item)),
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
def _cell(self, value):
|
|
400
|
+
if isinstance(value, (list, tuple)):
|
|
401
|
+
parts = [
|
|
402
|
+
str(item)
|
|
403
|
+
for item in value
|
|
404
|
+
if item is not None and item != ''
|
|
405
|
+
]
|
|
406
|
+
value = self.list_separator.join(parts)
|
|
407
|
+
if value is None:
|
|
408
|
+
return ''
|
|
409
|
+
return value
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
version = '0.0.1'
|
|
5
|
+
url = 'https://github.com/pmaigutyak/mp-prom'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
setup(
|
|
9
|
+
name='django-mp-prom',
|
|
10
|
+
version=version,
|
|
11
|
+
description='Django Prom.ua export',
|
|
12
|
+
author='Paul Maigutyak',
|
|
13
|
+
author_email='pmaigutyak@gmail.com',
|
|
14
|
+
url=url,
|
|
15
|
+
download_url='{}/archive/{}.tar.gz'.format(url, version),
|
|
16
|
+
packages=find_packages(),
|
|
17
|
+
include_package_data=True,
|
|
18
|
+
license='MIT',
|
|
19
|
+
)
|