tgshops-integrations 2.4__py3-none-any.whl → 3.0__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.
@@ -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.0
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=Gzr8gZWQ3B3tiqpE_iSqtAkINDbsm7QT_v_bjT1b8V4,6223
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.0.dist-info/METADATA,sha256=E4e2SV-nFT2HLUEzRZ90PWI1BTAzoaTtnXm-ZYalN5k,2774
16
+ tgshops_integrations-3.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
17
+ tgshops_integrations-3.0.dist-info/top_level.txt,sha256=HFNtxqDpzmlF4ZLnMiwhbU7pOa_YozxU2zBl0bnUmcY,21
18
+ tgshops_integrations-3.0.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,,