tgshops-integrations 2.4__py3-none-any.whl → 3.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,19 +1,17 @@
1
1
  import json
2
2
  import secrets
3
-
4
- import markdown
5
-
6
- from tgshops_integrations.models.categories import CategoryModel
7
- from tgshops_integrations.models.products import ExtraAttribute, ProductModel
8
- from tgshops_integrations.models.categories import CategoryResponseModel,PaginationResponseModel
9
- from tgshops_integrations.models.products import ProductModel
10
-
11
3
  import importlib.util
12
4
  from pathlib import Path
13
5
 
6
+ from tgshops_integrations.models.categories import CategoryModel, CategoryResponseModel, PaginationResponseModel
7
+ from tgshops_integrations.models.products import ExtraAttribute, ProductModel
8
+
14
9
 
15
- # Helper function to load config.py dynamically
16
- def load_config(config_path):
10
+ # Helper function to load `config.py` dynamically
11
+ def load_config(config_path: str):
12
+ """
13
+ Dynamically load a config file from the provided path.
14
+ """
17
15
  config_path = Path(config_path)
18
16
  if config_path.exists():
19
17
  spec = importlib.util.spec_from_file_location("config", config_path)
@@ -23,18 +21,22 @@ def load_config(config_path):
23
21
  else:
24
22
  raise FileNotFoundError(f"Configuration file not found at {config_path}")
25
23
 
26
- # Modify this to accept config_path dynamically
27
- def initialize_model_mapping(config_path):
28
- global CATEGORY_IMAGE_FIELD, ID_FIELD, CATEGORY_NAME_FIELD, CATEGORY_PARENT_ID_FIELD, CATEGORY_PARENT_FIELD,CATEGORY_ID_OF_CATEGORY_FIELD
29
- global PRODUCT_NAME_FIELD, PRODUCT_DESCRIPTION_FIELD, PRODUCT_PRICE_FIELD, PRODUCT_CURRENCY_FIELD, PRODUCT_STOCK_FIELD
30
- global PRODUCT_CATEGORY_NAME_FIELD, PRODUCT_CATEGORY_ID_FIELD, PRODUCT_IMAGE_FIELD, PRODUCT_DISCOUNT_PRICE_FIELD
31
- global PRODUCT_CATEGORY_ID_LOOKUP_FIELD,PRODUCT_IMAGES_LOOKUP_FIELD, PRODUCT_REQUIRED_OPTIONS_FIELD, PRODUCT_CATEGORIES_EXTRA_OPTIONS_FIELD
32
- global PRODUCT_CATEGORIES_EXTRA_OPTION_NAMES_FIELD, PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD, PRODUCT_ID_FIELD
33
- global PRODUCT_EXTERNAL_ID, PRODUCT_CHECKOUT_MODE, NEW_ID_FIELD, NOCODB_CHECKOUT_MODES
34
-
24
+
25
+ # Initialize model mapping constants dynamically from config
26
+ def initialize_model_mapping(config_path: str):
27
+ """
28
+ Load and initialize global constants for model mapping from a config file.
29
+ """
30
+ global CATEGORY_IMAGE_FIELD, ID_FIELD, CATEGORY_NAME_FIELD, CATEGORY_PARENT_ID_FIELD, CATEGORY_PARENT_FIELD
31
+ global CATEGORY_ID_OF_CATEGORY_FIELD, PRODUCT_NAME_FIELD, PRODUCT_DESCRIPTION_FIELD, PRODUCT_PRICE_FIELD
32
+ global PRODUCT_CURRENCY_FIELD, PRODUCT_STOCK_FIELD, PRODUCT_CATEGORY_NAME_FIELD, PRODUCT_CATEGORY_ID_FIELD
33
+ global PRODUCT_IMAGE_FIELD, PRODUCT_DISCOUNT_PRICE_FIELD, PRODUCT_CATEGORY_ID_LOOKUP_FIELD, PRODUCT_IMAGES_LOOKUP_FIELD
34
+ global PRODUCT_REQUIRED_OPTIONS_FIELD, PRODUCT_CATEGORIES_EXTRA_OPTIONS_FIELD, PRODUCT_CATEGORIES_EXTRA_OPTION_NAMES_FIELD
35
+ global PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD, PRODUCT_ID_FIELD, PRODUCT_EXTERNAL_ID, PRODUCT_CHECKOUT_MODE
36
+ global NEW_ID_FIELD, NOCODB_CHECKOUT_MODES
37
+
35
38
  config = load_config(config_path)
