channel-app 0.0.131__py3-none-any.whl → 0.0.135__py3-none-any.whl
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.
- channel_app/app/product/service.py +16 -0
- channel_app/core/commands.py +3 -1
- channel_app/core/tests.py +15 -164
- channel_app/omnitron/commands/tests/test_products.py +494 -0
- {channel_app-0.0.131.dist-info → channel_app-0.0.135.dist-info}/METADATA +1 -1
- channel_app-0.0.135.dist-info/RECORD +60 -0
- {channel_app-0.0.131.dist-info → channel_app-0.0.135.dist-info}/WHEEL +1 -1
- channel_app/channel_app/app/__init__.py +0 -0
- channel_app/channel_app/app/order/__init__.py +0 -0
- channel_app/channel_app/app/order/service.py +0 -230
- channel_app/channel_app/app/product/__init__.py +0 -0
- channel_app/channel_app/app/product/service.py +0 -237
- channel_app/channel_app/app/product_price/__init__.py +0 -0
- channel_app/channel_app/app/product_price/service.py +0 -254
- channel_app/channel_app/app/product_stock/__init__.py +0 -0
- channel_app/channel_app/app/product_stock/service.py +0 -258
- channel_app/channel_app/app/setup/__init__.py +0 -0
- channel_app/channel_app/app/setup/service.py +0 -61
- channel_app/channel_app/channel/__init__.py +0 -0
- channel_app/channel_app/channel/commands/__init__.py +0 -0
- channel_app/channel_app/channel/commands/orders/__init__.py +0 -0
- channel_app/channel_app/channel/commands/orders/orders.py +0 -329
- channel_app/channel_app/channel/commands/product_categories.py +0 -1
- channel_app/channel_app/channel/commands/product_images.py +0 -1
- channel_app/channel_app/channel/commands/product_prices.py +0 -148
- channel_app/channel_app/channel/commands/product_stocks.py +0 -220
- channel_app/channel_app/channel/commands/products.py +0 -161
- channel_app/channel_app/channel/commands/setup.py +0 -948
- channel_app/channel_app/channel/integration.py +0 -84
- channel_app/channel_app/core/__init__.py +0 -0
- channel_app/channel_app/core/clients.py +0 -12
- channel_app/channel_app/core/commands.py +0 -364
- channel_app/channel_app/core/data.py +0 -227
- channel_app/channel_app/core/integration.py +0 -74
- channel_app/channel_app/core/products.py +0 -64
- channel_app/channel_app/core/settings.py +0 -28
- channel_app/channel_app/core/utilities.py +0 -99
- channel_app/channel_app/omnitron/__init__.py +0 -0
- channel_app/channel_app/omnitron/batch_request.py +0 -82
- channel_app/channel_app/omnitron/commands/__init__.py +0 -0
- channel_app/channel_app/omnitron/commands/batch_requests.py +0 -281
- channel_app/channel_app/omnitron/commands/error_reports.py +0 -86
- channel_app/channel_app/omnitron/commands/integration_actions.py +0 -200
- channel_app/channel_app/omnitron/commands/orders/__init__.py +0 -0
- channel_app/channel_app/omnitron/commands/orders/addresses.py +0 -242
- channel_app/channel_app/omnitron/commands/orders/cargo_companies.py +0 -40
- channel_app/channel_app/omnitron/commands/orders/customers.py +0 -72
- channel_app/channel_app/omnitron/commands/orders/orders.py +0 -450
- channel_app/channel_app/omnitron/commands/product_categories.py +0 -1
- channel_app/channel_app/omnitron/commands/product_images.py +0 -1
- channel_app/channel_app/omnitron/commands/product_prices.py +0 -192
- channel_app/channel_app/omnitron/commands/product_stocks.py +0 -229
- channel_app/channel_app/omnitron/commands/products.py +0 -735
- channel_app/channel_app/omnitron/commands/setup.py +0 -839
- channel_app/channel_app/omnitron/constants.py +0 -98
- channel_app/channel_app/omnitron/exceptions.py +0 -42
- channel_app/channel_app/omnitron/integration.py +0 -159
- channel_app/setup.py +0 -21
- channel_app-0.0.131.dist-info/RECORD +0 -110
- /channel_app/{channel_app → omnitron/commands/tests}/__init__.py +0 -0
- {channel_app-0.0.131.dist-info → channel_app-0.0.135.dist-info}/top_level.txt +0 -0
@@ -1,839 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import uuid
|
3
|
-
from functools import lru_cache
|
4
|
-
|
5
|
-
import requests
|
6
|
-
from omnisdk.omnitron.endpoints import ChannelAttributeSetEndpoint, \
|
7
|
-
ChannelAttributeEndpoint, \
|
8
|
-
ChannelAttributeConfigEndpoint, ChannelAttributeValueEndpoint, \
|
9
|
-
ChannelAttributeSetConfigEndpoint, ChannelAttributeValueConfigEndpoint, \
|
10
|
-
ChannelAttributeSchemaEndpoint
|
11
|
-
from omnisdk.omnitron.endpoints import ChannelCategoryTreeEndpoint, \
|
12
|
-
ChannelCategoryNodeEndpoint, \
|
13
|
-
ContentTypeEndpoint, ChannelIntegrationActionEndpoint, ChannelEndpoint
|
14
|
-
from omnisdk.omnitron.models import CategoryNode, IntegrationAction, \
|
15
|
-
CategoryTree, Channel, \
|
16
|
-
ChannelAttributeSetConfig, ChannelAttributeValueConfig, \
|
17
|
-
ChannelAttributeSchema
|
18
|
-
from omnisdk.omnitron.models import ChannelAttributeSet, ChannelAttribute, \
|
19
|
-
ChannelAttributeConfig, \
|
20
|
-
ChannelAttributeValue
|
21
|
-
from requests import HTTPError
|
22
|
-
|
23
|
-
from channel_app.core.commands import OmnitronCommandInterface
|
24
|
-
from channel_app.core.data import CategoryTreeDto
|
25
|
-
from channel_app.core.utilities import is_updated, split_list
|
26
|
-
from channel_app.omnitron.constants import ContentType
|
27
|
-
|
28
|
-
|
29
|
-
class CreateOrUpdateCategoryTreeAndNodes(OmnitronCommandInterface):
|
30
|
-
"""
|
31
|
-
Using the channel category tree data (including all nodes)
|
32
|
-
Create/Update category tree and nodes on the Omnitron side.
|
33
|
-
"""
|
34
|
-
endpoint = ChannelCategoryTreeEndpoint
|
35
|
-
|
36
|
-
def get_data(self) -> CategoryTreeDto:
|
37
|
-
return self.objects
|
38
|
-
|
39
|
-
def send(self, validated_data):
|
40
|
-
"""
|
41
|
-
Iterative tree traversal implementation with stack by processing parents first
|
42
|
-
children later.
|
43
|
-
:return:
|
44
|
-
"""
|
45
|
-
# TODO what should we do once a leaf node becomes a parent node?
|
46
|
-
# How do we update this node? What happens to the products that are
|
47
|
-
# assigned to this category?
|
48
|
-
|
49
|
-
tree = validated_data
|
50
|
-
channel_endpoint = ChannelEndpoint()
|
51
|
-
|
52
|
-
if not self.integration.channel.category_tree:
|
53
|
-
ct = CategoryTree()
|
54
|
-
ct.name = "Sales Channel Tree {}".format(str(uuid.uuid4()))
|
55
|
-
node = self.endpoint(channel_id=self.integration.channel_id).create(
|
56
|
-
item=ct)
|
57
|
-
channel = Channel(category_tree=node.pk)
|
58
|
-
channel_endpoint.update(id=self.integration.channel_id,
|
59
|
-
item=channel)
|
60
|
-
self.integration.channel_object = None
|
61
|
-
stack = []
|
62
|
-
current = tree.root
|
63
|
-
node_endpoint = ChannelCategoryNodeEndpoint(
|
64
|
-
channel_id=self.integration.channel_id)
|
65
|
-
content_type = \
|
66
|
-
ContentTypeEndpoint().list(params={"model": "categorynode"})[0]
|
67
|
-
integration_action_endpoint = ChannelIntegrationActionEndpoint(
|
68
|
-
channel_id=self.integration.channel_id)
|
69
|
-
while True:
|
70
|
-
if current:
|
71
|
-
integration_action = self.get_integration_action(
|
72
|
-
content_type_model=content_type.model,
|
73
|
-
remote_id=current.remote_id)
|
74
|
-
if integration_action: # update node
|
75
|
-
node_object = CategoryNode()
|
76
|
-
node_object.name = current.name
|
77
|
-
node_endpoint.update(id=integration_action.object_id,
|
78
|
-
item=node_object)
|
79
|
-
current.omnitron_id = integration_action.object_id
|
80
|
-
elif not current.remote_id:
|
81
|
-
# if block to skip node creation for root node
|
82
|
-
pass
|
83
|
-
|
84
|
-
else: # create node
|
85
|
-
self.create_node(content_type, current,
|
86
|
-
integration_action_endpoint,
|
87
|
-
node_endpoint)
|
88
|
-
stack.extend(current.children)
|
89
|
-
current = None
|
90
|
-
elif stack:
|
91
|
-
current = stack.pop()
|
92
|
-
else:
|
93
|
-
break
|
94
|
-
return []
|
95
|
-
|
96
|
-
def create_node(self, content_type, current, integration_action_endpoint,
|
97
|
-
node_endpoint) -> (
|
98
|
-
CategoryNode, IntegrationAction):
|
99
|
-
|
100
|
-
parent_remote_id = current.parent and current.parent.remote_id
|
101
|
-
|
102
|
-
if parent_remote_id:
|
103
|
-
node_object_parent = self.get_integration_action(
|
104
|
-
content_type_model=content_type.model,
|
105
|
-
remote_id=parent_remote_id)
|
106
|
-
node_object_parent_id = node_object_parent.object_id
|
107
|
-
else:
|
108
|
-
root_node = self.root_node()
|
109
|
-
node_object_parent_id = root_node["pk"]
|
110
|
-
|
111
|
-
node_object_name = current.name
|
112
|
-
|
113
|
-
node_object = CategoryNode()
|
114
|
-
node_object.node = node_object_parent_id
|
115
|
-
node_object.name = node_object_name
|
116
|
-
node = None
|
117
|
-
try:
|
118
|
-
node = node_endpoint.create(item=node_object)
|
119
|
-
except requests.HTTPError as e:
|
120
|
-
if e.response.status_code // 100 != 4:
|
121
|
-
raise e
|
122
|
-
|
123
|
-
parent_node_detailed = self.get_node(node_id=node_object_parent_id,
|
124
|
-
is_detailed=True)
|
125
|
-
for child_node in parent_node_detailed.children:
|
126
|
-
if child_node["name"] == node_object_name:
|
127
|
-
node = self.get_node(node_id=child_node["pk"])
|
128
|
-
break
|
129
|
-
|
130
|
-
if not node:
|
131
|
-
raise e
|
132
|
-
|
133
|
-
integration_action = IntegrationAction(
|
134
|
-
channel_id=self.integration.channel_id,
|
135
|
-
content_type_id=content_type.id,
|
136
|
-
version_date=node.modified_date,
|
137
|
-
object_id=node.pk,
|
138
|
-
remote_id=current.remote_id)
|
139
|
-
|
140
|
-
integration_action = integration_action_endpoint.create(
|
141
|
-
item=integration_action)
|
142
|
-
current.omnitron_id = node.pk
|
143
|
-
return node, integration_action
|
144
|
-
|
145
|
-
def get_integration_action(self, content_type_model, remote_id):
|
146
|
-
if not remote_id:
|
147
|
-
return None
|
148
|
-
integration_action_endpoint = ChannelIntegrationActionEndpoint(
|
149
|
-
channel_id=self.integration.channel_id)
|
150
|
-
|
151
|
-
integration_action = integration_action_endpoint.list(params={
|
152
|
-
"channel_id": self.integration.channel_id,
|
153
|
-
"content_type_name": content_type_model,
|
154
|
-
"remote_id__exact": remote_id
|
155
|
-
})
|
156
|
-
if integration_action:
|
157
|
-
return integration_action[0]
|
158
|
-
else:
|
159
|
-
return None
|
160
|
-
|
161
|
-
def get_node(self, node_id, is_detailed=False) -> CategoryNode:
|
162
|
-
path = None
|
163
|
-
if is_detailed:
|
164
|
-
path = "detailed"
|
165
|
-
node_endpoint = ChannelCategoryNodeEndpoint(
|
166
|
-
channel_id=self.integration.channel_id,
|
167
|
-
path=path)
|
168
|
-
detailed_node = node_endpoint.retrieve(id=node_id)
|
169
|
-
return detailed_node
|
170
|
-
|
171
|
-
def root_node(self):
|
172
|
-
category_tree_endpoint = ChannelCategoryTreeEndpoint(
|
173
|
-
channel_id=self.integration.channel_id)
|
174
|
-
return category_tree_endpoint.retrieve(
|
175
|
-
id=self.integration.channel.category_tree).category_root
|
176
|
-
|
177
|
-
def check_run(self, is_ok, formatted_data):
|
178
|
-
return True
|
179
|
-
|
180
|
-
|
181
|
-
class CreateOrUpdateCategoryAttributes(OmnitronCommandInterface):
|
182
|
-
"""
|
183
|
-
Create Attribute related entries on the Omnitron using the
|
184
|
-
attribute data created from a CategoryDto object.
|
185
|
-
"""
|
186
|
-
integration_action_endpoint = ChannelIntegrationActionEndpoint
|
187
|
-
|
188
|
-
def get_data(self):
|
189
|
-
return self.objects
|
190
|
-
|
191
|
-
def send(self, validated_data):
|
192
|
-
integration_action, channel_category = validated_data
|
193
|
-
if not channel_category:
|
194
|
-
self.update_category_node_version_date(integration_action)
|
195
|
-
return [integration_action]
|
196
|
-
attribute_set_name = self.get_attribute_set_name(channel_category)
|
197
|
-
attribute_set = self.integration.do_action(
|
198
|
-
key="create_or_update_channel_attribute_set",
|
199
|
-
objects={"name": attribute_set_name,
|
200
|
-
"remote_id": integration_action.remote_id})[0]
|
201
|
-
self.integration.do_action(
|
202
|
-
key="get_or_create_channel_attribute_set_config",
|
203
|
-
objects={"attribute_set": attribute_set.id,
|
204
|
-
"object_id": integration_action.object_id,
|
205
|
-
"content_type": ContentType.category_node.value})
|
206
|
-
|
207
|
-
for channel_attribute in channel_category.attributes:
|
208
|
-
attribute = self.integration.do_action(
|
209
|
-
key="create_or_update_channel_attribute",
|
210
|
-
objects={"name": channel_attribute.name,
|
211
|
-
"remote_id": channel_attribute.remote_id})[0]
|
212
|
-
schema = self.integration.do_action(
|
213
|
-
key="get_or_create_channel_attribute_schema",
|
214
|
-
objects={"name": f"{attribute.name} Schema"}
|
215
|
-
)
|
216
|
-
self.integration.do_action(
|
217
|
-
key="create_or_update_channel_attribute_config",
|
218
|
-
objects={"attribute": attribute.pk,
|
219
|
-
"attribute_set": attribute_set.id,
|
220
|
-
"attribute_remote_id": channel_attribute.remote_id,
|
221
|
-
"is_required": channel_attribute.required,
|
222
|
-
"is_custom": channel_attribute.allow_custom_value,
|
223
|
-
"is_variant": channel_attribute.variant,
|
224
|
-
})
|
225
|
-
for channel_attribute_value in channel_attribute.values:
|
226
|
-
self.create_attribute_value_and_config(
|
227
|
-
attribute=attribute, attribute_set=attribute_set,
|
228
|
-
channel_attribute_value=channel_attribute_value)
|
229
|
-
|
230
|
-
self.update_category_node_version_date(integration_action)
|
231
|
-
return [attribute]
|
232
|
-
|
233
|
-
def update_category_node_version_date(self, integration_action):
|
234
|
-
category_node_endpoint = ChannelCategoryNodeEndpoint(
|
235
|
-
channel_id=self.integration.channel_id)
|
236
|
-
category_node = category_node_endpoint.update(
|
237
|
-
id=integration_action.object_id,
|
238
|
-
item=CategoryNode())
|
239
|
-
self.integration_action_endpoint(
|
240
|
-
channel_id=self.integration.channel_id).update(
|
241
|
-
id=integration_action.pk,
|
242
|
-
item=IntegrationAction(version_date=category_node.modified_date))
|
243
|
-
|
244
|
-
def create_attribute_value_and_config(self, attribute, attribute_set,
|
245
|
-
channel_attribute_value):
|
246
|
-
attribute_value = self.integration.do_action(
|
247
|
-
key="create_or_update_channel_attribute_value",
|
248
|
-
objects={
|
249
|
-
"attribute": attribute.pk,
|
250
|
-
"label": channel_attribute_value.name,
|
251
|
-
"value": channel_attribute_value.remote_id,
|
252
|
-
"remote_id": channel_attribute_value.remote_id})[0]
|
253
|
-
attribute_value_config = self.integration.do_action(
|
254
|
-
key="get_or_create_channel_attribute_value_config",
|
255
|
-
objects={
|
256
|
-
"attribute_value": attribute_value.pk,
|
257
|
-
"attribute_set": attribute_set.id})[0]
|
258
|
-
|
259
|
-
def get_attribute_set_name(self, channel_category):
|
260
|
-
# TODO create attribute set name as a breadcrumb
|
261
|
-
return channel_category.name
|
262
|
-
|
263
|
-
|
264
|
-
class AsyncCreateOrUpdateCategoryAttributes(CreateOrUpdateCategoryAttributes):
|
265
|
-
"""
|
266
|
-
Async process
|
267
|
-
Create Attribute related entries on the Omnitron using the
|
268
|
-
attribute data created from a CategoryDto object.
|
269
|
-
"""
|
270
|
-
|
271
|
-
async def run_async(self):
|
272
|
-
integration_action, channel_category = self.get_data()
|
273
|
-
attribute_set_name = self.get_attribute_set_name(channel_category)
|
274
|
-
attribute_set = self.integration.do_action(
|
275
|
-
key="create_or_update_channel_attribute_set",
|
276
|
-
objects={"name": attribute_set_name,
|
277
|
-
"remote_id": integration_action.remote_id})
|
278
|
-
self.integration.do_action(
|
279
|
-
key="get_or_create_channel_attribute_set_config",
|
280
|
-
objects={"attribute_set": attribute_set.remote_id,
|
281
|
-
"object_id": integration_action.object_id,
|
282
|
-
"content_type": ContentType.category_node.value})
|
283
|
-
|
284
|
-
for channel_attribute in channel_category.attributes:
|
285
|
-
attribute = self.integration.do_action(
|
286
|
-
key="create_or_update_channel_attribute",
|
287
|
-
objects={"name": channel_attribute.name,
|
288
|
-
"remote_id": channel_attribute.remote_id})
|
289
|
-
self.integration.do_action(
|
290
|
-
key="create_or_update_channel_attribute_config",
|
291
|
-
objects={"attribute": attribute.pk,
|
292
|
-
"attribute_set": attribute_set.remote_id,
|
293
|
-
"attribute_remote_id": channel_attribute.remote_id,
|
294
|
-
"is_required": channel_attribute.required,
|
295
|
-
"is_custom": channel_attribute.allow_custom_value,
|
296
|
-
"is_variant": channel_attribute.variant,
|
297
|
-
})
|
298
|
-
|
299
|
-
await asyncio.gather(
|
300
|
-
*[self.get_or_create_attribute_value_and_config(attribute,
|
301
|
-
attribute_set,
|
302
|
-
channel_attribute_value)
|
303
|
-
for
|
304
|
-
channel_attribute_value in channel_attribute.values])
|
305
|
-
|
306
|
-
category_node_endpoint = ChannelCategoryNodeEndpoint(
|
307
|
-
channel_id=self.integration.channel_id)
|
308
|
-
category_node = category_node_endpoint.update(
|
309
|
-
id=integration_action.object_id,
|
310
|
-
item=CategoryNode())
|
311
|
-
self.integration_action_endpoint(
|
312
|
-
channel_id=self.integration.channel_id).update(
|
313
|
-
id=integration_action.pk,
|
314
|
-
item=IntegrationAction(version_date=category_node.modified_date))
|
315
|
-
|
316
|
-
async def get_or_create_attribute_value_and_config(self, attribute,
|
317
|
-
attribute_set,
|
318
|
-
channel_attribute_value):
|
319
|
-
attribute_value = self.integration.do_action(
|
320
|
-
key="create_or_update_channel_attribute_value",
|
321
|
-
objects={
|
322
|
-
"attribute": attribute.pk,
|
323
|
-
"label": channel_attribute_value.name,
|
324
|
-
"value": channel_attribute_value.remote_id,
|
325
|
-
"remote_id": channel_attribute_value.remote_id})
|
326
|
-
attribute_value_config = self.integration.do_action(
|
327
|
-
key="get_or_create_channel_attribute_value_config",
|
328
|
-
objects={
|
329
|
-
"attribute_value": attribute_value.pk,
|
330
|
-
"attribute_set": attribute_set.remote_id})
|
331
|
-
await asyncio.sleep(0)
|
332
|
-
|
333
|
-
|
334
|
-
class GetCategoryIds(OmnitronCommandInterface):
|
335
|
-
"""
|
336
|
-
Get Category ids from Omnitron sorted by least recently updated category first
|
337
|
-
"""
|
338
|
-
endpoint = ChannelCategoryNodeEndpoint
|
339
|
-
CHUNK_SIZE = 50
|
340
|
-
|
341
|
-
def get_data(self):
|
342
|
-
return
|
343
|
-
|
344
|
-
def send(self, validated_data) -> list:
|
345
|
-
if not self.integration.channel.category_tree:
|
346
|
-
raise Exception("No category tree, no attributes :/")
|
347
|
-
integration_endpoint = ChannelIntegrationActionEndpoint(
|
348
|
-
channel_id=self.integration.channel_id)
|
349
|
-
|
350
|
-
integration_actions = integration_endpoint.list(params={
|
351
|
-
"channel_id": self.integration.channel_id,
|
352
|
-
"content_type_name": ContentType.category_node.value,
|
353
|
-
"sort": "version_date",
|
354
|
-
"limit": 200 # if we want to process oldest five nodes at a time
|
355
|
-
})
|
356
|
-
return integration_actions
|
357
|
-
|
358
|
-
|
359
|
-
class CreateOrUpdateChannelAttributeSet(OmnitronCommandInterface):
|
360
|
-
"""
|
361
|
-
Create AttributeSet object on Omnitron to store the attributes
|
362
|
-
"""
|
363
|
-
endpoint = ChannelAttributeSetEndpoint
|
364
|
-
|
365
|
-
def get_data(self) -> ChannelAttributeSet:
|
366
|
-
name = self.objects["name"]
|
367
|
-
remote_id = self.objects["remote_id"]
|
368
|
-
channel = self.integration.channel_id
|
369
|
-
return ChannelAttributeSet(name=name, remote_id=remote_id,
|
370
|
-
channel=channel)
|
371
|
-
|
372
|
-
def send(self, validated_data) -> list:
|
373
|
-
data = validated_data
|
374
|
-
endpoint = self.endpoint(channel_id=self.integration.channel_id)
|
375
|
-
|
376
|
-
attribute_sets = endpoint.list(
|
377
|
-
params={"remote_id__exact": data.remote_id,
|
378
|
-
"channel": self.integration.channel_id})
|
379
|
-
# TODO add channel filter too on the next version
|
380
|
-
|
381
|
-
if len(attribute_sets) > 0:
|
382
|
-
attribute_set = attribute_sets[0]
|
383
|
-
if self.is_updated(attribute_set, data):
|
384
|
-
self.endpoint(channel_id=self.integration.channel_id).update(
|
385
|
-
id=attribute_set.id, item=data)
|
386
|
-
return [attribute_sets[0]]
|
387
|
-
|
388
|
-
attribute_set = self.endpoint(
|
389
|
-
channel_id=self.integration.channel_id).create(item=data)
|
390
|
-
return [attribute_set]
|
391
|
-
|
392
|
-
def is_updated(self, attribute_set, data):
|
393
|
-
if data.channel != attribute_set.channel:
|
394
|
-
return True
|
395
|
-
if data.name != attribute_set.name:
|
396
|
-
return True
|
397
|
-
return False
|
398
|
-
|
399
|
-
|
400
|
-
class GetOrCreateChannelAttributeSetConfig(OmnitronCommandInterface):
|
401
|
-
"""
|
402
|
-
Create an AttributeSetConfig entry for AttributeSet denoting which object this set belongs to.
|
403
|
-
For example an attribute set usually defines the attributes a category needs. So
|
404
|
-
we create an AttributeSetConfig entry with category Omnitron id, CategoryNode content type and
|
405
|
-
AttributeSet id.
|
406
|
-
"""
|
407
|
-
|
408
|
-
endpoint = ChannelAttributeSetConfigEndpoint
|
409
|
-
|
410
|
-
def get_data(self) -> ChannelAttributeSetConfig:
|
411
|
-
attribute_set = self.objects["attribute_set"]
|
412
|
-
object_id = self.objects["object_id"]
|
413
|
-
content_type = self.objects["content_type"]
|
414
|
-
if not isinstance(attribute_set, int):
|
415
|
-
raise AssertionError
|
416
|
-
if not isinstance(object_id, (int, type(None))):
|
417
|
-
raise AssertionError
|
418
|
-
if not isinstance(content_type, str):
|
419
|
-
raise AssertionError
|
420
|
-
return ChannelAttributeSetConfig(attribute_set=attribute_set,
|
421
|
-
object_id=object_id,
|
422
|
-
content_type=content_type)
|
423
|
-
|
424
|
-
def send(self, validated_data) -> list:
|
425
|
-
data = validated_data
|
426
|
-
endpoint = self.endpoint(channel_id=self.integration.channel_id)
|
427
|
-
attribute_set_configs = endpoint.list(
|
428
|
-
params={"attribute_set": data.attribute_set,
|
429
|
-
"object_id": data.object_id,
|
430
|
-
"content_type__model__exact": data.content_type})
|
431
|
-
if len(attribute_set_configs) > 0:
|
432
|
-
return attribute_set_configs
|
433
|
-
|
434
|
-
attribute_set_config = endpoint.create(item=data)
|
435
|
-
return [attribute_set_config]
|
436
|
-
|
437
|
-
|
438
|
-
class CreateOrUpdateChannelAttribute(OmnitronCommandInterface):
|
439
|
-
endpoint = ChannelAttributeEndpoint
|
440
|
-
|
441
|
-
# TODO Omnitron does not support 2 different attributes with same name on same channel,
|
442
|
-
# If a marketplace uses more than one same-name attributes for different purposes,
|
443
|
-
# in Omnitron they map to a single attribute. Only one of those attributes will be recognized
|
444
|
-
# and an IntegrationAction entry will be created.
|
445
|
-
|
446
|
-
def get_data(self):
|
447
|
-
name = self.objects["name"]
|
448
|
-
remote_id = self.objects["remote_id"]
|
449
|
-
if not isinstance(name, str):
|
450
|
-
raise AssertionError
|
451
|
-
if not isinstance(remote_id, (str, int, type(None))):
|
452
|
-
raise AssertionError
|
453
|
-
|
454
|
-
return name, remote_id
|
455
|
-
|
456
|
-
def send(self, validated_data) -> list:
|
457
|
-
name, remote_id = validated_data
|
458
|
-
|
459
|
-
integration_endpoint = ChannelIntegrationActionEndpoint(
|
460
|
-
channel_id=self.integration.channel_id)
|
461
|
-
|
462
|
-
attribute = self.create_or_update_channel_attribute(
|
463
|
-
integration_endpoint, name, remote_id)
|
464
|
-
return [attribute]
|
465
|
-
|
466
|
-
def create_or_update_channel_attribute(self, integration_endpoint, name,
|
467
|
-
remote_id):
|
468
|
-
|
469
|
-
integration_action = integration_endpoint.list(params={
|
470
|
-
"content_type_name": "marketplaceattribute",
|
471
|
-
"channel_id": self.integration.channel_id,
|
472
|
-
"remote_id__exact": remote_id
|
473
|
-
})
|
474
|
-
if integration_action:
|
475
|
-
integration_action = integration_action[0]
|
476
|
-
attribute = self.endpoint(
|
477
|
-
channel_id=self.integration.channel_id).retrieve(
|
478
|
-
id=integration_action.object_id)
|
479
|
-
if attribute.name != name:
|
480
|
-
attribute = self.endpoint(
|
481
|
-
channel_id=self.integration.channel_id).update(
|
482
|
-
id=integration_action.object_id, item=ChannelAttribute(
|
483
|
-
channel=self.integration.channel_id,
|
484
|
-
name=name
|
485
|
-
))
|
486
|
-
return attribute
|
487
|
-
else:
|
488
|
-
try:
|
489
|
-
attribute = self.endpoint(
|
490
|
-
channel_id=self.integration.channel_id).create(
|
491
|
-
item=ChannelAttribute(channel=self.integration.channel_id,
|
492
|
-
name=name))
|
493
|
-
except HTTPError as e:
|
494
|
-
if e.response.status_code // 100 != 4:
|
495
|
-
raise e
|
496
|
-
params = {
|
497
|
-
"name__exact": name, "channel": self.integration.channel_id
|
498
|
-
}
|
499
|
-
attributes = self.endpoint(
|
500
|
-
channel_id=self.integration.channel_id).list(params=params)
|
501
|
-
if len(attributes) == 0:
|
502
|
-
raise Exception(
|
503
|
-
"Creation returned bad request but nothing exists!")
|
504
|
-
return attributes[0]
|
505
|
-
content_type = ContentTypeEndpoint().list(
|
506
|
-
params={"model": "marketplaceattribute"})[0]
|
507
|
-
integration_endpoint.create(item=IntegrationAction(
|
508
|
-
channel_id=self.integration.channel_id,
|
509
|
-
content_type_id=content_type.id,
|
510
|
-
remote_id=remote_id,
|
511
|
-
object_id=attribute.pk,
|
512
|
-
version_date=attribute.modified_date,
|
513
|
-
))
|
514
|
-
return attribute
|
515
|
-
|
516
|
-
|
517
|
-
class GetChannelAttributeSets(OmnitronCommandInterface):
|
518
|
-
"""
|
519
|
-
Get all attribute set in omnitron
|
520
|
-
"""
|
521
|
-
|
522
|
-
def get_data(self):
|
523
|
-
return
|
524
|
-
|
525
|
-
def send(self, validated_data) -> object:
|
526
|
-
endpoint = ChannelAttributeSetEndpoint(
|
527
|
-
channel_id=self.integration.channel_id)
|
528
|
-
attribute_sets = endpoint.list(
|
529
|
-
params={"channel": self.integration.channel_id})
|
530
|
-
for attribute_set_batch in endpoint.iterator:
|
531
|
-
attribute_sets.extend(attribute_set_batch)
|
532
|
-
return attribute_sets
|
533
|
-
|
534
|
-
|
535
|
-
class GetChannelAttributeSetConfigs(OmnitronCommandInterface):
|
536
|
-
"""
|
537
|
-
Get all attribute set config in omnitron
|
538
|
-
"""
|
539
|
-
CHUNK_SIZE = 500
|
540
|
-
|
541
|
-
def get_data(self):
|
542
|
-
return
|
543
|
-
|
544
|
-
def send(self, validated_data) -> object:
|
545
|
-
endpoint = ChannelAttributeSetConfigEndpoint(
|
546
|
-
channel_id=self.integration.channel_id)
|
547
|
-
attribute_set_configs = endpoint.list(params={
|
548
|
-
"content_type__model": ContentType.category_node.value,
|
549
|
-
"limit": self.CHUNK_SIZE})
|
550
|
-
for attribute_set_config_batch in endpoint.iterator:
|
551
|
-
attribute_set_configs.extend(attribute_set_config_batch)
|
552
|
-
attribute_set_configs_by_category_node_pk = {}
|
553
|
-
for attribute_set_config in attribute_set_configs:
|
554
|
-
attribute_set_configs_by_category_node_pk[
|
555
|
-
attribute_set_config.object_id] = attribute_set_config
|
556
|
-
return [attribute_set_configs_by_category_node_pk]
|
557
|
-
|
558
|
-
|
559
|
-
class CreateOrUpdateChannelAttributeConfig(OmnitronCommandInterface):
|
560
|
-
"""
|
561
|
-
Attach an attribute with an attribute set
|
562
|
-
"""
|
563
|
-
|
564
|
-
endpoint = ChannelAttributeConfigEndpoint
|
565
|
-
|
566
|
-
def get_data(self) -> dict:
|
567
|
-
attribute_set = self.objects["attribute_set"]
|
568
|
-
attribute = self.objects["attribute"]
|
569
|
-
attribute_remote_id = self.objects.get("attribute_remote_id", None)
|
570
|
-
is_required = self.objects.get("is_required", False)
|
571
|
-
is_variant = self.objects.get("is_variant", False)
|
572
|
-
is_custom = self.objects.get("is_custom", False)
|
573
|
-
is_image_attribute = self.objects.get("is_image_attribute", False)
|
574
|
-
is_meta = self.objects.get("is_meta", False)
|
575
|
-
|
576
|
-
if not isinstance(attribute_set, int):
|
577
|
-
raise AssertionError
|
578
|
-
if not isinstance(attribute, int):
|
579
|
-
raise AssertionError
|
580
|
-
if not isinstance(attribute_remote_id, (str, type(None))):
|
581
|
-
raise AssertionError
|
582
|
-
if not isinstance(is_required, bool):
|
583
|
-
raise AssertionError
|
584
|
-
if not isinstance(is_variant, bool):
|
585
|
-
raise AssertionError
|
586
|
-
if not isinstance(is_custom, bool):
|
587
|
-
raise AssertionError
|
588
|
-
if not isinstance(is_image_attribute, bool):
|
589
|
-
raise AssertionError
|
590
|
-
if not isinstance(is_meta, bool):
|
591
|
-
raise AssertionError
|
592
|
-
return {
|
593
|
-
"attribute_set": attribute_set,
|
594
|
-
"attribute": attribute,
|
595
|
-
"attribute_remote_id": attribute_remote_id,
|
596
|
-
"is_required": is_required,
|
597
|
-
"is_variant": is_variant,
|
598
|
-
"is_custom": is_custom,
|
599
|
-
"is_image_attribute": is_image_attribute,
|
600
|
-
"is_meta": is_meta,
|
601
|
-
}
|
602
|
-
|
603
|
-
def send(self, validated_data) -> ChannelAttributeConfig:
|
604
|
-
data = validated_data
|
605
|
-
attribute_set = data["attribute_set"]
|
606
|
-
attribute = data["attribute"]
|
607
|
-
attribute_remote_id = data["attribute_remote_id"]
|
608
|
-
is_required = data["is_required"]
|
609
|
-
is_variant = data["is_variant"]
|
610
|
-
is_custom = data["is_custom"]
|
611
|
-
is_image_attribute = data["is_image_attribute"]
|
612
|
-
is_meta = data["is_meta"]
|
613
|
-
|
614
|
-
endpoint = self.endpoint(channel_id=self.integration.channel_id)
|
615
|
-
try:
|
616
|
-
attributeconfig = endpoint.create(
|
617
|
-
item=ChannelAttributeConfig(
|
618
|
-
attribute_set=attribute_set,
|
619
|
-
attribute=attribute,
|
620
|
-
attribute_remote_id=attribute_remote_id,
|
621
|
-
is_required=is_required,
|
622
|
-
is_variant=is_variant,
|
623
|
-
is_custom=is_custom,
|
624
|
-
is_image_attribute=is_image_attribute,
|
625
|
-
is_meta=is_meta))
|
626
|
-
except HTTPError as e:
|
627
|
-
if e.response.status_code // 100 != 4:
|
628
|
-
raise e
|
629
|
-
attributeconfigs = endpoint.list(
|
630
|
-
params={"attribute_set": attribute_set,
|
631
|
-
"attribute": attribute})
|
632
|
-
attributeconfig = attributeconfigs[0]
|
633
|
-
if is_updated(attributeconfig, data):
|
634
|
-
attributeconfig = endpoint.update(
|
635
|
-
id=attributeconfig.pk,
|
636
|
-
item=ChannelAttributeConfig(
|
637
|
-
attribute_set=attribute_set,
|
638
|
-
attribute=attribute,
|
639
|
-
attribute_remote_id=attribute_remote_id,
|
640
|
-
is_required=is_required,
|
641
|
-
is_variant=is_variant,
|
642
|
-
is_custom=is_custom,
|
643
|
-
is_image_attribute=is_image_attribute,
|
644
|
-
is_meta=is_meta))
|
645
|
-
return [attributeconfig]
|
646
|
-
return [attributeconfig]
|
647
|
-
|
648
|
-
|
649
|
-
class CreateOrUpdateChannelAttributeValue(OmnitronCommandInterface):
|
650
|
-
endpoint = ChannelAttributeValueEndpoint
|
651
|
-
|
652
|
-
def get_data(self) -> dict:
|
653
|
-
remote_id = self.objects["remote_id"]
|
654
|
-
attribute = self.objects["attribute"]
|
655
|
-
label = self.objects["label"]
|
656
|
-
value = self.objects["value"]
|
657
|
-
if not isinstance(remote_id, (str, int)):
|
658
|
-
raise AssertionError
|
659
|
-
if not isinstance(attribute, int):
|
660
|
-
raise AssertionError
|
661
|
-
if not isinstance(label, str):
|
662
|
-
raise AssertionError
|
663
|
-
if not isinstance(value, (str, int)):
|
664
|
-
raise AssertionError
|
665
|
-
return self.objects
|
666
|
-
|
667
|
-
def send(self, validated_data) -> list:
|
668
|
-
# TODO all service calls here can be summarized to a single call to omnitron
|
669
|
-
# which checks IntegrationAction by remote id, if it finds the item, updates the item and
|
670
|
-
# IntegrationAction returns it and halts. If it cannot find the item, it creates the
|
671
|
-
# item, creates its IntegrationAction, returns them and halts.
|
672
|
-
data = validated_data
|
673
|
-
remote_id = data["remote_id"]
|
674
|
-
attribute = data["attribute"]
|
675
|
-
label = data["label"]
|
676
|
-
value = data["value"]
|
677
|
-
channel_id = self.integration.channel_id
|
678
|
-
# search in cached attribute values and ia
|
679
|
-
attribute_value = self.get_or_create_attribute_value(attribute, label,
|
680
|
-
remote_id,
|
681
|
-
value, channel_id)
|
682
|
-
return [attribute_value]
|
683
|
-
|
684
|
-
@staticmethod
|
685
|
-
@lru_cache(maxsize=None)
|
686
|
-
def get_or_create_attribute_value(attribute, label, remote_id, value,
|
687
|
-
channel_id):
|
688
|
-
data = {
|
689
|
-
"remote_id": remote_id,
|
690
|
-
"attribute": attribute,
|
691
|
-
"label": label,
|
692
|
-
"value": value
|
693
|
-
}
|
694
|
-
endpoint = ChannelAttributeValueEndpoint(channel_id=channel_id)
|
695
|
-
integration_endpoint = ChannelIntegrationActionEndpoint(
|
696
|
-
channel_id=channel_id)
|
697
|
-
integration_action = integration_endpoint.list(params={
|
698
|
-
"content_type_name": "marketplaceattributevalue",
|
699
|
-
"channel_id": channel_id,
|
700
|
-
"remote_id__exact": remote_id
|
701
|
-
})
|
702
|
-
attribute_value_item = ChannelAttributeValue(channel=channel_id,
|
703
|
-
attribute=attribute,
|
704
|
-
label=label,
|
705
|
-
value=value, )
|
706
|
-
if integration_action:
|
707
|
-
integration_action = integration_action[0]
|
708
|
-
attribute_value = endpoint.retrieve(
|
709
|
-
id=integration_action.object_id)
|
710
|
-
if is_updated(attribute_value, data):
|
711
|
-
if attribute_value.attribute == data["attribute"]:
|
712
|
-
endpoint.update(id=integration_action.object_id,
|
713
|
-
item=attribute_value_item)
|
714
|
-
else:
|
715
|
-
attribute_value = CreateOrUpdateChannelAttributeValue.create_attribute_value(
|
716
|
-
attribute, attribute_value_item, endpoint, label, value)
|
717
|
-
else:
|
718
|
-
attribute_value = CreateOrUpdateChannelAttributeValue.create_attribute_value(
|
719
|
-
attribute, attribute_value_item, endpoint, label, value)
|
720
|
-
|
721
|
-
content_type = ContentTypeEndpoint().list(params={
|
722
|
-
"model": "marketplaceattributevalue"})[0]
|
723
|
-
integration_endpoint.create(item=IntegrationAction(
|
724
|
-
channel_id=channel_id,
|
725
|
-
content_type_id=content_type.id,
|
726
|
-
remote_id=remote_id,
|
727
|
-
object_id=attribute_value.pk,
|
728
|
-
version_date=attribute_value.modified_date,
|
729
|
-
))
|
730
|
-
return attribute_value
|
731
|
-
|
732
|
-
@staticmethod
|
733
|
-
def create_attribute_value(attribute, attribute_value_item, endpoint, label,
|
734
|
-
value):
|
735
|
-
try:
|
736
|
-
attribute_value = endpoint.create(item=attribute_value_item)
|
737
|
-
except HTTPError:
|
738
|
-
attribute_value = endpoint.list(params={"attribute": attribute,
|
739
|
-
"label__exact": label,
|
740
|
-
"value__exact": value, })[
|
741
|
-
0]
|
742
|
-
return attribute_value
|
743
|
-
|
744
|
-
|
745
|
-
class GetOrCreateChannelAttributeValueConfig(OmnitronCommandInterface):
|
746
|
-
"""
|
747
|
-
If a subset of attribute values are defined for an attribute set, you need to create
|
748
|
-
AttributeValueConfig objects to achieve that setup
|
749
|
-
"""
|
750
|
-
# TODO what happens it a value is no more needed in an attribute set
|
751
|
-
# there is nothing that handles deletes right now
|
752
|
-
|
753
|
-
endpoint = ChannelAttributeValueConfigEndpoint
|
754
|
-
|
755
|
-
def get_data(self) -> dict:
|
756
|
-
attribute_set = self.objects["attribute_set"]
|
757
|
-
attribute_value = self.objects["attribute_value"]
|
758
|
-
if not isinstance(attribute_set, int):
|
759
|
-
raise AssertionError
|
760
|
-
if not isinstance(attribute_value, int):
|
761
|
-
raise AssertionError
|
762
|
-
return self.objects
|
763
|
-
|
764
|
-
def send(self, validated_data) -> list:
|
765
|
-
data = validated_data
|
766
|
-
attribute_set = data["attribute_set"]
|
767
|
-
attribute_value = data["attribute_value"]
|
768
|
-
channel_id = self.integration.channel_id
|
769
|
-
attribute_value_config = self.get_or_create_attribute_value_config(
|
770
|
-
attribute_set, attribute_value, channel_id)
|
771
|
-
return [attribute_value_config]
|
772
|
-
|
773
|
-
@staticmethod
|
774
|
-
@lru_cache(maxsize=None)
|
775
|
-
def get_or_create_attribute_value_config(attribute_set, attribute_value,
|
776
|
-
channel_id):
|
777
|
-
endpoint = ChannelAttributeValueConfigEndpoint(channel_id=channel_id)
|
778
|
-
attributevalueconfigs = endpoint.list(
|
779
|
-
params={"attribute_set": attribute_set,
|
780
|
-
"attribute_value": attribute_value})
|
781
|
-
if len(attributevalueconfigs) > 0:
|
782
|
-
return attributevalueconfigs[0]
|
783
|
-
attribute_value_config = endpoint.create(
|
784
|
-
item=ChannelAttributeValueConfig(
|
785
|
-
attribute_set=attribute_set,
|
786
|
-
attribute_value=attribute_value))
|
787
|
-
return attribute_value_config
|
788
|
-
|
789
|
-
|
790
|
-
class GetOrCreateChannelAttributeSchema(OmnitronCommandInterface):
|
791
|
-
"""
|
792
|
-
Schema(Ruleset) objects define how an attribute of a product will be populated. It is
|
793
|
-
not feasible to automatically fill the rules. We need to create empty schema objects so that
|
794
|
-
Omnitron users can fill the rules
|
795
|
-
"""
|
796
|
-
|
797
|
-
endpoint = ChannelAttributeSchemaEndpoint
|
798
|
-
|
799
|
-
def get_data(self) -> object:
|
800
|
-
return self.objects
|
801
|
-
|
802
|
-
def send(self, validated_data) -> object:
|
803
|
-
data = validated_data
|
804
|
-
schema = data.get("schema", {})
|
805
|
-
channel_id = self.integration.channel_id
|
806
|
-
name = f"{str(channel_id)} {data['name']}"
|
807
|
-
endpoint = self.endpoint(channel_id=channel_id)
|
808
|
-
try:
|
809
|
-
attribute_schema = endpoint.create(item=ChannelAttributeSchema(
|
810
|
-
name=name, schema=schema, channel=channel_id))
|
811
|
-
except HTTPError as e:
|
812
|
-
if e.response.status_code // 100 != 4:
|
813
|
-
raise e
|
814
|
-
attribute_schemas = endpoint.list(params={"name__exact": name,
|
815
|
-
"channel": channel_id})
|
816
|
-
attribute_schema = attribute_schemas[0]
|
817
|
-
return [attribute_schema]
|
818
|
-
|
819
|
-
|
820
|
-
class UpdateChannelConfSchema(OmnitronCommandInterface):
|
821
|
-
endpoint = ChannelEndpoint
|
822
|
-
|
823
|
-
def get_data(self) -> dict:
|
824
|
-
schema_dict = self.objects
|
825
|
-
return schema_dict
|
826
|
-
|
827
|
-
def send(self, validated_data) -> list:
|
828
|
-
channel = self.integration.channel
|
829
|
-
schema = validated_data
|
830
|
-
if isinstance(channel.schema, dict):
|
831
|
-
channel.schema.update(schema)
|
832
|
-
else:
|
833
|
-
channel.schema = schema
|
834
|
-
new_channel = Channel(schema=channel.schema)
|
835
|
-
channel_response = self.endpoint(
|
836
|
-
channel_id=self.integration.channel_id).update(
|
837
|
-
id=channel.pk,
|
838
|
-
item=new_channel)
|
839
|
-
return [channel_response]
|