tgshops-integrations 1.0__py3-none-any.whl → 1.2__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.
- tgshops_integrations/middlewares/gateway.py +23 -60
- tgshops_integrations/nocodb_connector/client.py +17 -5
- tgshops_integrations/nocodb_connector/model_mapping.py +52 -35
- {tgshops_integrations-1.0.dist-info → tgshops_integrations-1.2.dist-info}/METADATA +1 -1
- {tgshops_integrations-1.0.dist-info → tgshops_integrations-1.2.dist-info}/RECORD +7 -7
- {tgshops_integrations-1.0.dist-info → tgshops_integrations-1.2.dist-info}/WHEEL +0 -0
- {tgshops_integrations-1.0.dist-info → tgshops_integrations-1.2.dist-info}/top_level.txt +0 -0
@@ -5,6 +5,7 @@ from pathlib import Path
|
|
5
5
|
from aiocache import cached
|
6
6
|
from tgshops_integrations.models.products import ProductModel
|
7
7
|
from tgshops_integrations.nocodb_connector.client import NocodbClient
|
8
|
+
|
8
9
|
from tgshops_integrations.nocodb_connector.model_mapping import dump_product_data,dump_product_data_with_check, get_pagination_info, ID_FIELD, \
|
9
10
|
parse_product_data, PRODUCT_CATEGORY_ID_LOOKUP_FIELD, PRODUCT_NAME_FIELD, PRODUCT_PRICE_FIELD, \
|
10
11
|
PRODUCT_STOCK_FIELD
|
@@ -16,41 +17,35 @@ from tgshops_integrations.nocodb_connector.tables import *
|
|
16
17
|
|
17
18
|
from loguru import logger
|
18
19
|
|
19
|
-
# Step 1: Define the path to config.py (one level above)
|
20
|
-
config_path = Path(__file__).resolve().parent.parent / '../config.py'
|
21
|
-
|
22
|
-
# Step 2: Load config.py dynamically using importlib
|
23
|
-
spec = importlib.util.spec_from_file_location("config", config_path)
|
24
|
-
config = importlib.util.module_from_spec(spec)
|
25
|
-
spec.loader.exec_module(config)
|
26
|
-
|
27
|
-
# Step 3: Access variables from config.py
|
28
|
-
NOCODB_CATEGORIES = config.NOCODB_CATEGORIES
|
29
|
-
NOCODB_PRODUCTS = config.NOCODB_PRODUCTS
|
30
|
-
NOCODB_STATUSES = config.NOCODB_STATUSES
|
31
|
-
NOCODB_BOT_MESSAGES = config.NOCODB_BOT_MESSAGES
|
32
|
-
NOCODB_ORDERS = config.NOCODB_ORDERS
|
33
|
-
|
34
20
|
class Gateway(NocodbClient):
|
35
21
|
|
36
|
-
def __init__(self,logging=False,NOCODB_HOST=None,NOCODB_API_KEY=None,SOURCE=None,filter_buttons=[],special_attributes=False):
|
22
|
+
def __init__(self,logging=False,NOCODB_HOST=None,NOCODB_API_KEY=None,SOURCE=None,filter_buttons=[],config_path=None,special_attributes=False):
|
37
23
|
super().__init__(NOCODB_HOST=NOCODB_HOST,NOCODB_API_KEY=NOCODB_API_KEY,SOURCE=SOURCE)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
self.
|
24
|
+
if config_path:
|
25
|
+
self.load_config_from_path(config_path)
|
26
|
+
|
27
|
+
self.logging = logging
|
28
|
+
self.required_fields = [self.config.PRODUCT_NAME_FIELD, self.config.PRODUCT_PRICE_FIELD]
|
42
29
|
self.projection = []
|
43
|
-
self.special_attributes=special_attributes
|
44
|
-
self.filter_buttons=filter_buttons
|
30
|
+
self.special_attributes = special_attributes
|
31
|
+
self.filter_buttons = filter_buttons
|
32
|
+
|
33
|
+
def load_config_from_path(self,config_path):
|
34
|
+
if config_path.exists():
|
35
|
+
spec = importlib.util.spec_from_file_location("config", config_path)
|
36
|
+
self.config = importlib.util.module_from_spec(spec)
|
37
|
+
spec.loader.exec_module(self.config)
|
38
|
+
else:
|
39
|
+
raise FileNotFoundError(f"Configuration file not found at {config_path}")
|
45
40
|
|
46
41
|
async def load_data(self,SOURCE=None):
|
47
42
|
self.SOURCE=SOURCE
|
48
43
|
await self.get_all_tables()
|
49
|
-
self.category_manager=CategoryManager(table_id=self.tables_list[NOCODB_CATEGORIES],NOCODB_HOST=self.NOCODB_HOST,NOCODB_API_KEY=self.NOCODB_API_KEY,logging=True,filter_buttons=self.filter_buttons)
|
50
|
-
self.product_manager=ProductManager(table_id=self.tables_list[NOCODB_PRODUCTS],NOCODB_HOST=self.NOCODB_HOST,NOCODB_API_KEY=self.NOCODB_API_KEY,logging=True)
|
44
|
+
self.category_manager=CategoryManager(table_id=self.tables_list[self.config.NOCODB_CATEGORIES],NOCODB_HOST=self.NOCODB_HOST,NOCODB_API_KEY=self.NOCODB_API_KEY,logging=True,filter_buttons=self.filter_buttons)
|
45
|
+
self.product_manager=ProductManager(table_id=self.tables_list[self.config.NOCODB_PRODUCTS],NOCODB_HOST=self.NOCODB_HOST,NOCODB_API_KEY=self.NOCODB_API_KEY,logging=True)
|
51
46
|
|
52
47
|
async def create_product(self,product: ProductModel) -> ProductModel:
|
53
|
-
products_table = self.tables_list[NOCODB_PRODUCTS]
|
48
|
+
products_table = self.tables_list[self.config.NOCODB_PRODUCTS]
|
54
49
|
data = dump_product_data_with_check(data=product ,data_check=self.category_manager.categories)
|
55
50
|
# product_json = dump_product_data_with_check(data=product,data_check=self.categories)
|
56
51
|
external_id = data.pop("ID")
|
@@ -80,7 +75,7 @@ class Gateway(NocodbClient):
|
|
80
75
|
return actual_products
|
81
76
|
|
82
77
|
async def update_products(self, external_products: List[ProductModel]):
|
83
|
-
products_table = self.tables_list[NOCODB_PRODUCTS]
|
78
|
+
products_table = self.tables_list[self.config.NOCODB_PRODUCTS]
|
84
79
|
await self.product_manager.update_attributes(products=external_products)
|
85
80
|
# Updates categories if there were a new ones created
|
86
81
|
external_products=await self.category_manager.map_categories(external_products=external_products)
|
@@ -99,7 +94,7 @@ class Gateway(NocodbClient):
|
|
99
94
|
await self.create_product(product=product)
|
100
95
|
|
101
96
|
async def update_product(self, product: ProductModel):
|
102
|
-
products_table = self.tables_list[NOCODB_PRODUCTS]
|
97
|
+
products_table = self.tables_list[self.config.NOCODB_PRODUCTS]
|
103
98
|
data = dump_product_data_with_check(data=product ,data_check=self.category_manager.categories)
|
104
99
|
|
105
100
|
await self.update_table_record(
|
@@ -115,40 +110,8 @@ class Gateway(NocodbClient):
|
|
115
110
|
return product.id
|
116
111
|
return None # Return None if no product is found with the given name
|
117
112
|
|
118
|
-
async def create_table_column(self, name: str, table_id: Optional[str] = None):
|
119
|
-
|
120
|
-
BEARER_TOKEN = "jpdxJtyfDXdjbvxKAcIij1HA8HGalgalLLXZ46DV"
|
121
|
-
|
122
|
-
headers = {
|
123
|
-
"Authorization": f"Bearer {BEARER_TOKEN}"
|
124
|
-
}
|
125
|
-
if not table_id:
|
126
|
-
table_id = self.tables_list[NOCODB_PRODUCTS]
|
127
|
-
|
128
|
-
response = await self.httpx_client.post(
|
129
|
-
f"{self.NOCODB_HOST.replace('/api/v2', '/api/v1')}/db/meta/tables/{table_id}/columns",
|
130
|
-
json={
|
131
|
-
"column_name": name,
|
132
|
-
"dt": "character varying",
|
133
|
-
"dtx": "specificType",
|
134
|
-
"ct": "varchar(45)",
|
135
|
-
"clen": 45,
|
136
|
-
"dtxp": "45",
|
137
|
-
"dtxs": "",
|
138
|
-
"altered": 1,
|
139
|
-
"uidt": "SingleLineText",
|
140
|
-
"uip": "",
|
141
|
-
"uicn": "",
|
142
|
-
"title": name
|
143
|
-
},
|
144
|
-
headers=headers
|
145
|
-
)
|
146
|
-
logger.info(response.text())
|
147
|
-
|
148
|
-
return response.json()
|
149
|
-
|
150
113
|
async def delete_all_products(self):
|
151
114
|
items = await self.product_manager.get_products_v2(offset=0,limit=200)
|
152
|
-
products_table = self.tables_list[NOCODB_PRODUCTS]
|
115
|
+
products_table = self.tables_list[self.config.NOCODB_PRODUCTS]
|
153
116
|
for num,item in enumerate(items):
|
154
117
|
await self.delete_table_record(products_table, item.id)
|
@@ -267,10 +267,22 @@ class NocodbClient:
|
|
267
267
|
files = {'file': (image_name, file, 'image/jpeg')}
|
268
268
|
|
269
269
|
url = f"{self.NOCODB_HOST.replace('/api/v2', '/api/v1')}/db/storage/upload?path=noco/{source_column_id}/{product_table_name}/{images_column_id}"
|
270
|
-
timeout = httpx.Timeout(
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
270
|
+
timeout = httpx.Timeout(200.0)
|
271
|
+
try:
|
272
|
+
# First attempt
|
273
|
+
response = await self.httpx_client.post(url, files=files, headers=self.httpx_client.headers, timeout=timeout)
|
274
|
+
if response.status_code == 200:
|
275
|
+
return response.json()[0]['url']
|
276
|
+
|
277
|
+
# If first attempt fails, retry once
|
278
|
+
response = await self.httpx_client.post(url, files=files, headers=self.httpx_client.headers, timeout=timeout)
|
279
|
+
if response.status_code == 200:
|
280
|
+
return response.json()[0]['url']
|
281
|
+
|
282
|
+
except httpx.HTTPStatusError as e:
|
283
|
+
raise Exception(f"HTTP error occurred: {e}")
|
284
|
+
except Exception as e:
|
285
|
+
raise Exception(f"Request failed: {str(e)}")
|
286
|
+
|
275
287
|
else:
|
276
288
|
return ""
|
@@ -11,41 +11,58 @@ from tgshops_integrations.models.products import ProductModel
|
|
11
11
|
import importlib.util
|
12
12
|
from pathlib import Path
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
PRODUCT_NAME_FIELD
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
14
|
+
|
15
|
+
# Helper function to load config.py dynamically
|
16
|
+
def load_config(config_path):
|
17
|
+
config_path = Path(config_path)
|
18
|
+
if config_path.exists():
|
19
|
+
spec = importlib.util.spec_from_file_location("config", config_path)
|
20
|
+
config = importlib.util.module_from_spec(spec)
|
21
|
+
spec.loader.exec_module(config)
|
22
|
+
return config
|
23
|
+
else:
|
24
|
+
raise FileNotFoundError(f"Configuration file not found at {config_path}")
|
25
|
+
|
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_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
|
+
|
35
|
+
config = load_config(config_path)
|
36
|
+
|
37
|
+
# Step 3: Load all required constants from config.py
|
38
|
+
CATEGORY_IMAGE_FIELD = config.CATEGORY_IMAGE_FIELD
|
39
|
+
ID_FIELD = config.ID_FIELD
|
40
|
+
CATEGORY_NAME_FIELD = config.CATEGORY_NAME_FIELD
|
41
|
+
CATEGORY_PARENT_ID_FIELD = config.CATEGORY_PARENT_ID_FIELD
|
42
|
+
CATEGORY_PARENT_FIELD = config.CATEGORY_PARENT_FIELD
|
43
|
+
CATEGORY_ID_OF_CATEGORY_FIELD = config.CATEGORY_ID_OF_CATEGORY_FIELD
|
44
|
+
|
45
|
+
PRODUCT_NAME_FIELD = config.PRODUCT_NAME_FIELD
|
46
|
+
PRODUCT_DESCRIPTION_FIELD = config.PRODUCT_DESCRIPTION_FIELD
|
47
|
+
PRODUCT_PRICE_FIELD = config.PRODUCT_PRICE_FIELD
|
48
|
+
PRODUCT_CURRENCY_FIELD = config.PRODUCT_CURRENCY_FIELD
|
49
|
+
PRODUCT_STOCK_FIELD = config.PRODUCT_STOCK_FIELD
|
50
|
+
PRODUCT_CATEGORY_NAME_FIELD = config.PRODUCT_CATEGORY_NAME_FIELD
|
51
|
+
|
52
|
+
PRODUCT_CATEGORY_ID_FIELD = config.PRODUCT_CATEGORY_ID_FIELD
|
53
|
+
PRODUCT_IMAGE_FIELD = config.PRODUCT_IMAGE_FIELD
|
54
|
+
PRODUCT_DISCOUNT_PRICE_FIELD = config.PRODUCT_DISCOUNT_PRICE_FIELD
|
55
|
+
PRODUCT_CATEGORY_ID_LOOKUP_FIELD = config.PRODUCT_CATEGORY_ID_LOOKUP_FIELD
|
56
|
+
PRODUCT_REQUIRED_OPTIONS_FIELD = config.PRODUCT_REQUIRED_OPTIONS_FIELD
|
57
|
+
PRODUCT_CATEGORIES_EXTRA_OPTIONS_FIELD = config.PRODUCT_CATEGORIES_EXTRA_OPTIONS_FIELD
|
58
|
+
PRODUCT_CATEGORIES_EXTRA_OPTION_NAMES_FIELD = config.PRODUCT_CATEGORIES_EXTRA_OPTION_NAMES_FIELD
|
59
|
+
PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD = config.PRODUCT_EXTRA_CHOICE_REQUIRED_FIELD
|
60
|
+
PRODUCT_ID_FIELD = config.PRODUCT_ID_FIELD
|
61
|
+
PRODUCT_EXTERNAL_ID = config.PRODUCT_EXTERNAL_ID
|
62
|
+
PRODUCT_CHECKOUT_MODE = config.PRODUCT_CHECKOUT_MODE
|
63
|
+
NEW_ID_FIELD = config.NEW_ID_FIELD
|
64
|
+
|
65
|
+
NOCODB_CHECKOUT_MODES = config.NOCODB_CHECKOUT_MODES
|
49
66
|
|
50
67
|
|
51
68
|
def get_pagination_info(page_info: dict) -> PaginationResponseModel:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: tgshops-integrations
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.2
|
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
|
@@ -1,16 +1,16 @@
|
|
1
1
|
tgshops_integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
tgshops_integrations/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
tgshops_integrations/middlewares/gateway.py,sha256=
|
3
|
+
tgshops_integrations/middlewares/gateway.py,sha256=TLHASMEJMky-hYTqU5Q2Dh3C4hIjfAN-l6HoP6PzQpQ,6325
|
4
4
|
tgshops_integrations/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
tgshops_integrations/models/categories.py,sha256=EG6C8g5dOfXB2MH-vtqH13aqB7_VyOobY2FHpDb-fsY,977
|
6
6
|
tgshops_integrations/models/products.py,sha256=i0vP_eJMVCB-W25BCoodIB0AhsMTqYiDO48N-B6Ueo0,1379
|
7
7
|
tgshops_integrations/nocodb_connector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
tgshops_integrations/nocodb_connector/categories.py,sha256=YfQQ8UAkOonNZKO-oTH36vODu0eE-ElG-Me2bH5LdEM,9072
|
9
|
-
tgshops_integrations/nocodb_connector/client.py,sha256=
|
10
|
-
tgshops_integrations/nocodb_connector/model_mapping.py,sha256=
|
9
|
+
tgshops_integrations/nocodb_connector/client.py,sha256=FWEWSL5uxZJIIP8gNcfZT3KyBh1CLm9lsAElD93iyCU,12063
|
10
|
+
tgshops_integrations/nocodb_connector/model_mapping.py,sha256=CeGAwEZLK_NlK_eBTtsH6oRsjo6Z-QxWdC9KnwEhtPw,8770
|
11
11
|
tgshops_integrations/nocodb_connector/products.py,sha256=23uXnmznJN6fZw3tZ3a7dJg06LkI2QaNfVhSKochPn4,8677
|
12
12
|
tgshops_integrations/nocodb_connector/tables.py,sha256=ha_QXZXd93mht0fR5E1nM0wUpz1ePon-pIdO2HI67l8,356
|
13
|
-
tgshops_integrations-1.
|
14
|
-
tgshops_integrations-1.
|
15
|
-
tgshops_integrations-1.
|
16
|
-
tgshops_integrations-1.
|
13
|
+
tgshops_integrations-1.2.dist-info/METADATA,sha256=3PxRU6u73rR4bA2IzvCjKHV5UCEXEA02_J1fOL-46i4,2774
|
14
|
+
tgshops_integrations-1.2.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
15
|
+
tgshops_integrations-1.2.dist-info/top_level.txt,sha256=HFNtxqDpzmlF4ZLnMiwhbU7pOa_YozxU2zBl0bnUmcY,21
|
16
|
+
tgshops_integrations-1.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|