36
-
37
- # Step 3: Load all required constants from config.py
39
+
38
40
  CATEGORY_IMAGE_FIELD = config.CATEGORY_IMAGE_FIELD
39
41
  ID_FIELD = config.ID_FIELD
40
42
  CATEGORY_NAME_FIELD = config.CATEGORY_NAME_FIELD
@@ -48,7 +50,6 @@ def initialize_model_mapping(config_path):
48
50
  PRODUCT_CURRENCY_FIELD = config.PRODUCT_CURRENCY_FIELD
49
51
  PRODUCT_STOCK_FIELD = config.PRODUCT_STOCK_FIELD
50
52
  PRODUCT_CATEGORY_NAME_FIELD = config.PRODUCT_CATEGORY_NAME_FIELD
51
-
52
53
  PRODUCT_CATEGORY_ID_FIELD = config.PRODUCT_CATEGORY_ID_FIELD
53
54
  PRODUCT_IMAGE_FIELD = config.PRODUCT_IMAGE_FIELD
54
55
  PRODUCT_DISCOUNT_PRICE_FIELD = config.PRODUCT_DISCOUNT_PRICE_FIELD
@@ -62,22 +63,27 @@ def initialize_model_mapping(config_path):
62
63
  PRODUCT_EXTERNAL_ID = config.PRODUCT_EXTERNAL_ID
63
64
  PRODUCT_CHECKOUT_MODE = config.PRODUCT_CHECKOUT_MODE
64
65
  NEW_ID_FIELD = config.NEW_ID_FIELD
65
-
66
66
  NOCODB_CHECKOUT_MODES = config.NOCODB_CHECKOUT_MODES
67
67
 
68
68
 
69
69
  def get_pagination_info(page_info: dict) -> PaginationResponseModel:
70
- page_info = PaginationResponseModel(total_rows=page_info['totalRows'],
71
- page=page_info['page'],
72
- page_size=page_info['pageSize'],
73
- is_first_page=page_info['isFirstPage'],
74
- is_last_page=page_info['isLastPage'])
75
- return page_info
70
+ """
71
+ Parses pagination information into a model.
72
+ """
73
+ return PaginationResponseModel(
74
+ total_rows=page_info['totalRows'],
75
+ page=page_info['page'],
76
+ page_size=page_info['pageSize'],
77
+ is_first_page=page_info['isFirstPage'],
78
+ is_last_page=page_info['isLastPage']
79
+ )
80
+
76
81
 
77
82
  def parse_category_data(data: dict) -> CategoryResponseModel:
78
- preview_url = ""
79
- if data.get(CATEGORY_IMAGE_FIELD):
80
- preview_url = data[CATEGORY_IMAGE_FIELD][0].get("url", "")
83
+ """
84
+ Parses raw category data into a structured model.
85
+ """
86
+ preview_url = data.get(CATEGORY_IMAGE_FIELD, [{}])[0].get("url", "") if data.get(CATEGORY_IMAGE_FIELD) else ""
81
87
  return CategoryResponseModel(
82
88
  id=str(data[ID_FIELD]),
83
89
  name=data.get(CATEGORY_NAME_FIELD, ""),
@@ -87,22 +93,26 @@ def parse_category_data(data: dict) -> CategoryResponseModel:
87
93
 
88
94
 
89
95
  def dump_category_data(data: CategoryModel) -> dict:
96
+ """
97
+ Converts a CategoryModel into a dictionary suitable for API calls.
98
+ """
90
99
  return {
91
100
  CATEGORY_NAME_FIELD: data.name,
92
101
  CATEGORY_PARENT_FIELD: data.parent_category,
93
102
  CATEGORY_IMAGE_FIELD: [
94
- {"url": data.preview_url, 'title': f'{secrets.token_hex(6)}.jpeg', 'mimetype': 'image/jpeg'}]
103
+ {"url": data.preview_url, "title": f"{secrets.token_hex(6)}.jpeg", "mimetype": "image/jpeg"}
104
+ ]
95
105
  }
