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.
- tgshops_integrations/middlewares/gateway.py +116 -90
- tgshops_integrations/models/products.py +2 -3
- tgshops_integrations/nocodb_connector/categories_management.py +149 -0
- tgshops_integrations/nocodb_connector/client.py +189 -181
- tgshops_integrations/nocodb_connector/model_mapping.py +93 -84
- tgshops_integrations/nocodb_connector/products_management.py +151 -0
- {tgshops_integrations-2.4.dist-info → tgshops_integrations-3.0.dist-info}/METADATA +1 -1
- tgshops_integrations-3.0.dist-info/RECORD +18 -0
- tgshops_integrations-2.4.dist-info/RECORD +0 -16
- {tgshops_integrations-2.4.dist-info → tgshops_integrations-3.0.dist-info}/WHEEL +0 -0
- {tgshops_integrations-2.4.dist-info → tgshops_integrations-3.0.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
global
|
31
|
-
|
32
|
-
global
|
33
|
-
global
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
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,
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
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
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
extra_data = {
|
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.
|
140
|
-
PRODUCT_CATEGORY_ID_FIELD: [{
|
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
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
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, "")
|
184
|
+
description=data.get(PRODUCT_DESCRIPTION_FIELD, ""),
|
171
185
|
price=data.get(PRODUCT_PRICE_FIELD, 0.0),
|
172
|
-
|
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, [])
|
176
|
-
category=data.get(PRODUCT_CATEGORY_ID_LOOKUP_FIELD, [])
|
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=
|
180
|
-
|
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:
|
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,,
|
File without changes
|
File without changes
|