django-mp-avtopro 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.

Potentially problematic release.


This version of django-mp-avtopro might be problematic. Click here for more details.

@@ -0,0 +1,7 @@
1
+ include README.md
2
+ include LICENSE
3
+ include requirements.txt
4
+ recursive-include avtopro/locale *
5
+ recursive-include avtopro/static *
6
+ recursive-include avtopro/templates *
7
+ prune *.pyc
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-mp-avtopro
3
+ Version: 0.0.1
4
+ Summary: Django AvtoPro export
5
+ Home-page: https://github.com/pmaigutyak/mp-avtopto
6
+ Download-URL: https://github.com/pmaigutyak/mp-avtopto/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,76 @@
1
+ # MP-avtopro
2
+
3
+ Django AvtoPro price-list export.
4
+
5
+ ### Installation
6
+
7
+ Install with pip:
8
+
9
+ ```
10
+ pip install django-mp-avtopro
11
+ ```
12
+
13
+ Add `avtopro` to `INSTALLED_APPS`.
14
+
15
+ Set `DOMAIN` in settings. The `host` used for absolute links is `https://<DOMAIN>`.
16
+
17
+ ### Export
18
+
19
+ Subclass `avtopro.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 avtopro.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_vendor(self, product):
34
+ return product.manufacturer.name
35
+
36
+ def get_price(self, product):
37
+ return product.price
38
+
39
+ def get_quantity(self, product):
40
+ return 1
41
+
42
+ def get_code(self, product):
43
+ return product.code
44
+
45
+ def get_description(self, product):
46
+ return product.description
47
+
48
+ def get_used(self, product):
49
+ if product.is_new:
50
+ return None
51
+ return '1'
52
+
53
+ def get_images(self, product):
54
+ if not product.logo:
55
+ return None
56
+ return f'{self.host}{product.logo.url}'
57
+
58
+
59
+ urlpatterns = [
60
+ path('avtopro.csv', ProductExport.as_view(), name='avtopro'),
61
+ ]
62
+ ```
63
+
64
+ Required fields: `vendor`, `price`, `quantity`, `code`.
65
+
66
+ Optional fields stay out of the file until overridden: `description`, `used`, `images`, `restored`.
67
+
68
+ `used` is the `Б/У` column. Any non-empty value marks the part as used. Leave it empty for a new part. `restored` is the `Реставрація` column and works the same way.
69
+
70
+ A list is written as one cell joined with `;`. `get_images` can return a list of URLs. Empty values are written as blank cells.
71
+
72
+ The file is cached at `tmp/avtopro.csv` for one hour.
73
+
74
+ ### Requirements
75
+
76
+ * Django
File without changes
@@ -0,0 +1,7 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class AvtoproConfig(AppConfig):
5
+
6
+ name = 'avtopro'
7
+ verbose_name = 'Avtopro'
@@ -0,0 +1,140 @@
1
+ import csv
2
+ import time
3
+ from io import StringIO
4
+ from pathlib import Path
5
+
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 = 'avtopro.csv'
12
+ FEED_MAX_AGE = 60 * 60
13
+
14
+
15
+ class ExportStrategy(View):
16
+ """AvtoPro price-list export.
17
+
18
+ Subclass this view, implement get_queryset and the required product
19
+ field methods. Override an optional field when the shop has that
20
+ information. The shop domain is read from settings.DOMAIN.
21
+ """
22
+
23
+ file_name = FEED_FILE_NAME
24
+ max_age = FEED_MAX_AGE
25
+ content_type = 'text/csv'
26
+ list_separator = ';'
27
+ product_fields = (
28
+ ('vendor', 'Виробник'),
29
+ ('price', 'Ціна'),
30
+ ('quantity', 'Кількість'),
31
+ ('code', 'Код'),
32
+ ('description', 'Опис'),
33
+ ('used', 'Б/У'),
34
+ ('images', 'Фото'),
35
+ ('restored', 'Реставрація'),
36
+ )
37
+ required_product_fields = ('vendor', 'price', 'quantity', 'code')
38
+
39
+ @property
40
+ def host(self):
41
+ return f'https://{settings.DOMAIN}'
42
+
43
+ def get_queryset(self):
44
+ raise NotImplementedError
45
+
46
+ def get_vendor(self, product):
47
+ raise NotImplementedError
48
+
49
+ def get_price(self, product):
50
+ raise NotImplementedError
51
+
52
+ def get_quantity(self, product):
53
+ raise NotImplementedError
54
+
55
+ def get_code(self, product):
56
+ raise NotImplementedError
57
+
58
+ def get_description(self, product):
59
+ return None
60
+
61
+ def get_used(self, product):
62
+ return None
63
+
64
+ def get_images(self, product):
65
+ return None
66
+
67
+ def get_restored(self, product):
68
+ return None
69
+
70
+ def get_feed_path(self):
71
+ return Path(settings.BASE_DIR) / 'tmp' / self.file_name
72
+
73
+ def get(self, request, *args, **kwargs):
74
+ path = self.get_feed_path()
75
+ if self._is_stale(path):
76
+ self.write_feed(path)
77
+
78
+ try:
79
+ data = path.read_bytes()
80
+ except OSError:
81
+ return HttpResponseNotFound()
82
+
83
+ response = HttpResponse(data, content_type=self.content_type)
84
+ response['Content-Disposition'] = (
85
+ f'attachment; filename="{self.file_name}"'
86
+ )
87
+ return response
88
+
89
+ def _is_stale(self, path):
90
+ try:
91
+ mtime = path.stat().st_mtime
92
+ except OSError:
93
+ return True
94
+ return time.time() - mtime > self.max_age
95
+
96
+ def write_feed(self, path):
97
+ path.parent.mkdir(parents=True, exist_ok=True)
98
+ path.write_bytes(self.render_feed())
99
+
100
+ def render_feed(self):
101
+ stream = StringIO()
102
+ writer = csv.writer(stream)
103
+ columns = self._columns()
104
+ writer.writerow([header for _method, header in columns])
105
+
106
+ for product in self.get_queryset():
107
+ writer.writerow([
108
+ self._cell(getattr(self, method)(product))
109
+ for method, _header in columns
110
+ ])
111
+
112
+ return stream.getvalue().encode('utf-8')
113
+
114
+ def _columns(self):
115
+ columns = []
116
+ for key, header in self.product_fields:
117
+ method = f'get_{key}'
118
+ if (
119
+ key in self.required_product_fields
120
+ or self._overridden(method)
121
+ ):
122
+ columns.append((method, header))
123
+ return columns
124
+
125
+ def _overridden(self, method):
126
+ return getattr(type(self), method) is not getattr(
127
+ ExportStrategy, method
128
+ )
129
+
130
+ def _cell(self, value):
131
+ if isinstance(value, (list, tuple)):
132
+ parts = [
133
+ str(item)
134
+ for item in value
135
+ if item is not None and item != ''
136
+ ]
137
+ value = self.list_separator.join(parts)
138
+ if value is None:
139
+ return ''
140
+ return value
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-mp-avtopro
3
+ Version: 0.0.1
4
+ Summary: Django AvtoPro export
5
+ Home-page: https://github.com/pmaigutyak/mp-avtopto
6
+ Download-URL: https://github.com/pmaigutyak/mp-avtopto/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,10 @@
1
+ MANIFEST.in
2
+ README.md
3
+ setup.py
4
+ avtopro/__init__.py
5
+ avtopro/apps.py
6
+ avtopro/feed.py
7
+ django_mp_avtopro.egg-info/PKG-INFO
8
+ django_mp_avtopro.egg-info/SOURCES.txt
9
+ django_mp_avtopro.egg-info/dependency_links.txt
10
+ django_mp_avtopro.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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-avtopto'
6
+
7
+
8
+ setup(
9
+ name='django-mp-avtopro',
10
+ version=version,
11
+ description='Django AvtoPro 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
+ )