96
106
 
97
107
 
98
108
  def dump_product_data(data: ProductModel) -> dict:
99
-
100
- preview_url = ([{'url': image_url,
101
- 'title': f'{secrets.token_hex(6)}.jpeg',
102
- 'mimetype': 'image/jpeg'}
103
- for image_url in data.preview_url]
104
- if data.preview_url
105
- else [])
109
+ """
110
+ Converts a ProductModel into a dictionary suitable for API calls.
111
+ """
112
+ preview_url = [
113
+ {"url": image_url, "title": f"{secrets.token_hex(6)}.jpeg", "mimetype": "image/jpeg"}
114
+ for image_url in data.preview_url
115
+ ] if data.preview_url else []
106
116
 
107
117
  return {
108
118
  PRODUCT_NAME_FIELD: data.name,
@@ -110,23 +120,23 @@ def dump_product_data(data: ProductModel) -> dict:
110
120
  PRODUCT_PRICE_FIELD: data.price,
111
121
  PRODUCT_CURRENCY_FIELD: data.currency,
112
122
  PRODUCT_STOCK_FIELD: data.stock_qty,
113
- PRODUCT_CATEGORY_NAME_FIELD:[data.category_name] if data.category_name else None,
114
- #TODO category parameter is deprecated
115
- PRODUCT_CATEGORY_ID_FIELD: [{'Id': data.category}] if data.category else None,
123
+ PRODUCT_CATEGORY_NAME_FIELD: [data.category_name] if data.category_name else None,
124
+ PRODUCT_CATEGORY_ID_FIELD: [{"Id": data.category}] if data.category else None,
116
125
  PRODUCT_IMAGE_FIELD: preview_url,
117
126
  PRODUCT_DISCOUNT_PRICE_FIELD: data.final_price
118
127
  }
119
128
 
129
+
120
130
  def dump_product_data_with_check(data: ProductModel, data_check: dict) -> dict:
121
-
122
- preview_url = ([{'url': image_url,
123
- 'title': f'{secrets.token_hex(6)}.jpeg',
124
- 'mimetype': 'image/jpeg'}
125
- for image_url in data.preview_url]
126
- if data.preview_url
127
- else [])
128
-
129
- extra_data = {item.name : item.description for item in data.extra_attributes}
131
+ """
132
+ Converts a ProductModel into a dictionary and validates categories.
133
+ """
134
+ preview_url = [
135
+ {"url": image_url, "title": f"{secrets.token_hex(6)}.jpeg", "mimetype": "image/jpeg"}
136
+ for image_url in data.preview_url
137
+ ] if data.preview_url else []
138
+
139
+ extra_data = {attr.name: attr.description for attr in data.extra_attributes}
130
140
 
131
141
  product_data = {
132
142
  PRODUCT_ID_FIELD: data.id,
@@ -136,54 +146,53 @@ def dump_product_data_with_check(data: ProductModel, data_check: dict) -> dict:
136
146
  PRODUCT_PRICE_FIELD: data.price,
137
147
  PRODUCT_CURRENCY_FIELD: data.currency,
138
148
  PRODUCT_STOCK_FIELD: data.stock_qty,
139
- PRODUCT_CATEGORY_NAME_FIELD:[data.category_name] if data.category_name else None,
140
- PRODUCT_CATEGORY_ID_FIELD: [{'Id': data_check[item]} for item in data.category_name] if data.category_name else None,
149
+ PRODUCT_CATEGORY_NAME_FIELD: [data.product_properties] if data.product_properties else None,
150
+ PRODUCT_CATEGORY_ID_FIELD: [{"Id": data_check[item]} for item in data.product_properties] if data.product_properties else None,
141
151
  PRODUCT_IMAGE_FIELD: preview_url,
142
152
  PRODUCT_CHECKOUT_MODE: NOCODB_CHECKOUT_MODES,
143
153
  PRODUCT_DISCOUNT_PRICE_FIELD: data.final_price,
144
- }
145
-
146
- if len(extra_data)>0:
154
+ }
155
+
156
+ if extra_data:
147
157
  product_data.update(extra_data)
148
158
  return product_data
149
159
 
150
160
 
151
161
  async def parse_product_data(data: dict) -> ProductModel:
