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.
Files changed (61) hide show
  1. channel_app/app/product/service.py +16 -0
  2. channel_app/core/commands.py +3 -1
  3. channel_app/core/tests.py +15 -164
  4. channel_app/omnitron/commands/tests/test_products.py +494 -0
  5. {channel_app-0.0.131.dist-info → channel_app-0.0.135.dist-info}/METADATA +1 -1
  6. channel_app-0.0.135.dist-info/RECORD +60 -0
  7. {channel_app-0.0.131.dist-info → channel_app-0.0.135.dist-info}/WHEEL +1 -1
  8. channel_app/channel_app/app/__init__.py +0 -0
  9. channel_app/channel_app/app/order/__init__.py +0 -0
  10. channel_app/channel_app/app/order/service.py +0 -230
  11. channel_app/channel_app/app/product/__init__.py +0 -0
  12. channel_app/channel_app/app/product/service.py +0 -237
  13. channel_app/channel_app/app/product_price/__init__.py +0 -0
  14. channel_app/channel_app/app/product_price/service.py +0 -254
  15. channel_app/channel_app/app/product_stock/__init__.py +0 -0
  16. channel_app/channel_app/app/product_stock/service.py +0 -258
  17. channel_app/channel_app/app/setup/__init__.py +0 -0
  18. channel_app/channel_app/app/setup/service.py +0 -61
  19. channel_app/channel_app/channel/__init__.py +0 -0
  20. channel_app/channel_app/channel/commands/__init__.py +0 -0
  21. channel_app/channel_app/channel/commands/orders/__init__.py +0 -0
  22. channel_app/channel_app/channel/commands/orders/orders.py +0 -329
  23. channel_app/channel_app/channel/commands/product_categories.py +0 -1
  24. channel_app/channel_app/channel/commands/product_images.py +0 -1
  25. channel_app/channel_app/channel/commands/product_prices.py +0 -148
  26. channel_app/channel_app/channel/commands/product_stocks.py +0 -220
  27. channel_app/channel_app/channel/commands/products.py +0 -161
  28. channel_app/channel_app/channel/commands/setup.py +0 -948
  29. channel_app/channel_app/channel/integration.py +0 -84
  30. channel_app/channel_app/core/__init__.py +0 -0
  31. channel_app/channel_app/core/clients.py +0 -12
  32. channel_app/channel_app/core/commands.py +0 -364
  33. channel_app/channel_app/core/data.py +0 -227
  34. channel_app/channel_app/core/integration.py +0 -74
  35. channel_app/channel_app/core/products.py +0 -64
  36. channel_app/channel_app/core/settings.py +0 -28
  37. channel_app/channel_app/core/utilities.py +0 -99
  38. channel_app/channel_app/omnitron/__init__.py +0 -0
  39. channel_app/channel_app/omnitron/batch_request.py +0 -82
  40. channel_app/channel_app/omnitron/commands/__init__.py +0 -0
  41. channel_app/channel_app/omnitron/commands/batch_requests.py +0 -281
  42. channel_app/channel_app/omnitron/commands/error_reports.py +0 -86
  43. channel_app/channel_app/omnitron/commands/integration_actions.py +0 -200
  44. channel_app/channel_app/omnitron/commands/orders/__init__.py +0 -0
  45. channel_app/channel_app/omnitron/commands/orders/addresses.py +0 -242
  46. channel_app/channel_app/omnitron/commands/orders/cargo_companies.py +0 -40
  47. channel_app/channel_app/omnitron/commands/orders/customers.py +0 -72
  48. channel_app/channel_app/omnitron/commands/orders/orders.py +0 -450
  49. channel_app/channel_app/omnitron/commands/product_categories.py +0 -1
  50. channel_app/channel_app/omnitron/commands/product_images.py +0 -1
  51. channel_app/channel_app/omnitron/commands/product_prices.py +0 -192
  52. channel_app/channel_app/omnitron/commands/product_stocks.py +0 -229
  53. channel_app/channel_app/omnitron/commands/products.py +0 -735
  54. channel_app/channel_app/omnitron/commands/setup.py +0 -839
  55. channel_app/channel_app/omnitron/constants.py +0 -98
  56. channel_app/channel_app/omnitron/exceptions.py +0 -42
  57. channel_app/channel_app/omnitron/integration.py +0 -159
  58. channel_app/setup.py +0 -21
  59. channel_app-0.0.131.dist-info/RECORD +0 -110
  60. /channel_app/{channel_app → omnitron/commands/tests}/__init__.py +0 -0
  61. {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]