amélioration du programme en cas de nécessité de reprise du programme
This commit is contained in:
40
api.py
40
api.py
@ -3,7 +3,33 @@ import base64
|
|||||||
import requests
|
import requests
|
||||||
import ssl
|
import ssl
|
||||||
from requests.adapters import HTTPAdapter
|
from requests.adapters import HTTPAdapter
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
|
log_directory = "logs"
|
||||||
|
|
||||||
|
# 🔧 Configuration du handler avec rotation quotidienne
|
||||||
|
log_file = os.path.join(log_directory, "woocommerce.log")
|
||||||
|
handler = TimedRotatingFileHandler(
|
||||||
|
filename=log_file,
|
||||||
|
when="midnight", # ⏰ Rotation tous les jours à minuit
|
||||||
|
interval=1, # 📅 Chaque 1 jour
|
||||||
|
backupCount=7, # ♻️ Garde les 7 derniers fichiers de log
|
||||||
|
encoding='utf-8' # 🧾 Pour supporter tous les caractères
|
||||||
|
)
|
||||||
|
|
||||||
|
# 📋 Format du log
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
fmt="%(asctime)s - %(levelname)s - %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S"
|
||||||
|
)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 🔌 Récupère le logger
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
logger.setLevel(logging.DEBUG) # 👁 Niveau minimum à capturer
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
class WoocommerceApiClient(API):
|
class WoocommerceApiClient(API):
|
||||||
def __init__(self, url, wc_consumer_key, wc_consumer_secret, wp_application_user, wp_application_password, verify_ssl=False, **kwargs):
|
def __init__(self, url, wc_consumer_key, wc_consumer_secret, wp_application_user, wp_application_password, verify_ssl=False, **kwargs):
|
||||||
@ -59,8 +85,8 @@ class WoocommerceApiClient(API):
|
|||||||
try:
|
try:
|
||||||
if self._is_wc_api(endpoint):
|
if self._is_wc_api(endpoint):
|
||||||
kwargs.update(self._get_requests_general_kwargs(endpoint))
|
kwargs.update(self._get_requests_general_kwargs(endpoint))
|
||||||
print(kwargs) # ✅ Montre tout le dict
|
#print(kwargs) # ✅ Montre tout le dict
|
||||||
print(kwargs["headers"]) # ✅ Si tu veux un champ en particulier
|
#print(kwargs["headers"]) # ✅ Si tu veux un champ en particulier
|
||||||
return requests.get(**kwargs)
|
return requests.get(**kwargs)
|
||||||
else:
|
else:
|
||||||
return super().get(endpoint, **kwargs)
|
return super().get(endpoint, **kwargs)
|
||||||
@ -83,7 +109,7 @@ class WoocommerceApiClient(API):
|
|||||||
kwargs['files'] = files
|
kwargs['files'] = files
|
||||||
if data:
|
if data:
|
||||||
kwargs['json'] = data
|
kwargs['json'] = data
|
||||||
print("kwargs envoyés à requests.post:", kwargs)
|
#print("kwargs envoyés à requests.post:", kwargs)
|
||||||
response = requests.post(**kwargs)
|
response = requests.post(**kwargs)
|
||||||
elif self._is_wp_api(endpoint):
|
elif self._is_wp_api(endpoint):
|
||||||
kwargs.update(self._get_requests_general_kwargs(endpoint))
|
kwargs.update(self._get_requests_general_kwargs(endpoint))
|
||||||
@ -91,10 +117,14 @@ class WoocommerceApiClient(API):
|
|||||||
response = requests.post(**kwargs)
|
response = requests.post(**kwargs)
|
||||||
else:
|
else:
|
||||||
response = super().post(endpoint, data, **kwargs)
|
response = super().post(endpoint, data, **kwargs)
|
||||||
if response and response.status_code not in (500, 400):
|
if response.status_code not in (500, ) or retry >= self.max_retry:
|
||||||
return response
|
return response
|
||||||
|
else:
|
||||||
|
retry += 1
|
||||||
|
logger.debug(f"status_code={response.status_code} - retry #{retry}")
|
||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
if retry < self.max_retry:
|
if retry < self.max_retry:
|
||||||
|
logger.debug(f"got requests.exceptions.ConnectionError - retry #{retry}")
|
||||||
print(".", end="")
|
print(".", end="")
|
||||||
retry += 1
|
retry += 1
|
||||||
else: raise
|
else: raise
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
284
wcctl.py
284
wcctl.py
@ -3,60 +3,151 @@ import argparse
|
|||||||
from woocommerce import API as WoocommerceApi
|
from woocommerce import API as WoocommerceApi
|
||||||
from api import WoocommerceApiClient
|
from api import WoocommerceApiClient
|
||||||
from api_woocommerce import AuthentificationWpApi, MediaManager, CategoryManager, ProductManager, AttributeManager, VariationsManager, TabManager, WooCommerceManager
|
from api_woocommerce import AuthentificationWpApi, MediaManager, CategoryManager, ProductManager, AttributeManager, VariationsManager, TabManager, WooCommerceManager
|
||||||
import sys
|
import os, sys
|
||||||
from base64 import b64encode
|
|
||||||
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def import_medias_ods(args, media_manager):
|
class WcCtlBase:
|
||||||
if args.media:
|
def __init__(self, args):
|
||||||
if args.media_regex:
|
self.args = args
|
||||||
try:
|
self._wcm = None
|
||||||
media_manager.upload_media(args.media_regex)
|
|
||||||
except ValueError:
|
@property
|
||||||
print("error name product_regex")
|
def wcm(self):
|
||||||
elif args.media_range:
|
if self._wcm is None:
|
||||||
try:
|
wcapi = WoocommerceApiClient(
|
||||||
parts = args.media_range.split(':')
|
url=self.args.wc_url,
|
||||||
start = int(parts[0]) -1 if parts[0] else 0
|
wc_consumer_key=self.args.wc_key,
|
||||||
end = int(parts[1]) if len(parts) > 1 and parts[1] else None
|
wc_consumer_secret=self.args.wc_secret,
|
||||||
print(f"start = {start}, end = {end or 'fin'}")
|
wp_application_user="admin_lcdm",
|
||||||
media_manager.upload_media_from_to(start, end)
|
wp_application_password= "Do4p tLYF 5uje PF2M 21Zo x3OR", #"eAB4 49W6 ZRBj zIZc fH62 tv5c", #"yTW8 Mc6J FUCN tPSq bnuJ 0Sdw", #
|
||||||
except ValueError:
|
wp_api=True,
|
||||||
print("❌ Mauvais format pour --media-range. Utilisez par exemple --media-range=1:40")
|
version="wc/v3",
|
||||||
else:
|
verify_ssl=True,
|
||||||
start, end = 0, None
|
timeout=30
|
||||||
print("ℹ️ --media activé, mais aucune plage spécifiée.")
|
)
|
||||||
|
|
||||||
|
ath = AuthentificationWpApi()
|
||||||
|
media_manager = MediaManager(ath, wcapi, filename_ods=self.args.ods_path)
|
||||||
|
category_manager = CategoryManager(wcapi, ath, filename_ods=self.args.ods_path)
|
||||||
|
product_manager = ProductManager(wcapi, ath, filename_ods=self.args.ods_path)
|
||||||
|
attribute_manager = AttributeManager(wcapi, filename_ods=self.args.ods_path)
|
||||||
|
tab_manager = TabManager(wcapi, filename_ods=self.args.ods_path)
|
||||||
|
variation_manager = VariationsManager(wcapi, filename_ods=self.args.ods_path)
|
||||||
|
self._wcm = WooCommerceManager(wcapi=wcapi,
|
||||||
|
media_manager=media_manager,
|
||||||
|
category_manager=category_manager,
|
||||||
|
product_manager=product_manager,
|
||||||
|
tab_manager=tab_manager,
|
||||||
|
attribute_manager=attribute_manager,
|
||||||
|
variation_manager=variation_manager,
|
||||||
|
filename_ods=self.args.ods_path)
|
||||||
|
|
||||||
def import_products_ods(args, woocommerce_manager):
|
return self._wcm
|
||||||
if args.product:
|
|
||||||
if args.product_regex:
|
class DeleteWcCtl(WcCtlBase):
|
||||||
try:
|
def action(self):
|
||||||
woocommerce_manager.process_file(args.product_regex)
|
logger.info(f"DeleteWcCtl:action({self.args}) - begin")
|
||||||
except ValueError:
|
if self.args.all:
|
||||||
print("error name product_regex")
|
return self.wcm.delete_all_informations()
|
||||||
elif args.product_range:
|
|
||||||
try:
|
if self.args.attributes:
|
||||||
parts = args.product_range.split(':')
|
return None
|
||||||
start = int(parts[0]) -1 if parts[0] else 0
|
|
||||||
end = int(parts[1]) if len(parts) > 1 and parts[1] else None
|
class ImportOdsCtl(WcCtlBase):
|
||||||
print(f"start = {start}, end = {end or 'fin'}")
|
def action(self):
|
||||||
woocommerce_manager.process_file_from_to(start, end)
|
logger.info(f"ImportOdsCtl:action({self.args}) - begin")
|
||||||
except ValueError:
|
|
||||||
print("❌ Mauvais format pour --product-range. Utilisez par exemple --product-range=1:40")
|
if self.args.media:
|
||||||
else:
|
self.import_medias_ods()
|
||||||
start, end = 0, None
|
|
||||||
print("ℹ️ --product activé, mais aucune plage spécifiée.")
|
if self.args.category:
|
||||||
|
medias = self.wmc.mm.get_all_as_slug_dict()
|
||||||
|
self.wmc.cm.medias = medias
|
||||||
|
regex = self.args.category_regex if self.args.category_regex else None
|
||||||
|
self.wcm.cm.update_data_categories(regex)
|
||||||
|
|
||||||
|
if self.args.attribute:
|
||||||
|
regex = self.args.attribute_regex if self.args.attribute_regex else None
|
||||||
|
self.wcm.am.create(regex)
|
||||||
|
self.wcm.am.configure_term()
|
||||||
|
|
||||||
|
if self.args.product:
|
||||||
|
self.import_products_ods()
|
||||||
|
|
||||||
|
if self.args.logo:
|
||||||
|
self.wcm.mm.assign_image_logo()
|
||||||
|
|
||||||
|
|
||||||
|
def import_medias_ods(self):
|
||||||
|
if self.args.media:
|
||||||
|
if self.args.media_regex:
|
||||||
|
try:
|
||||||
|
self.wcm.mm.upload_media(self.args.media_regex)
|
||||||
|
except ValueError:
|
||||||
|
logger.error("error name product_regex")
|
||||||
|
elif self.args.media_range:
|
||||||
|
try:
|
||||||
|
parts = self.args.media_range.split(':')
|
||||||
|
start = int(parts[0]) -1 if parts[0] else 0
|
||||||
|
end = int(parts[1]) if len(parts) > 1 and parts[1] else None
|
||||||
|
logger.debug(f"start = {start}, end = {end or 'fin'}")
|
||||||
|
self.wcm.mm.upload_media_from_to(start, end)
|
||||||
|
except ValueError:
|
||||||
|
print("❌ Mauvais format pour --media-range. Utilisez par exemple --media-range=1:40")
|
||||||
|
else:
|
||||||
|
start, end = 0, None
|
||||||
|
print("ℹ️ --media activé, mais aucune plage spécifiée.")
|
||||||
|
|
||||||
|
def import_products_ods(self):
|
||||||
|
if self.args.product:
|
||||||
|
if self.args.product_regex:
|
||||||
|
try:
|
||||||
|
self.wcm.process_file(self.args.product_regex)
|
||||||
|
except ValueError:
|
||||||
|
print("error name product_regex")
|
||||||
|
elif self.args.product_range:
|
||||||
|
try:
|
||||||
|
parts = self.args.product_range.split(':')
|
||||||
|
start = int(parts[0]) -1 if parts[0] else 0
|
||||||
|
end = int(parts[1]) if len(parts) > 1 and parts[1] else None
|
||||||
|
print(f"start = {start}, end = {end or 'fin'}")
|
||||||
|
self.wcm.process_file_from_to(start, end)
|
||||||
|
except ValueError:
|
||||||
|
print("❌ Mauvais format pour --product-range. Utilisez par exemple --product-range=1:40")
|
||||||
|
else:
|
||||||
|
start, end = 0, None
|
||||||
|
print("ℹ️ --product activé, mais aucune plage spécifiée.")
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
ath = AuthentificationWpApi()
|
|
||||||
|
|
||||||
|
# initialize logging
|
||||||
|
log_directory = "logs"
|
||||||
|
|
||||||
#ctx = ssl.create_default_context()
|
# 🔧 Configuration du handler avec rotation quotidienne
|
||||||
#ctx.set_ciphers('@SECLEVEL=2:ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES:DHE+AES:AESGCM:!aNULL:!eNULL:!aDSS:!SHA1:!AESCCM:!PSK')
|
log_file = os.path.join(log_directory, "woocommerce.log")
|
||||||
#http = urllib3.PoolManager(ssl_context=ctx)
|
handler = TimedRotatingFileHandler(
|
||||||
#credentials = b64encode(f"{args.wc_key}:{args.wc_secret}").decode("utf-8")
|
filename=log_file,
|
||||||
|
when="midnight", # ⏰ Rotation tous les jours à minuit
|
||||||
|
interval=1, # 📅 Chaque 1 jour
|
||||||
|
backupCount=7, # ♻️ Garde les 7 derniers fichiers de log
|
||||||
|
encoding='utf-8' # 🧾 Pour supporter tous les caractères
|
||||||
|
)
|
||||||
|
|
||||||
|
# 📋 Format du log
|
||||||
|
formatter = logging.Formatter(
|
||||||
|
fmt="%(asctime)s - %(levelname)s - %(message)s",
|
||||||
|
datefmt="%Y-%m-%d %H:%M:%S"
|
||||||
|
)
|
||||||
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
# 🔌 Récupère le logger
|
||||||
|
logger.setLevel(logging.DEBUG) # 👁 Niveau minimum à capturer
|
||||||
|
logger.addHandler(handler)
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog='wcctl', description='WooCommerce CLI controller')
|
parser = argparse.ArgumentParser(prog='wcctl', description='WooCommerce CLI controller')
|
||||||
|
|
||||||
# 🌐 Options globales
|
# 🌐 Options globales
|
||||||
@ -70,6 +161,10 @@ def main():
|
|||||||
# 🧱 Sous-commandes
|
# 🧱 Sous-commandes
|
||||||
subparsers = parser.add_subparsers(dest='command', required=True)
|
subparsers = parser.add_subparsers(dest='command', required=True)
|
||||||
|
|
||||||
|
delete_parser = subparsers.add_parser('delete', help='delete entities')
|
||||||
|
delete_parser.add_argument('--attributes', action="store_true", required=False, default=False, help='delete all attributes')
|
||||||
|
delete_parser.add_argument('--all', action='store_true', required=False, default=False, help='Delete media, categories, products, attributes, tabs')
|
||||||
|
|
||||||
# 📥 Commande : import-ods
|
# 📥 Commande : import-ods
|
||||||
import_parser = subparsers.add_parser('import-ods', help='Import ODS file data')
|
import_parser = subparsers.add_parser('import-ods', help='Import ODS file data')
|
||||||
import_parser.add_argument('--ods-path', required=True, help='Path to the ODS file')
|
import_parser.add_argument('--ods-path', required=True, help='Path to the ODS file')
|
||||||
@ -95,11 +190,7 @@ def main():
|
|||||||
# product
|
# product
|
||||||
import_parser.add_argument('--product', action='store_true', help='import all products')
|
import_parser.add_argument('--product', action='store_true', help='import all products')
|
||||||
import_parser.add_argument('--product-regex', type=str, help='Regex to filter and import product by name')
|
import_parser.add_argument('--product-regex', type=str, help='Regex to filter and import product by name')
|
||||||
import_parser.add_argument('--product-range', type=str, help='Range of product rows to process (e.g., 10:30)')
|
import_parser.add_argument('--product-range', type=str, help='Range of product rows to process (e.g., 10:30)')
|
||||||
|
|
||||||
|
|
||||||
# delete all informations
|
|
||||||
import_parser.add_argument('--delete-all', action='store_true', help='Delete media, categories, products, attributes, tabs')
|
|
||||||
|
|
||||||
# 📥 Commande : import-ods
|
# 📥 Commande : import-ods
|
||||||
checkssl_parser = subparsers.add_parser('checkssl', help='Check ssl connectivity')
|
checkssl_parser = subparsers.add_parser('checkssl', help='Check ssl connectivity')
|
||||||
@ -109,29 +200,13 @@ def main():
|
|||||||
|
|
||||||
# Analyse des arguments
|
# Analyse des arguments
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
"""wcapi = WoocommerceApi(
|
|
||||||
url=args.wc_url,
|
|
||||||
consumer_key=args.wc_key,
|
|
||||||
consumer_secret=args.wc_secret,
|
|
||||||
wp_api=True,
|
|
||||||
version="wc/v3",
|
|
||||||
verify_ssl=False,
|
|
||||||
timeout=30
|
|
||||||
)"""
|
|
||||||
|
|
||||||
wcapi = WoocommerceApiClient(
|
if args.command == "delete":
|
||||||
url=args.wc_url,
|
sys.exit(DeleteWcCtl(args).action())
|
||||||
wc_consumer_key=args.wc_key,
|
|
||||||
wc_consumer_secret=args.wc_secret,
|
if args.command == "import-ods":
|
||||||
wp_application_user="admin_lcdm",
|
sys.exit(ImportOdsCtl(args).action())
|
||||||
wp_application_password= "Do4p tLYF 5uje PF2M 21Zo x3OR", #"eAB4 49W6 ZRBj zIZc fH62 tv5c", #"yTW8 Mc6J FUCN tPSq bnuJ 0Sdw", #
|
|
||||||
wp_api=True,
|
|
||||||
version="wc/v3",
|
|
||||||
verify_ssl=True,
|
|
||||||
timeout=30
|
|
||||||
)
|
|
||||||
|
|
||||||
if args.command == "check":
|
if args.command == "check":
|
||||||
#wcctl-api-rw
|
#wcctl-api-rw
|
||||||
#class WoocommerceApiClient(API) def __init__(self, url, wc_consumer_key, wc_consumer_secret, wp_application_user, wp_application_password, verify_ssl=False, **kwargs):
|
#class WoocommerceApiClient(API) def __init__(self, url, wc_consumer_key, wc_consumer_secret, wp_application_user, wp_application_password, verify_ssl=False, **kwargs):
|
||||||
@ -146,20 +221,6 @@ def main():
|
|||||||
verify_ssl=True,
|
verify_ssl=True,
|
||||||
timeout=30
|
timeout=30
|
||||||
)
|
)
|
||||||
print("recupération d'un produit", end="")
|
|
||||||
response = wcapi.get("products", params={"per_page": 1, "page": 1})
|
|
||||||
print(response)
|
|
||||||
print("OK")
|
|
||||||
|
|
||||||
"""wcapi = WoocommerceApi(
|
|
||||||
url=args.wc_url,
|
|
||||||
consumer_key="wcctl-api-rw",
|
|
||||||
consumer_secret="NUrp R7tI oDKr FSqT hhf8 KxOT",
|
|
||||||
wp_api=True,
|
|
||||||
version="wp/v2",
|
|
||||||
verify_ssl=False,
|
|
||||||
timeout=30
|
|
||||||
)"""
|
|
||||||
print("recupération d'un media", end="")
|
print("recupération d'un media", end="")
|
||||||
response = wcapi.get("media", params={"per_page": 100, "page": 1})
|
response = wcapi.get("media", params={"per_page": 100, "page": 1})
|
||||||
print(response)
|
print(response)
|
||||||
@ -173,53 +234,6 @@ def main():
|
|||||||
print(f'connection OK')
|
print(f'connection OK')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
ath = AuthentificationWpApi()
|
|
||||||
media_manager = MediaManager(ath, wcapi, filename_ods=args.ods_path)
|
|
||||||
category_manager = CategoryManager(wcapi, ath, filename_ods=args.ods_path)
|
|
||||||
product_manager = ProductManager(wcapi, ath, filename_ods=args.ods_path)
|
|
||||||
attribute_manager = AttributeManager(wcapi, filename_ods=args.ods_path)
|
|
||||||
tab_manager = TabManager(wcapi, filename_ods=args.ods_path)
|
|
||||||
variation_manager = VariationsManager(wcapi, filename_ods=args.ods_path)
|
|
||||||
woocommerce_manager = WooCommerceManager(wcapi=wcapi,
|
|
||||||
media_manager=media_manager,
|
|
||||||
category_manager=category_manager,
|
|
||||||
product_manager=product_manager,
|
|
||||||
tab_manager=tab_manager,
|
|
||||||
attribute_manager=attribute_manager,
|
|
||||||
variation_manager=variation_manager,
|
|
||||||
filename_ods=args.ods_path)
|
|
||||||
|
|
||||||
# Dispatch en fonction de la commande
|
|
||||||
#if args.command == 'import-ods':
|
|
||||||
# import_medias_ods(args)
|
|
||||||
#print(f"🔍 args.media = {args.media}")
|
|
||||||
#print(f"🔍 args.media_range = {args.media_range}")
|
|
||||||
print(f"args = {args}")
|
|
||||||
|
|
||||||
if args.media:
|
|
||||||
import_medias_ods(args, media_manager)
|
|
||||||
|
|
||||||
if args.delete_all:
|
|
||||||
woocommerce_manager.delete_all_informations()
|
|
||||||
|
|
||||||
if args.category:
|
|
||||||
medias = media_manager.get_all_as_slug_dict()
|
|
||||||
category_manager.medias = medias
|
|
||||||
regex = args.category_regex if args.category_regex else None
|
|
||||||
category_manager.update_data_categories(regex)
|
|
||||||
|
|
||||||
if args.attribute:
|
|
||||||
regex = args.attribute_regex if args.attribute_regex else None
|
|
||||||
attribute_manager.create(regex)
|
|
||||||
attribute_manager.configure_term()
|
|
||||||
|
|
||||||
if args.product:
|
|
||||||
import_products_ods(args, woocommerce_manager)
|
|
||||||
|
|
||||||
if args.logo:
|
|
||||||
media_manager.assign_image_logo()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user