152
- preview_url = [image['url'] for image in data[PRODUCT_IMAGE_FIELD]] if data.get(PRODUCT_IMAGE_FIELD, '') else []
153
- primary_keys = [ID_FIELD,PRODUCT_NAME_FIELD, PRODUCT_DESCRIPTION_FIELD, PRODUCT_PRICE_FIELD,
154
- PRODUCT_CURRENCY_FIELD, PRODUCT_STOCK_FIELD, PRODUCT_CATEGORY_ID_FIELD, PRODUCT_IMAGE_FIELD,
155
- PRODUCT_CATEGORY_NAME_FIELD, PRODUCT_DISCOUNT_PRICE_FIELD, PRODUCT_CATEGORY_ID_LOOKUP_FIELD,
156
- PRODUCT_REQUIRED_OPTIONS_FIELD, PRODUCT_CATEGORIES_EXTRA_OPTIONS_FIELD,
157
- PRODUCT_CATEGORIES_EXTRA_OPTION_NAMES_FIELD, PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD,
158
- "UpdatedAt", "CreatedAt"]
159
-
160
- # Dynamically adding extra attributes
161
- extra_attributes = []
162
- for key, value in data.items():
163
- if key not in primary_keys and value is not None and type(value) in [str, int, float]:
164
- extra_attributes.append(ExtraAttribute(name=key, description=str(value)))
162
+ """
163
+ Parses raw product data into a ProductModel.
164
+ """
165
+ preview_url = [image['url'] for image in data.get(PRODUCT_IMAGE_FIELD, [])]
166
+
167
+ # Dynamically add extra attributes
168
+ extra_attributes = [
169
+ ExtraAttribute(name=key, description=str(value))
170
+ for key, value in data.items()
171
+ if key not in {
172
+ ID_FIELD, PRODUCT_NAME_FIELD, PRODUCT_DESCRIPTION_FIELD, PRODUCT_PRICE_FIELD, PRODUCT_CURRENCY_FIELD,
173
+ PRODUCT_STOCK_FIELD, PRODUCT_CATEGORY_ID_FIELD, PRODUCT_IMAGE_FIELD, PRODUCT_CATEGORY_NAME_FIELD,
174
+ PRODUCT_DISCOUNT_PRICE_FIELD, PRODUCT_CATEGORY_ID_LOOKUP_FIELD, PRODUCT_REQUIRED_OPTIONS_FIELD,
175
+ PRODUCT_CATEGORIES_EXTRA_OPTIONS_FIELD, PRODUCT_CATEGORIES_EXTRA_OPTION_NAMES_FIELD,
176
+ PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD, "UpdatedAt", "CreatedAt"
177
+ } and value is not None and isinstance(value, (str, int, float))
178
+ ]
165
179
 
166
180
  product = ProductModel(
167
- id=str(data[ID_FIELD]) if data.get(ID_FIELD) else data.get(NEW_ID_FIELD),
181
+ id=str(data.get(ID_FIELD, data.get(NEW_ID_FIELD, ""))),
168
182
  external_id=data.get(PRODUCT_EXTERNAL_ID, ""),
169
183
  name=data.get(PRODUCT_NAME_FIELD, ""),
170
- description=data.get(PRODUCT_DESCRIPTION_FIELD, "") if data.get(PRODUCT_DESCRIPTION_FIELD) else "",
184
+ description=data.get(PRODUCT_DESCRIPTION_FIELD, ""),
171
185
  price=data.get(PRODUCT_PRICE_FIELD, 0.0),
172
- currency=data.get(PRODUCT_CURRENCY_FIELD, ["RUB","CZK","GBP"]) if data.get(PRODUCT_CURRENCY_FIELD) else "RUB",
186
+ final_price=data.get(PRODUCT_DISCOUNT_PRICE_FIELD, 0.0),
187
+ currency=data.get(PRODUCT_CURRENCY_FIELD, "RUB"),
173
188
  stock_qty=data.get(PRODUCT_STOCK_FIELD, 0),
174
189
  preview_url=preview_url,
175
- category_name=data.get(PRODUCT_CATEGORY_NAME_FIELD, []) if data.get(PRODUCT_CATEGORY_NAME_FIELD) else [],
176
- category=data.get(PRODUCT_CATEGORY_ID_LOOKUP_FIELD, []) if data.get(PRODUCT_CATEGORY_ID_LOOKUP_FIELD) else [],
177
- # category=[],
190
+ category_name=data.get(PRODUCT_CATEGORY_NAME_FIELD, []),
191
+ category=data.get(PRODUCT_CATEGORY_ID_LOOKUP_FIELD, []),
178
192
  extra_attributes=extra_attributes,
179
- extra_option_choice_required=any(data.get(PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD, [])),
180
- metadata = data
193
+ extra_option_choice_required=bool(data.get(PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD, [])),
194
+
195
+ metadata=data
181
196
  )
182
- if data.get(PRODUCT_DISCOUNT_PRICE_FIELD, data.get(PRODUCT_PRICE_FIELD, 0.0)):
183
- product.final_price = data.get(PRODUCT_DISCOUNT_PRICE_FIELD, data.get(PRODUCT_PRICE_FIELD, 0.0))
184
197
 
185
198
  return product
186
-
187
-
188
-
189
-
@@ -0,0 +1,151 @@
1
+ from typing import List, Optional
2
+ import hashlib
3
+ from aiocache import cached
4
+ from loguru import logger
5
+ from models.products import ProductModel
6
+ from tgshops_integrations.nocodb_connector.client import custom_key_builder, NocodbClient
7
+ from tgshops_integrations.nocodb_connector.model_mapping import (
8
+ dump_product_data,
9
+ dump_product_data_with_check,
10
+ get_pagination_info,
11
+ parse_product_data,
12
+ ID_FIELD,
13
+ PRODUCT_CATEGORY_ID_LOOKUP_FIELD,
14
+ PRODUCT_NAME_FIELD,
15
+ PRODUCT_PRICE_FIELD,
16
+ PRODUCT_STOCK_FIELD,
17
+ PRODUCT_EXTERNAL_ID,
18
+ PRODUCT_IMAGES_LOOKUP_FIELD,
19
+ )
20
+
21
+ class ProductManager(NocodbClient):
22
+ def __init__(self, table_id=None, logging=False, NOCODB_HOST=None, NOCODB_API_KEY=None, SOURCE=None):
23
+ super().__init__(NOCODB_HOST=NOCODB_HOST, NOCODB_API_KEY=NOCODB_API_KEY, SOURCE=SOURCE)
24
+ self.logging = logging
25
+ self.required_fields = [PRODUCT_NAME_FIELD, PRODUCT_PRICE_FIELD]
26
+ self.projection = []
27
+ self.products_table = table_id
28
+ self.columns = []
29
+
30
+ def hash_product(self, product: ProductModel, special_attributes=False) -> str:
31
+ """
32
+ Generates a hash of the product for comparison.
33
+ """
34
+ if special_attributes:
35
+ hash_string = ''.join(attr.description for attr in product.extra_attributes if attr.name.endswith('*'))
36
+ else:
37
+ hash_string = f"{product.external_id}{product.price}{sorted(product.product_properties)}{product.name}{product.description}"
38
+
39
+ return hashlib.sha256(hash_string.encode()).hexdigest()
40
+
41
+ @cached(ttl=30, key_builder=custom_key_builder)
42
+ async def get_products(self) -> List[ProductModel]:
43
+ """
44
+ Fetches all products from the table.
45
+ """
46
+ records = await self.get_table_records(self.products_table, self.required_fields, self.projection)
47
+ return [parse_product_data(record) for record in records]
48
+
49
+ async def get_products_v2(self, offset: int, limit: int) -> List[ProductModel]:
50
+ """
51
+ Fetches paginated products.
52
+ """
53
+ response = await self.get_table_records_v2(
54
+ table_name=self.products_table,
55
+ required_fields=self.required_fields,
56
+ projection=self.projection,
57
+ offset=offset,
58
+ limit=limit,
59
+ )
60
+ return [await parse_product_data(record) for record in response['list']]
61
+
62
+ @cached(ttl=180, key_builder=custom_key_builder)
63
+ async def search_products(self, search_string: str, limit: int) -> List[ProductModel]:
64
+ """
65
+ Searches for products with names containing the search string.
66
+ """
67
+ records = await self.get_table_records(
68
+ table_name=self.products_table,
69
+ required_fields=self.required_fields,
70
+ projection=self.projection,
71
+ extra_where=f"({PRODUCT_NAME_FIELD},like,%{search_string}%)",
72
+ limit=limit,
73
+ )
74
+ return [parse_product_data(record) for record in records]
75
+
76
+ @cached(ttl=60, key_builder=custom_key_builder)
77
+ async def get_product(self, product_id: str) -> ProductModel:
78
+ """
79
+ Fetches a single product by its ID.
80
+ """
81
+ record = await self.get_table_record(self.products_table, product_id)
82
+ return parse_product_data(record)
83
+
84
+ @cached(ttl=60, key_builder=custom_key_builder)
85
+ async def get_product_in_category(self, category_id: Optional[str] = None) -> List[ProductModel]:
86
+ """
87
+ Fetches products within a specific category or all products if no category is specified.
88
+ """
89
+ extra_where = None
90
+ if category_id:
91
+ extra_where = f"({PRODUCT_STOCK_FIELD},gt,0)~and({PRODUCT_CATEGORY_ID_LOOKUP_FIELD},eq,{category_id})"
92
+
93
+ records = await self.get_table_records(
94
+ table_name=self.products_table,
95
+ required_fields=self.required_fields,
96
+ projection=self.projection,
97
+ extra_where=extra_where,
98
+ )
99
+ return [parse_product_data(record) for record in records]
100
+
101
+ async def update_product(self, product: ProductModel):
102
+ """
103
+ Updates an existing product in the table.
104
+ """
105
+ data = dump_product_data_with_check(data=product, data_check=self.category_manager.categories)
106
+ await self.update_table_record(
107
+ table_name=self.products_table,
108
+ record_id=product.id,
109
+ updated_data=data,
110
+ )
111
+ logger.info(f"Updated product {product.external_id}")
112
+
113
+ async def create_product(self, checked_data: dict, product: ProductModel) -> ProductModel:
114
+ """
115
+ Creates a new product in the table.
116
+ """
117
+ external_id = checked_data.pop("ID")
118
+ metadata = await self.get_table_meta(self.products_table)
119
+ images_column = next(
120
+ column["id"]
121
+ for column in metadata["columns"]
122
+ if column["column_name"] == "Images"
123
+ )
124
+
125
+ checked_data[PRODUCT_IMAGES_LOOKUP_FIELD] = [image['title'] for image in checked_data['Images']]
126
+
127
+ for num, item in enumerate(checked_data['Images']):
128
+ item['url'] = await self.save_image_to_nocodb(
129
+ source_column_id=self.SOURCE,
130
+ image_url=item['url'],
131
+ image_name=item['title'],
132
+ product_table_name=self.products_table,
133
+ images_column_id=images_column,
134
+ )
135
+
136
+ record = await self.create_table_record(table_name=self.products_table, record=checked_data)
137
+ logger.info(f"Created product {external_id}")
138
+ return record
139
+
140
+ async def get_all_products(self) -> List[ProductModel]:
141
+ """
142
+ Fetches all products in paginated portions.
143
+ """
144
+ all_products = []
145
+ portion = 200
146
+ for i in range(10): # Limit to 10 iterations
147
+ products_portion = await self.get_products_v2(offset=i * portion, limit=portion)
148
+ all_products.extend(products_portion)
149
+ if len(products_portion) < portion:
150
+ break
151
+ return all_products
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tgshops-integrations
3
- Version: 2.4
3
+ Version: 3.1
4
4
  Summary: Library is intended to provide the integration of the external service or CRM system with the TelegramShops/It allows to configure the relationship between NocoDB list of the products used further to display in the shop/As a resultss the products can be synchronized and updated uppon the request.
5
5
  Home-page: https://git.the-devs.com/virtual-shops/shop-system/shop-backend-integrations/integration-library/integration-library
6
6
  Author: Dimi Latoff
@@ -0,0 +1,18 @@
1
+ tgshops_integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ tgshops_integrations/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ tgshops_integrations/middlewares/gateway.py,sha256=-YQi3-62k6gkxJLx27FESe6wlRrD4_Ltqk8wZJ8ExYg,6247
4
+ tgshops_integrations/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ tgshops_integrations/models/categories.py,sha256=EG6C8g5dOfXB2MH-vtqH13aqB7_VyOobY2FHpDb-fsY,977
6
+ tgshops_integrations/models/products.py,sha256=zkRqXLU-tAReDF5R9j3q58le686dw3PapxcFOGUpk7Q,1376
7
+ tgshops_integrations/nocodb_connector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ tgshops_integrations/nocodb_connector/categories.py,sha256=eoAWRA0aSE84_ONrwflM1ZFXLSCFjYl_CFqUf7lA_EU,9063
9
+ tgshops_integrations/nocodb_connector/categories_management.py,sha256=dvPXcrgruJyuWFT7Ho-8YRcjJxhR3Hcw0hJiVKQgcyA,6603
10
+ tgshops_integrations/nocodb_connector/client.py,sha256=U2rNozjluWVul-VZroQW-8b2j6nEtKrhOkFMz1L8KmI,12022
11
+ tgshops_integrations/nocodb_connector/model_mapping.py,sha256=ixv-uT1Pmt1szZCQ5pGsMbwRvmnHZmn0NqG77nxZkDM,8551
12
+ tgshops_integrations/nocodb_connector/products.py,sha256=kAp7lUaRO7CkU_SumbIdLOJf38SmDBEyBBZCYyyyOFM,9313
13
+ tgshops_integrations/nocodb_connector/products_management.py,sha256=t_ZDyIAPTe_6UrxLuJ32Uhlk8ssErP6Eo0n-TIZfEko,6046
14
+ tgshops_integrations/nocodb_connector/tables.py,sha256=ha_QXZXd93mht0fR5E1nM0wUpz1ePon-pIdO2HI67l8,356
15
+ tgshops_integrations-3.1.dist-info/METADATA,sha256=oBAxZoo2AU7A_BKg0kfhnzZMeYli2Du1v901AI_M0Vk,2774
16
+ tgshops_integrations-3.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
17
+ tgshops_integrations-3.1.dist-info/top_level.txt,sha256=HFNtxqDpzmlF4ZLnMiwhbU7pOa_YozxU2zBl0bnUmcY,21
18
+ tgshops_integrations-3.1.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- tgshops_integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- tgshops_integrations/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- tgshops_integrations/middlewares/gateway.py,sha256=Fj7pnFcVxKmrFU4dRN5RI23lM4gG61In7X2fybudS3o,6368
4
- tgshops_integrations/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- tgshops_integrations/models/categories.py,sha256=EG6C8g5dOfXB2MH-vtqH13aqB7_VyOobY2FHpDb-fsY,977
6
- tgshops_integrations/models/products.py,sha256=i0vP_eJMVCB-W25BCoodIB0AhsMTqYiDO48N-B6Ueo0,1379
7
- tgshops_integrations/nocodb_connector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- tgshops_integrations/nocodb_connector/categories.py,sha256=eoAWRA0aSE84_ONrwflM1ZFXLSCFjYl_CFqUf7lA_EU,9063
9
- tgshops_integrations/nocodb_connector/client.py,sha256=RD8UDS4AXrN1GFmjakZsdTGbP_u2bsoxfopq7UV6MT0,11725
10
- tgshops_integrations/nocodb_connector/model_mapping.py,sha256=nsul7OjUHgGhpV-iwwaAvb9SnBlwj4evHDcXSd-v1zk,8919
11
- tgshops_integrations/nocodb_connector/products.py,sha256=kAp7lUaRO7CkU_SumbIdLOJf38SmDBEyBBZCYyyyOFM,9313
12
- tgshops_integrations/nocodb_connector/tables.py,sha256=ha_QXZXd93mht0fR5E1nM0wUpz1ePon-pIdO2HI67l8,356
13
- tgshops_integrations-2.4.dist-info/METADATA,sha256=aDJyso77_TGjthlxfi-BIduQkt1643LBK9fTB0KWCIg,2774
14
- tgshops_integrations-2.4.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
15
- tgshops_integrations-2.4.dist-info/top_level.txt,sha256=HFNtxqDpzmlF4ZLnMiwhbU7pOa_YozxU2zBl0bnUmcY,21
16
- tgshops_integrations-2.4.dist-info/RECORD,,