def main(): #connecting to your woocommerce REST API wcapi = API( url="https://your_wordpress_domain.com", consumer_key="ck_5e55eb1f8b3po06e47f16bdd733980dc9196a7a6",# your WooCommerce API key this one won't work consumer_secret="cs_4aa7999999262fa19224451945ed7f58fa6ac494",# your WooCommerce API secret this one won't work wp_api=True, query_string_auth=True, version="wc/v3" ) # for performance we load all the products in a list products = [] for x in range(4): p = wcapi.get(f"products?per_page=300&offset={300*x}").json() products.extend(p) # We in each product and use the search engine to find if any updates in the prices for x,product in enumerate(products): name = product['name'] price = product['regular_price'] n_name, n_price, score = get_new_name_and_price(name) try: score = int(score) except: score = 0 if n_name == None or score < 13: wcapi.delete(f"products/{product['id']}", params={"force": True}).json() print(f'{x} / {score} - Deleted {name} ~ {n_name}') if score < 13 and n_name != None: data = { "regular_price": str(n_price), "name": n_name, "status": "publish", } wcapi.post(f"products", data).json() print(f'Created new version of {n_name}') else: try: if float(price) != float(n_price): data = { "regular_price": str(n_price), "name": n_name, "status": "publish", } wcapi.put(f"products/{product['id']}", data).json() print(f'{x} / {score} - Updated {name} ~ {price}') print(f'{x} / {score} - Updated {n_name} ~ {n_price}') else: print('Pass') except Exception as e: print(e)
def put_request(path, data): settings = get_woocommerce_settings() wcapi = API(url=settings['woocommerce_url'], consumer_key=settings['api_key'], consumer_secret=settings['api_secret'], verify_ssl=settings['verify_ssl'], wp_api=True, version="wc/v3", timeout=5000) #frappe.log_error("{0} data: {1}".format(path, data)) r = wcapi.put(path, data) #r.raise_for_status() # manually raise for status to get more info from error (message details) if r.status_code != requests.codes.ok: make_woocommerce_log(title="WooCommerce put error {0}".format( r.status_code), status="Error", method="put_request", message="{0}: {1}".format(r.url, r.json()), request_data=data, exception=True) return r.json()
class WP_Interface(): def __init__(self): self.wcapi = API( url="http://127.0.0.1/index.php", consumer_key="ck_d8f143faa5371aa8e40927134b7df279babdaf59", consumer_secret="cs_24fa6f9c569ef3205b4252e904c92601ca759147", wp_api=True, version="wc/v2") # Create thread that reads wp orders every x minutes self.do_scrap_orders = True self.scrap_orders_t = threading.Thread(target=self.scrapping_thread) self.scrap_orders_t.daemon = True self.scrap_orders_t.start() def update_order_status(self, p_order, p_status): data = {"status": p_status} self.wcapi.put("orders/" + str(p_order), data).json() def retrieve_orders(self): orders = self.wcapi.get("orders") return orders.json() def create_orders(self, p_orders_dic): """ @brief: This function retrievec the completed order and returns a list of the job to be done with the description of each board """ for item_order in p_orders_dic: # Check if order is payed if item_order['status'] == 'completed': # Check if already in database if not (dbapi.is_wc_order_in_db(item_order['id'])): # Add to local db dbapi.add_order_to_db(item_order) # Update wp status (not sure yet, only when accepted by machine... maybe) # self.update_order_status(int(item_order['id']), 'processing') def scrapping_thread(self): while self.do_scrap_orders: wp_orders = self.retrieve_orders() self.create_orders(wp_orders) sleep(READING_TIME_INTERVAL) def update_stock(self): pass
class WooAPIUtility(object): def __init__(self): wc_creds = CredentialsUtility.get_wc_api_keys() self.env = os.environ.get('ENV', 'test') self.base_url = WOO_API_HOSTS[self.env] self.wcapi = API(url=self.base_url, consumer_key=wc_creds['wc_key'], consumer_secret=wc_creds['wc_secret'], version="wc/v3") def assert_status_code(self): assert self.status_code == self.expected_status_code, f"Bad Status code." \ f"Expected {self.expected_status_code}, Actual status code: {self.status_code}," \ f"URL: {self.endpoint}, Response Json: {self.rs_json}" def post(self, wc_endpoint, params=None, expected_status_code=200): rs_api = self.wcapi.post(wc_endpoint, data=params) self.status_code = rs_api.status_code self.expected_status_code = expected_status_code self.rs_json = rs_api.json() self.endpoint = wc_endpoint self.assert_status_code() logger.debug(f"POST API response: {self.rs_json}") return self.rs_json def get(self, wc_endpoint, params=None, expected_status_code=200): rs_api = self.wcapi.get(wc_endpoint, params=params) self.status_code = rs_api.status_code self.expected_status_code = expected_status_code self.rs_json = rs_api.json() self.endpoint = wc_endpoint self.assert_status_code() logger.debug(f"GET API response: {self.rs_json}") return self.rs_json def put(self, wc_endpoint, params=None, expected_status_code=200): rs_api = self.wcapi.put(wc_endpoint, data=params) self.status_code = rs_api.status_code self.expected_status_code = expected_status_code self.rs_json = rs_api.json() self.endpoint = wc_endpoint self.assert_status_code() logger.debug(f"PUT API response: {self.rs_json}") return self.rs_json
def put_request(path, data): settings = get_woocommerce_settings() wcapi = API(url=settings['woocommerce_url'], consumer_key=settings['api_key'], consumer_secret=settings['api_secret'], verify_ssl=settings['verify_ssl'], wp_api=True, version="wc/v3", timeout=5000) #frappe.log_error("{0} data: {1}".format(path, str(data))) r = wcapi.put(path, data) r.raise_for_status() return r.json()
def put_request(path, data): settings = get_woocommerce_settings() wcapi = API( url=settings['woocommerce_url'], consumer_key=settings['api_key'], consumer_secret=settings['api_secret'], verify_ssl=settings['verify_ssl'], wp_api=True, version="wc/v2" ) r = wcapi.put(path, data) r.raise_for_status() return r.json()
def put_request(path, data, settings=None): if not settings: settings = get_woocommerce_settings() wcapi = API(url=settings['woocommerce_url'], consumer_key=settings['api_key'], consumer_secret=settings['password'], wp_api=True, version="wc/v3", verify_ssl=True, queryStringAuth=True) r = wcapi.put(path, data) try: r.raise_for_status() except Exception as e: raise e return r.json()
class WooApiHandler: def __init__(self): self.wcapi = API( # WC Office url=cred.url, consumer_key=cred.consumer_key, consumer_secret=cred.consumer_secret, version=cred.version, timeout=cred.timeout) self.products_uploaded = 0 self.products_updated = 0 self.PRODUCT_UPLOAD_BATCH_SIZE = 30 self.PRODUCT_DOWNLOAD_BATCH_SIZE = 100 def get_sub_categories(self, parent_id): """CHANGE WOO PAGE""" params = {"per_page": "100", "parent": parent_id} get_sub_categories_response = self.wcapi.get("products/categories", params=params).json() remote_sub_categories = [] count = 0 for item in get_sub_categories_response: remote_sub_categories.append(self.create_remote_category(item)) count += 1 return remote_sub_categories def create_remote_category(self, item): #print(json.dumps(item, indent=4, sort_keys=True)) category = Category(html.unescape(item['name']), item['id']) category.parent_remote_id = item['parent'] if item['image']: category.image_id = item['image']['id'] else: category.image_id = None #category.print_category(1) return category def get_products(self): remote_products = [] count = 0 page = 1 while True: params = { "per_page": self.PRODUCT_DOWNLOAD_BATCH_SIZE, "page": page } get_products_response = self.wcapi.get("products", params=params).json() #TEST PRINT #print(json.dumps(get_products_response, indent=4, sort_keys=True)) for woo_product_item in get_products_response: remote_products.append( self.create_remote_product(woo_product_item)) count += 1 page += 1 print("DOWLOADED " + str(count) + " PRODUCTS") if len(get_products_response) < self.PRODUCT_DOWNLOAD_BATCH_SIZE: break print(str(count) + " TOTAL PRODUCTS ALREADY ON SITE") return remote_products def create_remote_product(self, woo_product_item): categories = [] for category in woo_product_item['categories']: categories.append(category['id']) remote_category_id = categories[0] remote_image_ids = [] for remote_image in woo_product_item['images']: remote_image_ids.append(remote_image['id']) product_out_price = float(woo_product_item['regular_price']) product_out_price = "{0:.2f}".format(product_out_price) remoteProduct = RemoteProduct( supplier_product_id=woo_product_item['sku'], remote_product_id=woo_product_item['id'], remote_category_id=remote_category_id, product_name=html.unescape(woo_product_item['name']), product_description=html.unescape(woo_product_item['description']), remote_image_ids=remote_image_ids, product_out_price=product_out_price) if woo_product_item['sale_price'] != "": product_discount_price = float(woo_product_item['sale_price']) product_discount_price = "{0:.2f}".format(product_discount_price) remoteProduct.product_discount_price = product_discount_price else: remoteProduct.product_discount_price = "0.0" return remoteProduct def add_category(self, name, pId): data = {"name": name, "display": "default", "parent": pId} response = self.wcapi.post("products/categories", data).json() # print(json.dumps(response, indent=4, sort_keys=True)) if "id" in response.keys(): print(" Uploaded: " + name + " ---> " + str(response['id'])) return response['id'] else: print("Not Uploaded") return "" def upload_products(self, products, update): """ When batch upload fails try to upload single products Raise batch size to 100 """ if update: self.print_title("updating products") else: self.print_title("uploading products") uploaded_products = [] while products: product_batch = [] for i in range(self.PRODUCT_UPLOAD_BATCH_SIZE): if products: product_batch.append(products.pop()) else: break data = {} create = [] for product in product_batch: if not product.supplier_product_id == "148902": if update: create.append(self.create_woo_update_product(product)) else: create.append(self.create_woo_product(product)) if update: data['update'] = create else: data['create'] = create #try: batch_upload_response = self.wcapi.post("products/batch", data).json() if "create" or "update" in batch_upload_response.keys(): uploaded_products.extend( self.create_remote_products(batch_upload_response)) else: print( json.dumps(batch_upload_response, indent=4, sort_keys=True)) #except: #print("An exception occurred") return uploaded_products def create_remote_products(self, batch_upload_response): #print(json.dumps(batch_upload_response, indent=4, sort_keys=True)) #print(batch_upload_response['create']) uploaded_products = [] if "create" in batch_upload_response.keys(): woo_products = batch_upload_response['create'] for woo_product in woo_products: uploaded_products.append( self.create_remote_product(woo_product)) self.products_uploaded += 1 #for uploaded_product in uploaded_products: # uploaded_product.print_remote_product() print(str(self.products_uploaded) + " TOTAL PRODUCTS UPLOADED") if "update" in batch_upload_response.keys(): woo_products = batch_upload_response['update'] for woo_product in woo_products: uploaded_products.append( self.create_remote_product(woo_product)) self.products_updated += 1 # for uploaded_product in uploaded_products: # uploaded_product.print_remote_product() print(str(self.products_updated) + " TOTAL PRODUCTS UPDATED") return uploaded_products def create_woo_update_product(self, product): woo_product = {} woo_product['id'] = product.remote_product_id if product.product_name: woo_product['name'] = product.product_name if product.product_description: woo_product['description'] = product.product_description woo_product['short_description'] = product.product_description woo_product['regular_price'] = product.product_out_price if float(product.product_discount_price) != 0.0: woo_product['sale_price'] = product.product_discount_price else: woo_product['sale_price'] = "" return woo_product def create_woo_product(self, product): woo_product = {} cat_id = product.remote_category_id # print(catId) woo_product['sku'] = product.supplier_product_id woo_product['categories'] = [{"id": cat_id}] woo_product['name'] = product.product_name woo_product['type'] = "simple" woo_product['regular_price'] = product.product_out_price if float(product.product_discount_price) != 0.0: woo_product['sale_price'] = product.product_discount_price else: woo_product['sale_price'] = "" woo_product['description'] = product.product_description woo_product['short_description'] = product.product_description woo_product['attributes'] = product.product_specification woo_product['images'] = product.product_images return woo_product def update_category_image(self, category, img_ref): data = {"image": {"id": img_ref}} request = "products/categories/" + str(category.remote_id) update_category_image_response = self.wcapi.put(request, data).json() #print(json.dumps(update_category_image_response, indent=4, sort_keys=True)) def update_category_display(self, category, level): if level == 1 or level == 2: data = {"display": "subcategories"} print(category.name + " ---> subcategories") else: data = {"display": "default"} print(category.name + " ---> default") request = "products/categories/" + str(category.remote_id) update_category_display_response = self.wcapi.put(request, data).json() def force_delete_all_products(self): params = {"per_page": "100"} response_get = self.wcapi.get("products", params=params).json() products_to_delete = [d['id'] for d in response_get] params = {"force": "True"} for p in products_to_delete: print(p) response_delete = self.wcapi.delete("products/" + str(p), params=params).json() def delete_all_products(self): count = 0 while True: params = {"per_page": "100"} response_get = self.wcapi.get("products", params=params).json() products_to_delete = [d['id'] for d in response_get] data = {'delete': products_to_delete} response_delete = self.wcapi.post("products/batch", data).json() count += len(products_to_delete) if len(products_to_delete) < 100: break else: print("Deleted products: " + str(count)) print("") print("Deleted products: " + str(count)) def delete_all_categories(self): count = 0 while True: params = {"per_page": "100"} response_get = self.wcapi.get("products/categories", params=params).json() categories_to_delete = [d['id'] for d in response_get] data = {'delete': categories_to_delete} response_delete = self.wcapi.post("products/categories/batch", data).json() count += len(categories_to_delete) if len(categories_to_delete) < 100: break else: print("Deleted categories: " + str(count)) print("") print("Deleted categories: " + str(count)) def print_title(self, title): print("") print("----------") print("") print(title.upper() + ":") print("")
class Bridge: categorie_ids = [] def __init__(self): self.wcapi = API(url=os.environ.get("wc_endpoint", None), consumer_key=os.environ.get("wc_consumer_key", None), consumer_secret=os.environ.get( "wc_consumer_secret", None), wp_api=True, verify_ssl=False, version="wc/v2", timeout=30) def category_tree_manage(self, cats, parent=0): category_id = parent for cat in cats: response = self.wcapi.get("products/categories?search=%s" % cat["name"]) results = response.json() if len(results) > 0: for result in results: self.categorie_ids.append({"id": result["id"]}) category_id = result["id"] else: response = self.wcapi.post("products/categories", { "name": cat["name"], "parent": parent, "image": {} }) result = response.json() category_id = result["id"] self.categorie_ids.append({"id": category_id}) if "children" in cat and len(cat["children"]) > 0: self.category_tree_manage(cat["children"], category_id) def insert(self, data): github = Github() github.getRelease(data) self.category_tree_manage(github.release["categories"]) response = self.wcapi.get("products?sku=%s-%s" % (github.release["repository"]["name"], github.release["repository"]["id"])) result = response.json() if (len(result) == 0): response = self.wcapi.post( "products", { "name": github.release["repository"]["name"], "type": "simple", "regular_price": "0.0", "virtual": True, "downloadable": True, "sku": "%s-%s" % (github.release["repository"]["name"], github.release["repository"]["id"]), "sold_individually": True, "description": github.release["readme"], "short_description": github.release["repository"]["description"], "categories": self.categorie_ids }) return response.json() else: response = self.wcapi.put( "products/%s" % result[0]["id"], { "name": github.release["repository"]["name"], "description": github.release["readme"], "short_description": github.release["repository"]["description"], "categories": self.categorie_ids }) return response.json()
from airtable import airtable #https://github.com/josephbestjames/airtable.py from woocommerce import API #https://github.com/woocommerce/wc-api-python from datetime import datetime, timezone import dateutil.parser ### APIs API_KEY = 'keyO4pjsHsgtCPofA' BASE_ID = 'app1fFJJYvoUtSQUL' TABLE_NAME = 'Inventory' wcapi = API(url="https://marchalocal.ch", consumer_key='ck_7e032301f2b9c73f376bf3f608b7274ca27ce020', consumer_secret="cs_6ffda704beefd69e0f771b8bfb01297a53101bf8", wp_api=True, version="wc/v3", timeout=15) at = airtable.Airtable(BASE_ID, API_KEY) table = at.get(TABLE_NAME, filter_by_formula=None) for record in table["records"]: ## Not functional yet... : ( try: stock = record["fields"]["Stock"] wc_id = record["fields"]["woocommerce_ID"] data = {"stock_quantity": stock} wcapi.put("products/{}".format(wc_id), data).json() except: print("Item {} not in online store...".format( record["fields"]["Product"]))
class start(QDialog): def __init__(self): QDialog.__init__(self) self.wcapi = API( url="http://presetsmentor.com", consumer_key="ck_73006715ef68ce4ddf79d156419291e53b27d2ee", consumer_secret="cs_c672c6b24e0f6bc33ba676918f51fda163aafecd", timeout=30, ) if hasattr(sys, '_MEIPASS'): palette = QPalette() palette.setBrush( QPalette.Background, QBrush(QPixmap(os.path.join(sys._MEIPASS, "Installer.png")))) self.setAutoFillBackground(True) self.setPalette(palette) self._filenewIcon = QIcon( QPixmap(os.path.join(sys._MEIPASS, "pmlogo.jpg"))) css_path = os.path.join(sys._MEIPASS, "style.css") with open(css_path, "r") as fh: self.setStyleSheet(fh.read()) else: self._filenewIcon = QIcon(QPixmap("pmlogo.jpg")) palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap("Installer.png"))) self.setAutoFillBackground(False) self.setPalette(palette) sshFile = "style.css" sub = "resource" with open(sshFile, "r") as fh: self.setStyleSheet(fh.read()) self.setWindowTitle("Presets Mentor Installer") self.setWindowIcon(self._filenewIcon) self.layout = QGridLayout() self.text = QLabel() QDialog.setFixedSize(self, 800, 600) self.thread_count = threading.active_count() self.threads = [] self.msg_queue = Queue() self.settext() self.button2 = QPushButton("submit") self.group_box = QGroupBox() moods = [ QCheckBox("Canon"), QCheckBox("Nikon"), QCheckBox("Sony"), QCheckBox("Other") ] #self.group_box.setCheckable(True) self.chk_vlayout = QVBoxLayout() self.mood_button_group = [] for i in xrange(len(moods)): # Add each radio button to the button layout self.chk_vlayout.addWidget(moods[i]) self.mood_button_group.append(moods[i]) # Add each radio button to the button group & give it an ID of i #self.mood_button_group.addButton(moods[i]) self.button2.setFixedWidth(180) #self.chk_vlayout.addWidget(self.button2) self.group_box.setLayout(self.chk_vlayout) self.group_box.setFixedWidth(200) self.group_box.setStyleSheet( "border:0; padding-left:10px; font-size:18px;") self.layout.setAlignment(self.group_box, Qt.AlignRight) self.setLayout(self.layout) self.progress = QProgressBar(self) self.progress.setGeometry(260, 385, 280, 20) self.progress.hide() self.setFocus() def setback(self, img): if hasattr(sys, '_MEIPASS'): palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap(os.path.join(sys._MEIPASS, img)))) self.setAutoFillBackground(True) self.setPalette(palette) else: palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap(img))) self.setAutoFillBackground(False) self.setPalette(palette) def settext(self): self.button = QPushButton("Next") self.button.setMaximumSize(180, 180) self.button.setMinimumSize(150, 30) self.layout.addWidget(self.button, 0, 1) self.setContentsMargins(100, 100, 100, 100) self.button.clicked.connect(self.setOrderId) self.layout.setAlignment(self.button, Qt.AlignBottom) def setOrderId(self): self.setback("Installer2.png") self.button.hide() #self.btn1=QGroupBox() #self.btnc=QVBoxLayout() self.line_edit = QLineEdit() self.line_edit.setFixedHeight(30) self.line_edit.setFixedWidth(180) self.button1 = QPushButton("submit") self.line_edit.setFocus() self.button1.clicked.connect(self.get_order_id) self.button1.setFixedHeight(30) self.layout.addWidget(self.line_edit, 1, 1) self.layout.addWidget(self.button1, 2, 1) self.setContentsMargins(100, 340, 100, 180) def get_order_id(self): self.order_id = self.line_edit.text() try: j = (self.wcapi.get("orders/" + self.order_id + "?filter[meta]=true").json()) if j[u'order'][u'status'] == "completed": #if d < 3 : d = j[u'order'][u'order_meta'][u'number'] self.x = int(float(d)) if self.x < 3: self.line_edit.hide() self.button1.hide() #self.label.setText("") self.setback("Installer3.png") self.layout.addWidget(self.group_box, 1, 0) self.button2.setFixedWidth(150) self.button2.setFixedHeight(30) self.layout.addWidget(self.button2, 1, 1) self.setContentsMargins(200, 400, 350, 50) self.layout.setAlignment(self.button2, Qt.AlignBottom) self.button2.clicked.connect(self.setcamType) else: self.setback("Installer6.png") self.line_edit.hide() self.button1.hide() self.text.setText( "<a href='mailto:[email protected]'>[email protected]</a>" ) self.text.setOpenExternalLinks(True) self.layout.addWidget(self.text, 1, 1) self.setContentsMargins(100, 450, 100, 70) self.button1.setFixedWidth(180) self.layout.setAlignment(self.button1, Qt.AlignJustify) self.layout.setAlignment(self.line_edit, Qt.AlignJustify) self.text.setStyleSheet( "font-size:35px;margin-right: 80px;font-family: Open sans-serif;" ) except: self.setback("Installer6.png") self.line_edit.hide() self.button1.hide() self.text.setText( "<a href='mailto:[email protected]'>[email protected]</a>" ) self.text.setOpenExternalLinks(True) self.layout.addWidget(self.text, 1, 1) self.setContentsMargins(100, 450, 100, 70) self.button1.setFixedWidth(180) self.layout.setAlignment(self.button1, Qt.AlignJustify) self.layout.setAlignment(self.line_edit, Qt.AlignJustify) self.text.setStyleSheet( "font-size:35px;margin-right: 80px;font-family: Open sans-serif;" ) def setcamType(self): self.group_box.hide() self.button2.hide() self.text.hide() self.setback("Installer4.png") came = [] for i in range(0, 4): if self.mood_button_group[i].isChecked(): typecam = str(self.mood_button_group[i].text()) came.append(typecam) if len(came) > 1: self.setback("Installer4m.png") self.http_call(came) self.last_scren() def http_call(self, cam): self.progress.show() input_file = "http://psdev.pskiss.com/wp-content/installer/installer_path.json" f = requests.get(input_file, auth=('test', 'test1234')) f.close() text = f.text obj = [] tj = list(json.loads(text)) self.username = getpass.getuser() for c in cam: c = c + ".zip" tj.append({ 'url': 'http://psdev.pskiss.com//wp-content/installer/CameraRaw/CameraProfiles/' + c, 'file': c, 'path': "AppData\\Roaming\\Adobe\\CameraRaw\\CameraProfiles" }) for url in tj[::-1]: #thread_count = threading.active_count() clean_url = url[u'url'] file_path = self.username + "\\" + url[u'path'] short_url = '/'.join(clean_url.split('/')[:-1]) local_name = url[u'file'] self.download(clean_url, local_name, file_path) self.progress.reset() self.progress.deleteLater() def download(self, url, local_name, file_path): self.completed = 0 self.progress.setValue(0) self.d = downloader self.d.thread(target=downloader.ondownload(), args=(self.d, ), kwargs={ 'url': url, 'local_name': local_name }) self.d.start() self.d.updateProgress.connect(self.setProgress) #w=Worker(Queue) #work=Thread(target=Worker.zip,args=(w,),kwargs={'file':local_name,'file_path':file_path}, name='zip') #work.setDaemon(True) #work.start() def setProgress(self, progress): self.progress.setValue(progress) def last_scren(self): self.x = self.x + 1 data = {"order": {"order_meta": {"number": self.x}}} self.wcapi.put("orders/" + self.order_id + "?filter[meta]=true", data).json() #self.group_box.hide() #self.button2.hide() self.progress.hide() self.setback("Installer5.png") self.text5 = QLabel( '<a href="https://www.youtube.com/watch?v=sLV8cqTGECE">Click Here To Watch our Tutorial</a>' ) self.text5.setOpenExternalLinks(True) self.layout.addWidget(self.text5) self.setContentsMargins(30, 400, 100, 80) self.layout.setAlignment(self.text5, Qt.AlignCenter)
from woocommerce import API wcapi = API(url="https://splitovik.ru", consumer_key="ck_b386c34cc21c495c288913d37a8ecde73ffcffaa", consumer_secret="cs_fba9bb4212f793e19e4dfca6f6bc65ca750bb293", wp_api=True, version="wc/v3") #print(wcapi.get("products").json()) #print() price = '333' #id_prod = 2126 id_prod = 'X4734166' str_id_prod = 'products/' + str(id_prod) print(str_id_prod) data = {"regular_price": price} print(wcapi.get(str_id_prod).json()) print('111') wcapi.put(str_id_prod, data) print(wcapi.get("products/2126").json())
class WooCommerceDataAccess: """ Class for integrating WooCommerce. Please refer to documentations. https://woocommerce.github.io/woocommerce-rest-api-docs/ """ def __init__(self, url, consumer_key, consumer_secret): self.wcapi = API( url=url, consumer_key=consumer_key, consumer_secret=consumer_secret, version="wc/v3", timeout=10 ) def get_all_products(self): """This API helps you to view all the products.""" page = 1 output = [] while True: try: products = self.wcapi.get("products", params={'per_page': 10, 'page': page}).json() page += 1 if len(products) == 0: break for product in products: if product["stock_quantity"] is None: product["stock_quantity"] = 0 output.append(product) except Exception: # db.create_logs("WooCommerce", "Error", f"Products timeout error, retrying in {RETRY_DELAY}s...") print(f"[WOOCOMMERCE] Timeout error, retrying in {RETRY_DELAY}s...") time.sleep(RETRY_DELAY) return output def get_product(self, id): """This API lets you retrieve and view a specific product by ID.""" try: return self.wcapi.get(f"products/{id}").json() except Exception as e: raise e def update_product(self, id, data): """This API lets you make changes to a product.""" try: self.wcapi.put(f"products/{id}", data).json() except Exception as e: raise e def delete_product(self, id): """This API helps you delete a product.""" try: self.wcapi.delete(f"products/{id}", params={"force": True}) except Exception as e: raise e def get_all_variations(self, product_id): """This API helps you to view all the product variations.""" page = 1 output = [] while True: try: variations = self.wcapi.get(f"products/{product_id}/variations", params={'per_page': 10, 'page': page}).json() page += 1 if len(variations) == 0: break for variation in variations: if variation["stock_quantity"] is None: variation["stock_quantity"] = 0 output.append(variation) except Exception: #output.clear() #db.create_logs("WooCommerce", "Error", f"Variations timeout error, retrying in {RETRY_DELAY}s...") print(f"[WOOCOMMERCE] Timeout error, retrying in {RETRY_DELAY}s...") time.sleep(RETRY_DELAY) return output def get_variation(self, product_id, id): """This API lets you retrieve and view a specific product variation by ID.""" try: return self.wcapi.get(f"products/{product_id}/variations/{id}").json() except Exception as e: raise e def update_variation(self, product_id, id, data): """This API lets you make changes to a product variation.""" try: self.wcapi.put(f"products/{product_id}/variations/{id}", data) except Exception as e: raise e def delete_variation(self, product_id, id): """This API helps you delete a product variation.""" try: self.wcapi.delete(f"products/{product_id}/variations/{id}", params={"force": True}) except Exception as e: raise e def get_all_orders(self, status): """This API helps you to view all the orders.""" page = 1 output = [] while True: try: orders = self.wcapi.get("orders", params={'per_page': 10, 'page': page, 'status': [status]}).json() page += 1 if len(orders) == 0: break for order in orders: output.append(order) except Exception: #output.clear() print(f"[WOOCOMMERCE] Timeout error, retrying in {RETRY_DELAY}s...") time.sleep(RETRY_DELAY) # Fetch english order (Wordpress plugin WPML) page = 1 while True: try: orders = self.wcapi.get("orders", params={'per_page': 10, 'page': page, 'status': [status], 'lang': 'en'}).json() page += 1 if len(orders) == 0: break for order in orders: output.append(order) except Exception: #output.clear() print(f"[WOOCOMMERCE] Timeout error, retrying in {RETRY_DELAY}s...") time.sleep(RETRY_DELAY) return output def get_order(self, id): """This API lets you retrieve and view a specific order.""" try: return self.wcapi.get(f"orders/{id}").json() except Exception as e: print(e) def update_order(self, id, data): """This API lets you make changes to an order.""" try: self.wcapi.put(f"orders/{id}", data) except Exception as e: raise e def delete_order(self, id): """This API helps you delete an order.""" try: self.wcapi.delete(f"orders/{id}", params={"force": True}).json() except Exception as e: raise e def get_all_customers(self): """This API helps you to view all the customers.""" try: return self.wcapi.get(f"customers").json() except Exception as e: raise e def get_customer(self, id): """This API lets you retrieve and view a specific customer by ID.""" try: return self.wcapi.get(f"customers/{id}").json() except Exception as e: raise e
def sync_stock_product(self): url=self.env["enotif_woo.keys"].search([],limit=1).woocommerce_url key=self.env["enotif_woo.keys"].search([],limit=1).woocommerce_api_key secret=self.env["enotif_woo.keys"].search([],limit=1).woocommerce_api_secret wcapi = API( url=url, consumer_key=key, consumer_secret=secret, #wp_api=True, version="wc/v3" ) #print(wcapi.get("products").json()) #stock_quantity integer Stock quantity. #stock_status # string Controls the stock status of the product. Options: # instock, # outofstock, # onbackorder. # # Default is instock. productos_woo = wcapi.get('products', params={'per_page': 100,'page':1}).json() productos_woo+=wcapi.get('products', params={'per_page': 100,'page':2}).json() for pw in productos_woo: sku=pw['sku'] id=pw['id'] name=pw['name'] descripton_sale=pw['description'] list_price=pw['price'] type="product" productos_all=self.env['product.template'].search([('default_code', '=', sku)],limit=1) if productos_all: stock=productos_all.qty_available precio=productos_all.list_price if productos_all.qty_available<=0: stock_status="outofstock" else: stock_status="instock" data = { 'stock_quantity': stock, 'stock_status': stock_status, 'price':precio } #wcapi.put("products/attributes/"+str(id), data).json() wcapi.put("products/"+str(id), data).json() values = { "name": name, "lst_price": list_price, "descripton_sale":descripton_sale, } self.write(values) else: values = { "default_code": sku, "name": name, "lst_price": list_price, "descripton_sale":descripton_sale, "type":type, "available_in_pos":True, } self.create(values)
res = wcapi.post('products', data).json() product_id = res['id'] product = wcapi.get('products/%s' % product_id).json() print 'ID %s - SKU %s - Name %s' % ( product['id'], product['sku'], product['name'], ) # ----------------------------------------------------------------------------- # Product update: # ----------------------------------------------------------------------------- data = {"regular_price": "24.54"} print(wcapi.put("products/%s" % product_id, data).json()) # ----------------------------------------------------------------------------- # Product list: # ----------------------------------------------------------------------------- #print(wcapi.get("products").json()) for product in wcapi.get("products").json(): print 'ID %s - SKU %s - Name %s' % ( product['id'], product['sku'], product['name'], ) # ----------------------------------------------------------------------------- # Product delete: # -----------------------------------------------------------------------------
class WooCommerceAPI(BasePlatformAPI): persistent_identifier = "woo-commerce" webhook_enabled = True def __init__(self, **kwargs): super().__init__(**kwargs) self.lastOrderCheck = datetime.datetime.now() self.latestChanges = list() self.oneTimeBlacklist = list() self.APIClient = API( url="https://dottodotstudio.co.uk", consumer_key="ck_d9b41a1b849878a05becd4819de3679143561e4f", consumer_secret="cs_2d07f8d355855145d0afb30fae628aad6ba5f0a9", version="wc/v3", query_string_auth=True) # Stock Count & Consistency Check Handlers ########################################### def getAllStockCounts(self): wpProductDataList = self._bulkStockCount() stdCountList = list() count = 1 total = len(wpProductDataList) productDBLogger.info( "WcAPI Converting/Parsing Simple Product Stock Counts...") for wpRawProductData in wpProductDataList: stdCount = self._convertProductDataIntoStockCount(wpRawProductData) if stdCount is not None: stdCountList.append(stdCount) else: pass count = count + 1 return stdCountList def getProductStockCount(self, productObject: Product): productSKU = productObject.sku responseObject = self.APIClient.get("products", params={"sku": productSKU}) if responseObject.status_code == 200: responseJSON = responseObject.json() if len(responseJSON) > 0: productJSON = responseJSON[0] stockCount = self._convertProductDataIntoStockCount( productJSON) return stockCount else: print( f"Length of JSON response was below 0... product had sku:{productObject.sku}" ) return None else: productDBLogger.warn( f"WcAPI encountered an error when fetching stock record explicitly for product with SKU: {productSKU}" ) return None def _bulkStockCount(self): bulkStockCountList = list() bulkStockCountList = bulkStockCountList + self._bulkSimpleStockCount( ) + self._bulkVariationStockCount() return bulkStockCountList def _bulkSimpleStockCount(self): productDBLogger.info("WcAPI Getting Simple Product Stock Counts...") page = 1 finishedReading = False rawStockCountList = list() failedAttempts = 0 while (not finishedReading) and (failedAttempts < 4): try: requestResponse = self.APIClient.get("products", params={ "page": page, "per_page": 50, "type": "simple" }) if requestResponse.status_code == 200: failedAttempts = 0 responseJSON = requestResponse.json() rawStockCountList = rawStockCountList + responseJSON totalPages = int( requestResponse.headers["X-WP-TotalPages"]) if page >= totalPages: finishedReading = True else: finishedReading = False page = page + 1 else: print(requestResponse.content) failedAttempts = failedAttempts + 1 finishedReading = False except ReadTimeout: productDBLogger.warn( "WcAPI Bulk Stock Count got a read timeout, retrying!") failedAttempts = failedAttempts + 1 finishedReading = False if failedAttempts == 4: productDBLogger.error( "WpApi BulkStockCount Encountered 4 errors in a row in simple product fetch and was forced to quit early!!!" ) return rawStockCountList def _bulkVariationStockCount(self): productDBLogger.info("WcAPI Fetching Variable Product Listings") page = 1 finishedReading = False variableProductIDList = list() failedAttempts = 0 while (not finishedReading) and (failedAttempts < 4): requestResponse = self.APIClient.get("products", params={ "page": page, "per_page": 50, "type": "variable" }) if requestResponse.status_code == 200: failedAttempts = 0 responseJSON = requestResponse.json() for variableProductJSON in responseJSON: variableProductID = variableProductJSON.get("id") variableProductIDList.append(variableProductID) totalPages = int(requestResponse.headers["X-WP-TotalPages"]) if page >= totalPages: finishedReading = True else: finishedReading = False page = page + 1 else: print(requestResponse.content) failedAttempts = failedAttempts + 1 finishedReading = False if failedAttempts == 4: productDBLogger.error( "WpApi BulkStockCount Encountered 4 errors in a row in simple product fetch and was forced to quit early!!!" ) productDBLogger.info("WcAPI Fetching Variation Stock Counts ") rawStockCountList = list() count = 1 total = len(variableProductIDList) for variableProductID in variableProductIDList: page = 1 finishedReading = False failedAttempts = 0 while (not finishedReading) and (failedAttempts < 4): requestResponse = self.APIClient.get( f"products/{variableProductID}/variations", params={ "page": page, "per_page": 50 }) if requestResponse.status_code == 200: failedAttempts = 0 responseJSON = requestResponse.json() rawStockCountList = rawStockCountList + responseJSON totalPages = int( requestResponse.headers["X-WP-TotalPages"]) if page >= totalPages: page = page + 1 finishedReading = True else: page = page + 1 finishedReading = False else: print(requestResponse.content) failedAttempts = failedAttempts + 1 finishedReading = False if failedAttempts == 4: productDBLogger.error( f"WC API forced to abort fetching variation data for variable product with WooCommerce ID: {variableProductID}" ) count = count + 1 return rawStockCountList # Latest Change and Reporting Handlers ####################################### def _logChange(self, changeObj: ReceivedPlatformStockChange): self.latestChanges.append(changeObj) def getLatestChanges(self): latestChanges = self.latestChanges self.latestChanges = list() return latestChanges def _addToOneTimeBlacklist(self, productSKU: str): self.oneTimeBlacklist.append(productSKU) def _productSKUInOneTimeBlacklist(self, productSKU: str): exists = (productSKU in self.oneTimeBlacklist) if exists: self.oneTimeBlacklist.remove(productSKU) return exists # Webhook product modified handlers ##################################### def webhook(self): # TODO: Clean up this if tree. if not request.is_json: print(request.data) return "JSON Expected!", 200 else: changeObj = self._convertProductDataIntoReceivedChange( request.json) if changeObj is None: pass else: if self._willOwnChangeHaveAnyEffectOnStockRecord(changeObj): if not self._productSKUInOneTimeBlacklist( changeObj.productSKU): self._logChange(changeObj) else: pass else: pass return "Received!", 200 # Sent Change Handlers ####################### def applyChange(self, change): if change.action == "set": return self._setStock(change) elif change.action == "change": return self._changeStock(change) else: productDBLogger.warn( f"WC API Encountered Unexpected action type: {change.action}") return False def _setStock(self, change: SentPlatformStockChange): # TODO: Implement ID caching, similar to Etsy # Must do this for changeStock methods too? productObject = change.product WCProductObject = WooCommerceProduct.fetchBySKU( self.APIClient, productObject.sku) if WCProductObject is None: productDBLogger.warn( "WooCommerce was sent a change for a non-existent Product ~ Un registering WC from product's services." ) productDBLogger.critical( "Exceptions need to be implemented. This will be a large refactor but is very important." ) productObject.unregister_service(self.persistent_identifier) return True else: if WCProductObject.type == "simple": return self._setStockSimpleProduct(WCProductObject, change.value) elif WCProductObject.type == "variation": return self._setStockVariationProduct(WCProductObject, change.value) else: productDBLogger.warn( f"WCAPI was asked to set the stock of a product of unexpected type: {WCProductObject.type}" ) return False def _changeStock(self, change: SentPlatformStockChange): productObject = change.product WCProductObject = WooCommerceProduct.fetchBySKU( self.APIClient, productObject.sku) if WCProductObject is None: productDBLogger.warn( "WooCommerce was sent a change for a non-existent Product ~ Un registering WC from product's services." ) productDBLogger.critical( "Exceptions need to be implemented. This will be a large refactor but is very important." ) productDBLogger.critical( "I mean heck! This function just returned True!!!! This means my system thinks the change occurred sucessfully!" ) productObject.unregister_service(self.persistent_identifier) return True else: # Pre-calculate new stock values... if WCProductObject.manage_stock: newStock = WCProductObject.stock_quantity + change.value else: newStock = productObject.stockRecord.value + change.value # Hand-off to set handlers. if WCProductObject.type == "simple": return self._setStockSimpleProduct(WCProductObject, newStock) elif WCProductObject.type == "variation": return self._setStockVariationProduct(WCProductObject, newStock) else: productDBLogger.warn( f"WooCommerceAPI was asked to set the stock of a product of unexpected type: {WCProductObject.type}" ) return False def _setStockSimpleProduct(self, WCProductObject: WooCommerceProduct, newStock: Decimal): requestData = {"manage_stock": True, "stock_quantity": str(newStock)} responseObject = self.APIClient.put(f"products/{WCProductObject.ID}", requestData) if responseObject.status_code == 200: return True else: productDBLogger.error( f"Failed to set stock of a simple product: ErrorMSG: {responseObject.content}" ) return False def _setStockVariationProduct(self, WCProductObject: WooCommerceProduct, newStock: Decimal): requestData = {"manage_stock": True, "stock_quantity": str(newStock)} responseObject = self.APIClient.put( f"products/{WCProductObject.parentID}/variations/{WCProductObject.ID}", requestData) if responseObject.status_code == 200: return True else: productDBLogger.error( f"Failed to set stock of a variation product: ErrorMSG: {responseObject.content}" ) return False # Product Data Converters and Request Methods ########################################### def _convertProductDataIntoReceivedChange(self, productData: dict): productSKU = productData.get("sku") productType = productData.get("type") stockManaged = productData.get("manage_stock") if (not stockManaged) or (productSKU is None) or (productType == "variable"): return None # If passes checks... productDBLogger.info( dateutil.parser.parse(productData.get("date_modified_gmt"))) return ReceivedPlatformStockChange( productSKU=productSKU, action="set", value=dround(Decimal(productData.get("stock_quantity")), 6), timeOccurred=dateutil.parser.parse( productData.get("date_modified_gmt")), platformChangeID=f"dummy-platform-change-id-{uuid4()}", platformIdentity=self.persistent_identifier) def _convertProductDataIntoStockCount(self, productData: dict): # Unpack Product SKU, ensure it isn't none, e.g. that an sku has been inputted. productSKU = productData.get("sku") if (productSKU is None) or (productSKU == ""): return None else: # Ensure that WC is managing stock for this product manageStock = productData.get("manage_stock") if manageStock: # Get WC stored stock value stockQuantity = productData.get("stock_quantity") # Ensure stock value actually exists. # TODO: Maybe some other behaviour should occur here. # e.g. Stock updates with the latest stock value for the product. if stockQuantity is None: return None stockQuantity = dround(Decimal(stockQuantity), 6) # Get productObject, for WC product. # This product may not exist in DB yet however, and as such must be created. productObject = Product.objects(sku=productSKU).first() if productObject is None: # When productObject doesn't exist, build a new product. productObject = Product(sku=productSKU) # Create a new stockRecord, defaulting the value to that reported by this platform... # as product doesn't yet exist for other platforms. newStockRecord = StockRecord(product=productObject, value=stockQuantity) # Save these objects. productObject.save() newStockRecord.save() # Register that product exists on this service... (product has to be saved) productObject.register_service(self.persistent_identifier) # If the productObject doesn't have a record of existing on this service, register it. if not productObject.is_registered_on_service( self.persistent_identifier): productObject.register_service(self.persistent_identifier) productObject.save() # Create and return the stock count. stdStockCount = StockCount( product=productObject, value=stockQuantity, platformIdentity=self.persistent_identifier) return stdStockCount else: return None # TODO: Tidy this method up... # maybe incorporate its functionality into the base stock manager to prevent data conflict @staticmethod def _willOwnChangeHaveAnyEffectOnStockRecord( changeObj: ReceivedPlatformStockChange): if changeObj.action == "change": if changeObj.value == 0: return False else: return True elif changeObj.action == "set": productObj = Product.objects(sku=changeObj.productSKU).first() if productObj is None: return True else: productObjStockRecord = productObj.stockRecord if productObjStockRecord is None: return True else: if changeObj.value == productObjStockRecord.value: return False else: return True else: productDBLogger.warn( f"WC Api encountered an unexpected change action: {changeObj.action}, applied to product: {changeObj.productSKU}" ) return False
class WooCommerceShim(Database): """ Contains various methods for interacting with the WooCommerce API. These methods will do things such as adding new products, and removing sold products. """ def __init__(self, *args, **kwargs): super(WooCommerceShim, self).__init__(*args, **kwargs) # Setup logging self.log = logging.getLogger(__name__) self.log.setLevel(os.environ.get('log_level', 'INFO')) self.log.addHandler(LOG_HANDLER) self.api = WCAPI(url=os.environ.get('woo_url', False), consumer_key=os.environ.get('woo_key', False), consumer_secret=os.environ.get('woo_secret', False)) self.wp_api = WPAPI(url=os.environ.get('woo_url', False), api='wp-json', version='wp/v2', wp_user=os.environ.get('wordpress_user', False), wp_pass=os.environ.get('wordpress_app_password', False), basic_auth=True, user_auth=True, consumer_key=False, consumer_secret=False) mapping_path = os.environ.get( 'category_mapping', 'database/ebay-to-woo-commerce-category-map.json') try: with open(mapping_path, 'r') as mapping_file: self.category_mapping = json.load(mapping_file) except IOError: self.category_mapping = None def __does_image_exist_on_woocommerce(self, slug): """ Searches the Wordpress media library for any files that have a URL `slug` that matches the one provided Returns True if the file exists, and False otherwise This method is unreliable! Duplicates are basically guarenteed to happen... """ self.log.info('Checking if a file has a slug matching: %s' % (slug)) result = self.wp_api.get('/media?slug=%s' % (slug)).json() if len(result) == 0: return False return True def __divide_into_chunks(self, iterable, chunk_size=100): """ Used to make bulk requests via the API, which limits the amount of products to change at once to 100 `iterable` is something that can be iterated over, be it a list or a range. When a range, you must wrap the output of this method in `list()` `chunk_size` is optional, and defines how many products to change per request. The default of 100 is the maximum that the API will allow Returns the `iterable` containing as many items as are in the `chunk_size` """ for i in range(0, len(iterable), chunk_size): yield iterable[i:i + chunk_size] def __search_map(self, value, field): """ Uses List Comprehension to search for the `value` in the `field`. Normal usage would be similar to `self.__search_map(ebay_category_id, 'ebay_ids')` Returns an integer, which is the first matching Woo Commerce ID for the selected `value` (in the case that one ebay category is mapped to multiple woo commerce categories) When a matching category can't be found, this method will call itself to search for the "Uncategorized" `value` on the "wc-name" `field` """ try: mapped = [ key for key in self.category_mapping if value in key[field] ] return int(mapped[0]['wc-id']) except IndexError: # Couldn't find it, return the uncategorized id return self.__search_map('Uncategorized', 'wc-name') def does_product_exist(self, item_id): """ Determines if the product with the `item_id` has already been uploaded to WooCommerce, by checking for the truthyness of `post_id` """ data = self.db_get_product_data(item_id) if data.get('post_id') is not None: return True return False def get_mapped_category_id(self, ebay_category_id): """ Determines if the user provided a category mapping, and if so returns an integer, which is the Woo Commerce category id that is mapped to the `ebay_category_id` (or the Uncategorized category id if a mapping can not be found) In the case that the user has not provided a category mapping, this method returns None """ if self.category_mapping is not None: return self.__search_map(ebay_category_id, 'ebay_ids') return None def download_product_images_from_ebay(self, item_id): """ Downloads all of the images for a provided `item_id` and returns a dictionary containing the image name, mime type, and bytes-like object for the raw images The image URLs come from the database table `item_metadata`, which is populated when `self.__get_item_metadata()` runs """ count = 0 return_images = list() image_urls = self.db_get_product_image_urls(item_id) image_urls_count = len(image_urls) if image_urls_count > 0: self.log.info("Found %d image URLs for: %s" % (image_urls_count, item_id)) for image in image_urls: url = image.get('value', '') if image.get('post_id') is not None: self.log.warning( "We've already uploaded %s, skipping download" % (url)) continue self.log.info("Downloading %s" % (url)) req = requests.get(url) if req.content: mime_type = req.headers.get('Content-Type', '') slug = '%s-%d' % (item_id, count) extension = mime_type.split('/')[1] filename = '%s.%s' % (slug, extension) if 'image' not in mime_type: msg = "%d didn't get an image somehow. Content type was: %s" self.log.error(msg % (item_id, mime_type)) continue return_images.append( Image(slug=slug, ebay_url=url, name=filename, mime_type=mime_type, data=req.content)) # self.log.info("Image %s downloaded" % (filename)) if count < image_urls_count: self.log.debug( "Waiting a quarter second until next download") time.sleep(0.25) else: self.log.error( "No content returned. Is %s reachable in a browser?" % (url)) count += 1 else: self.log.warning("No Image URLs found for item: %s" % (item_id)) return return_images def upload_image_to_woocommerce(self, image, post_id): """ Uploads the provided `image` to wordpress, and returns the response `image` is a dictionary containing the following keys: `name` - This is the destination file name, including extension `type` - This is the MIMETYPE of the image, usually derived from the extension `data` - This is a bytes-like object representing the entire image. We get this from dowloading an image directly from Ebay's servers and temporarily storing it in memory `post_id` is the post in which to attach the image to. This is returned in the response from `self.create_product()` Returns either a string containing the URL the image can be found at, or False if the image fails to be uploaded """ self.log.info("Uploading %s to wordpress" % (image.name)) endpoint = '/media?post=%d' % (post_id) headers = { 'cache-control': 'no-cache', 'content-disposition': 'attachment; filename=%s' % (image.name), 'content-type': '%s' % (image.mime_type) } # Don't upload a duplicate image if it was uploaded in the past if self.__does_image_exist_on_woocommerce(image.slug): self.log.warning( "Image %s already exists on wordpress. Not uploading again" % (image.name)) return None, None # Upload the image response = self.wp_api.post(endpoint, image.data, headers=headers) try: image_id = response.json().get('id') url = response.json().get('guid', dict).get('raw') self.log.debug("Uploaded %s to %s" % (image.name, url)) return image_id, url except AttributeError: self.log.error('Could not upload %s' % image.name) return None, None def upload_product_images(self, item_id): """ With the provided `item_id`, the database is searched for the post id (set during `create_product`) When the post_id is found, it will be used to download the images for that product from ebay, and then upload the images """ post_id = self.db_woo_get_post_id(item_id) gallery = [] if post_id is not None: for image in self.download_product_images_from_ebay(item_id): image_id, url = self.upload_image_to_woocommerce( image, post_id) if image_id and url: self.db_metadata_uploaded(image_id, item_id) gallery.append({'id': image_id}) # Add the images to the gallery self.api.put('products/%d' % (post_id), {'images': gallery}).json() else: self.log.warning('The product %d has not yet been uploaded' % (item_id)) return self def create_product(self, item_id): """ Pulls the product related to the `item_id` out of the database and uploads it to WooCommerce Returns the result as JSON """ attributes = list() attributes_to_upload = list() self.log.info('Creating a WooCommerce product from ebay id: %s' % (item_id)) if self.does_product_exist(item_id): self.log.warning( 'Product with item id %d already exists, skipping' % (item_id)) return self product = self.db_get_product_data(item_id) attributes = self.db_get_all_product_metadata(item_id) # Strip out any pictures attributes = [ attribute for attribute in attributes if attribute['key'] != 'picture_url' ] # Format the attributes in a way that WooCommerce is expecting for index, attribute in enumerate(attributes): attributes_to_upload.append({ 'name': attribute['key'], 'options': [attribute['value']], 'visible': True, 'variation': True, 'position': index, }) upload_data = { 'name': product['title'], 'type': 'simple', 'status': 'publish', 'short_description': product['condition_description'], 'description': DEFAULT_DESCRIPTION, 'sku': product['sku'], # 'attributes': attributes_to_upload, # 'default_attributes': attributes_to_upload, } # Add the category id category_id = self.get_mapped_category_id(product.get( 'category_id', 0)) if category_id is not None: upload_data['categories'] = [{'id': category_id}] res = self.api.post('products', upload_data).json() self.log.debug(res) if res.get('id', False): self.db_product_uploaded(res['id'], item_id) else: # Invalid or duplicate sku if res.get('code') == 'product_invalid_sku': if res.get('data') and res['data'].get('resource_id'): new_post_id = res['data']['resource_id'] self.log.warning( 'The SKU for %s already exists for %s. Updating.' % (new_post_id, item_id)) self.db_product_uploaded(new_post_id, item_id) else: self.log.error('Unable to retrive product_id') self.log.debug(res) self.log.debug(upload_data) return self def delete_product_images(self, item_id): """ With the provided `item_id`, an API request will be made to WooCommerce to identify all images associted with it. Then, it will delete each of those images. """ # This feature has not been implemented. # Delete media through wordpress directly pass def delete_product(self, item_id): """ With the provided `item_id`, an API request will be made to WooCommerce to force delete the item The `item_id` is supplied by the queue, which gets them from `db.db_get_inactive_uploaded_item_ids()` When an item is force deleted, it will not appear in the "Trash" Returns the response as a dictionary or None if there is no post id """ post_id = self.db_woo_get_post_id(item_id) if post_id is not None: self.log.info('Deleting %d from WooCommerce' % (item_id)) try: response = self.api.delete('products/%d' % (post_id), params={ 'force': True }).json() except TypeError: self.log.error("Got unexpected response type: %s" % (str(response))) return None self.delete_product_images(post_id) status_code = response.get('data', dict).get('staus', 500) if status_code == 404: self.log.warning("Product was already deleted") elif status_code < 300 and status_code > 199: self.log.info('Product deleted') else: self.log.debug(response) return response return None def delete_all_products_in_range(self, id_range, chunk_size=100): """ With a provided `id_range`, which is expected to be a `range` or `list` type, multiple bulk requests will be made to the Woo Commerce API to delete those items. When `id_range` is of type(range), your ending ID needs to be the last ID to delete + 1 Returns None """ self.log.info('Deleting products from %d to %d' % (id_range[0], id_range[-1])) # The API says that it supports chunks up to 100 items, but in testing # it would always time out, even if it successfully deleted the items # with any chunk size greater than or equal to 50 for chunk in self.__divide_into_chunks(id_range, chunk_size): post_ids = list(chunk) data = {'delete': post_ids} self.api.post('products/batch', data) self.log.info('Deleted ids %s' % (post_ids)) for post_id in post_ids: self.delete_product_images(post_id) def try_command(self, command, data): """ Wrapper for running methods. Verifies that we support the method, raising a NameError if not and then runs the method specified in the `command` argument in a try, except statement `command` is a string that is inside `__available_commands` `data` is dependent on the type of command that is being ran. In most instances, it is an integer containing the ebay ItemID. With the `delete_all_products` command, it is either a range or a list containing the post ids for existing products """ __available_commands = [ 'create_product', 'delete_product', 'upload_images', 'delete_all_products', ] err_msg = "Command %s is unrecognized. Supported commands are: %s" % ( command, ', '.join(__available_commands)) if command not in __available_commands: self.log.exception(err_msg) raise NameError(err_msg) try: if command == 'create_product': return self.create_product(data) elif command == 'delete_product': return self.delete_product(data) elif command == 'upload_images': return self.upload_product_images(data) elif command == 'delete_all_products': return self.delete_all_products_in_range(data) else: self.log.exception(err_msg) raise NameError(err_msg) # The several kinds of timeout exceptions that are normally returned by the API except (timeout, ReadTimeoutError, requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout): self.log.warning( 'The Previous request Timed Out. Waiting 5s before retrying') time.sleep(5) self.try_command(command, data)
class Wc: def __init__(self, url, consumer_key, consumer_secret): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(url, str) assert isinstance(consumer_key, str) assert isinstance(consumer_secret, str) self.wcapi = API( url=url, consumer_key=consumer_key, consumer_secret=consumer_secret, wp_api=True, version="wc/v2", verify_ssl=False, query_string_auth=True, timeout= 30, # requires especially for WooCommerce connects with Jetpack ) def getGeneralSetting(self): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") try: r = self.wcapi.get("settings/general") return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(str(exp)) logger.error(traceback.format_exc()) raise exp def getProductsList(self, items_per_page, page_no, category_id=None, tag_id=None): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(items_per_page, int) assert isinstance(page_no, int) url = "products?per_page={0}&page={1}".format(items_per_page, page_no) if category_id is not None: url += "&category=" + str(category_id) if tag_id is not None: url += "&tag=" + str(tag_id) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(str(exp)) logger.error(traceback.format_exc()) raise exp def getTagBySlug(self, tag_name): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(tag_name, str) url = "products/tags?slug={0}".format(tag_name) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(str(exp)) logger.error(traceback.format_exc()) raise exp def getProductDetail(self, product_id): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(product_id, str) url = "products/{0}".format(product_id) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def getProductVariations(self, product_id, item_per_page, page_no): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(product_id, str) assert isinstance(item_per_page, int) assert isinstance(page_no, str) url = "products/{0}/variations?per_page={1}&page={2}".format( product_id, item_per_page, page_no) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def getProductDetailByIds(self, product_ids): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(product_ids, list) url = "products?include=" + ",".join(product_ids) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def getProductCategoriesList(self, items_per_page, page_no): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(items_per_page, int) assert isinstance(page_no, int) url = "products/categories?per_page={0}&page={1}".format( items_per_page, page_no) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def searchProductsByName(self, items_per_page, page_no, name): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(items_per_page, int) assert isinstance(page_no, int) kw_url = quote(name) # print("kw_url=" + kw_url) url = "products?search={0}&status=publish&per_page={1}&page={2}".format( kw_url, items_per_page, page_no) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def searchProductsByPriceRange(self, items_per_page, page_no, min_price, max_price): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(items_per_page, int) assert isinstance(page_no, int) assert min_price is not None or max_price is not None # print("kw_url=" + kw_url) # url = "products?search={0}&status=publish&per_page={1}&page={2}".format(kw_url, items_per_page, page_no) url = "products?" if min_price is not None: url += "min_price=" + min_price + "&" if max_price is not None: url += "max_price=" + max_price + "&" url += "status=publish&per_page={0}&page={1}".format( items_per_page, page_no) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def createOrder(self, iinfo, items): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert iinfo is not None assert items is not None assert iinfo["billing"] is not None assert iinfo["billing"]["first_name"] is not None assert iinfo["billing"]["last_name"] is not None assert iinfo["billing"]["email"] is not None assert iinfo["billing"]["phone"] is not None assert iinfo["billing"]["address1"] is not None # assert iinfo["billing"]["address2"] is not None assert iinfo["billing"]["city"] is not None assert iinfo["billing"]["state"] is not None # assert iinfo["billing"]["postal"] is not None assert iinfo["billing"]["country"] is not None assert iinfo["shipping"] is not None assert iinfo["shipping"]["first_name"] is not None assert iinfo["shipping"]["last_name"] is not None assert iinfo["shipping"]["address1"] is not None # assert iinfo["shipping"]["address2"] is not None assert iinfo["shipping"]["city"] is not None assert iinfo["shipping"]["state"] is not None # assert iinfo["shipping"]["postal"] is not None assert iinfo["shipping"]["country"] is not None assert isinstance(items, list) for item in items: assert item["product_id"] != 0 assert item["qty"] is not None assert item["unit_price"] is not None # map to WooCommerce order properties # http://woocommerce.github.io/woocommerce-rest-api-docs/#create-an-order postData = { "set_paid": False, "prices_include_tax": False, "billing": { "first_name": iinfo["billing"]["first_name"], "last_name": iinfo["billing"]["last_name"], "address_1": iinfo["billing"]["address1"], # "address_2": iinfo["billing"]["address2"], "city": iinfo["billing"]["city"], "state": iinfo["billing"]["state"], # "postcode": iinfo["billing"]["postal"], "country": iinfo["billing"]["country"], "email": iinfo["billing"]["email"], "phone": iinfo["billing"]["phone"] }, "shipping": { "first_name": iinfo["shipping"]["first_name"], "last_name": iinfo["shipping"]["last_name"], "address_1": iinfo["shipping"]["address1"], # "address_2": iinfo["shipping"]["address2"], "city": iinfo["shipping"]["city"], "state": iinfo["shipping"]["state"], # "postcode": iinfo["shipping"]["postal"], "country": iinfo["shipping"]["country"], "email": iinfo["shipping"]["email"], "phone": iinfo["shipping"]["phone"] }, "line_items": [] } if "postal" in iinfo["billing"]: postData["billing"]["postal"] = iinfo["billing"]["postal"] if "postal" in iinfo["shipping"]: postData["shipping"]["postal"] = iinfo["shipping"]["postal"] if "address2" in iinfo["billing"]: postData["billing"]["address_2"] = iinfo["billing"]["address2"] if "address2" in iinfo["shipping"]: postData["shipping"]["address_2"] = iinfo["shipping"]["address2"] for item in items: item_obj = { "product_id": item["product_id"], "quantity": int(item["qty"]), "price": float(item["unit_price"]) } postData["line_items"].append(item_obj) url = "orders" try: r = self.wcapi.post(url, postData) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def getShippingSettings(self): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") result = [] r = self.wcapi.get("shipping/zones") ship_zones = json.loads(mod_misc.wcCorrectResp(r.text)) for zone in ship_zones: if "_links" in zone: del zone["_links"] r = self.wcapi.get("shipping/zones/" + str(zone["id"]) + "/locations") locations = json.loads(mod_misc.wcCorrectResp(r.text)) for loc in locations: if "_links" in loc: del loc["_links"] r = self.wcapi.get("shipping/zones/" + str(zone["id"]) + "/methods") methods = json.loads(mod_misc.wcCorrectResp(r.text)) methods2 = [] for method in methods: # print("method") # print(method) if method["enabled"]: methods2.append({ "id": method["id"], "title": method["title"], "method_id": method["method_id"], "method_title": method["method_title"], "settings": method["settings"] }) result.append({ "zone": zone, "locations": locations, "methods": methods2 }) return result def getShippingMethods(self): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") result = [] r = self.wcapi.get("shipping_methods") ship_methods = json.loads(mod_misc.wcCorrectResp(r.text)) return ship_methods def updateOrder(self, order_id, update_props): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert order_id is not None assert update_props is not None url = "orders/" + str(order_id) try: r = self.wcapi.put(url, update_props) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def getOrder(self, order_id): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert order_id is not None url = "orders/" + str(order_id) try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def deleteOrder(self, order_id): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(order_id, str) url = "orders/" + order_id try: r = self.wcapi.delete(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def getPaymentGateways(self): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") url = "payment_gateways" try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def getProductStock(self, product_id): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert isinstance(product_id, str) url = "products/" + product_id try: r = self.wcapi.get(url) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp def updateProductBatch(self, cmd_obj): logger.debug( str(currentframe().f_lineno) + ":" + inspect.stack()[0][3] + "()") assert cmd_obj is not None assert cmd_obj["update"] is not None url = "products/batch" try: r = self.wcapi.post(url, cmd_obj) return json.loads(mod_misc.wcCorrectResp(r.text)) except Exception as exp: logger.error(traceback.format_exc()) raise exp
class Request: def __init__(self): """ http://woocommerce.github.io/woocommerce-rest-api-docs/ """ consumer_key = config['CONNECTION']['consumer_key'] consumer_secret = config['CONNECTION']['consumer_secret'] self.wcapi = API( url=config['CONNECTION']['url'], consumer_key=consumer_key, consumer_secret=consumer_secret, wp_api=True, version="wc/v2" ) def post(self, endpoint, data): """ this method implements POST request to the provided endpoint with provided data in the body. :param endpoint: endpoinf for request :param data: json body :return: lis of elements - response_code, response_body, response_url """ response = self.wcapi.post(endpoint, data) response_code = response.status_code response_body = response.json() response_url = response.url return [response_code, response_body, response_url] def get(self, endpoint): """ this method implements GET request to the provided endpoint. :param endpoint: endpoint for request :return: lis of elements - response_code, response_body, response_url """ response = self.wcapi.get(endpoint) response_code = response.status_code response_body = response.json() response_url = response.url return [response_code, response_body, response_url] def delete(self, endpoint): """ this method implements DELETE request to the provided endpoint :param endpoint: endpoint for request :return: lis of elements - response_code, response_body, response_url """ response = self.wcapi.delete(endpoint) response_code = response.status_code response_body = response.json() response_url = response.url return [response_code, response_body, response_url] def put(self, endpoint, data): """ this method implements PUT request to the provided endpoint with provided data :param data: json body :param endpoint: endpoint for request :return: lis of elements - response_code, response_body, response_url """ response = self.wcapi.put(endpoint, data) response_code = response.status_code response_body = response.json() response_url = response.url return [response_code, response_body, response_url]
from woocommerce import API from woocomerce_keys import Woocomerce woo = Woocomerce() wcapi = API( url="https://www.jamaicatea.cl", consumer_key=woo.get_key(), consumer_secret=woo.get_secret(), #wp_api=True, version="wc/v3") #print(wcapi.get("products").json()) data = {'regular_price': '1290', 'sale_price': '990'} print(wcapi.put("products/2844/variations/2845", data).json())
class start(QDialog): def __init__(self): QDialog.__init__(self) self.wcapi = API( url="http://psdev.pskiss.com", consumer_key="ck_fc533175c1e0817a29f0f6879366b0c8c62bf19b", consumer_secret="cs_cdbbe45f3b87940e11991dcd632da402a9308d10", timeout=30, ) if hasattr(sys, '_MEIPASS'): palette = QPalette() palette.setBrush( QPalette.Background, QBrush(QPixmap(os.path.join(sys._MEIPASS, "Installer.png")))) self.setAutoFillBackground(True) self.setPalette(palette) self._filenewIcon = QIcon( QPixmap(os.path.join(sys._MEIPASS, "pmlogo.jpg"))) css_path = os.path.join(sys._MEIPASS, "style.css") with open(css_path, "r") as fh: self.setStyleSheet(fh.read()) else: self._filenewIcon = QIcon(QPixmap("pmlogo.jpg")) palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap("Installer.png"))) self.setAutoFillBackground(False) self.setPalette(palette) sshFile = "style.css" sub = "resource" with open(sshFile, "r") as fh: self.setStyleSheet(fh.read()) self.setWindowTitle("Presets Mentor Installer") self.setWindowIcon(self._filenewIcon) self.layout = QGridLayout() self.text = QLabel() QDialog.setFixedSize(self, 800, 600) self.thread_count = threading.active_count() self.threads = [] self.msg_queue = Queue() self.settext() self.button2 = QPushButton("submit") self.group_box = QGroupBox() moods = [ QCheckBox("Canon"), QCheckBox("Nikon"), QCheckBox("Sony"), QCheckBox("Other") ] #self.group_box.setCheckable(True) self.chk_vlayout = QVBoxLayout() self.mood_button_group = [] for i in xrange(len(moods)): # Add each radio button to the button layout self.chk_vlayout.addWidget(moods[i]) self.mood_button_group.append(moods[i]) # Add each radio button to the button group & give it an ID of i #self.mood_button_group.addButton(moods[i]) self.button2.setFixedWidth(180) #self.chk_vlayout.addWidget(self.button2) self.group_box.setLayout(self.chk_vlayout) self.group_box.setFixedWidth(200) self.group_box.setStyleSheet( "border:0; padding-left:10px; font-size:18px;") self.layout.setAlignment(self.group_box, Qt.AlignRight) self.setLayout(self.layout) self.progress = QProgressBar(self) self.progress.setGeometry(260, 385, 280, 20) self.progress.hide() self.setFocus() def setback(self, img): if hasattr(sys, '_MEIPASS'): palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap(os.path.join(sys._MEIPASS, img)))) self.setAutoFillBackground(True) self.setPalette(palette) else: palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap(img))) self.setAutoFillBackground(False) self.setPalette(palette) def settext(self): self.button = QPushButton("Next") self.button.setMaximumSize(180, 180) self.button.setMinimumSize(150, 30) self.layout.addWidget(self.button, 0, 1) self.setContentsMargins(100, 100, 100, 100) self.button.clicked.connect(self.setOrderId) self.layout.setAlignment(self.button, Qt.AlignBottom) def setOrderId(self): self.setback("Installer2.png") self.button.hide() #self.btn1=QGroupBox() #self.btnc=QVBoxLayout() self.line_edit = QLineEdit() self.line_edit.setFixedHeight(30) self.line_edit.setFixedWidth(180) self.button1 = QPushButton("submit") self.line_edit.setFocus() self.button1.clicked.connect(self.get_order_id) self.button1.setFixedHeight(30) self.layout.addWidget(self.line_edit, 1, 1) self.layout.addWidget(self.button1, 2, 1) self.setContentsMargins(100, 340, 100, 180) def get_order_id(self): self.order_id = self.line_edit.text() try: j = (self.wcapi.get("orders/" + self.order_id + "?filter[meta]=true").json()) if j[u'order'][u'status'] == "completed": #if d < 3 : d = j[u'order'][u'order_meta'][u'number'] self.x = int(float(d)) if self.x < 100: self.line_edit.hide() self.button1.hide() #self.label.setText("") self.setback("Installer3.png") self.layout.addWidget(self.group_box, 1, 0) self.button2.setFixedWidth(150) self.button2.setFixedHeight(30) self.layout.addWidget(self.button2, 1, 1) self.setContentsMargins(200, 400, 350, 50) self.layout.setAlignment(self.button2, Qt.AlignBottom) self.button2.clicked.connect(self.setcamType) else: self.setback("Installer6.png") self.text.setText( "<a href='mailto:[email protected]'>[email protected]</a>" ) self.text.setOpenExternalLinks(True) self.layout.addWidget(self.text, 0, 1) self.setContentsMargins(90, 350, 90, 70) self.button1.setFixedWidth(180) self.layout.setAlignment(self.button1, Qt.AlignJustify) self.layout.setAlignment(self.line_edit, Qt.AlignJustify) self.text.setStyleSheet( "font-size:35px;margin-right: 80px;font-family: Open sans-serif;" ) except: self.setback("Installer6.png") self.setContentsMargins(100, 400, 100, 100) def setcamType(self): self.group_box.hide() self.button2.hide() self.text.hide() self.setback("Installer4.png") came = [] for i in range(0, 4): if self.mood_button_group[i].isChecked(): typecam = str(self.mood_button_group[i].text()) came.append(typecam) self.http_call(came) self.last_scren() def http_call(self, cam): self.progress.show() input_file = "http://psdev.pskiss.com/wp-content/installer/installer_path.json" f = requests.get(input_file, auth=('test', 'test1234')) f.close() text = f.text obj = [] tj = list(json.loads(text)) self.username = getpass.getuser() for c in cam: c = c + ".zip" tj.append({ 'url': 'http://psdev.pskiss.com//wp-content/installer/CameraRaw/CameraProfiles/' + c, 'file': c, 'path': "AppData\\Roaming\\Adobe\\CameraRaw\\CameraProfiles" }) for url in tj: #thread_count = threading.active_count() clean_url = url[u'url'] file_path = self.username + "\\" + url[u'path'] short_url = '/'.join(clean_url.split('/')[:-1]) local_name = url[u'file'] self.download(clean_url, local_name, file_path) self.progress.reset() self.progress.deleteLater() def download(self, url, local_name, file_path): s = requests.session() s.close() #s.keep_alive = False self.completed = 0 self.progress.setValue(0) try: with closing(s.get(url, auth=('test', 'test1234'), stream=True)) as response: if response.ok: response.raise_for_status() total_size = int(response.headers.get('content-length', 0)) self.progress.setMaximum(total_size) # msg_queue.put(lambda: HelloApp.on_startPb(self.p,total_size)) local_file = open(local_name, 'wb') for chunk in response.iter_content(chunk_size=400024): if chunk: QCoreApplication.processEvents() local_file.write(chunk) self.completed += 400024 self.progress.setValue(self.completed) w = Worker(Queue) work = Thread(target=Worker.zip, args=(w, ), kwargs={ 'file': local_name, 'file_path': file_path }, name='zip') work.setDaemon(True) work.start() except requests.exceptions.HTTPError as err: self.setback("Installer6.png") def on__startbar(self, total): self.progress.setValue(0) self.progress.setMaximum(total) def on_download_complete(self, url, file_name): print("complited " + file_name) self.lock.release() def on__updatabar(self): self.completed += 1024 self.progress.setValue(self.completed) def last_scren(self): self.x = self.x + 1 data = {"order": {"order_meta": {"number": self.x}}} self.wcapi.put("orders/" + self.order_id + "?filter[meta]=true", data).json() #self.group_box.hide() #self.button2.hide() self.progress.hide() self.setback("Installer5.png") self.text5 = QLabel( '<a href="https://www.youtube.com/watch?v=sLV8cqTGECE">Click Here To Watch our Tutorial</a>' ) self.text5.setOpenExternalLinks(True) self.layout.addWidget(self.text5) self.setContentsMargins(30, 400, 100, 80) self.layout.setAlignment(self.text5, Qt.AlignCenter)
def _call(self, method, endpoint, data=None): try: api = API( url=self.woo.location, consumer_key=self.woo.consumer_key, consumer_secret=self.woo.consumer_secret, version=self.woo.version, wp_api=True, timeout=None, ) if api: if method == 'GET': r = api.get(endpoint) elif method == 'POST': r = api.post(endpoint, data) elif method == 'PUT': r = api.put(endpoint, data) if r.status_code in [200, 201]: res = r.json() _logger.info('%s: %s' % (endpoint, res)) return r.json() else: code = r.json().get('code') message = r.json().get('message') _logger.info('%s: %s, %s' % (endpoint, code, message)) err_res = {'id': None} if 'customers' in endpoint: if code == 'registration-error-email-exists' and method == 'POST': return self._call(method='GET', endpoint='customers?search=%s' % data.get('email'))[0] elif code == 'registration-error-invalid-email': return err_res elif code == 'rest_missing_callback_param': return err_res elif code == 'woocommerce_rest_invalid_id': return err_res elif 'products/categories' in endpoint: if code == 'term_exists' and method == 'POST': items = [] for item in self._call( method='GET', endpoint='products/categories?search=%s' % data.get('name')): if item.get('name') == data.get( 'name') and data.get( 'parent', 0) == item.get('parent'): items.append(item) return items[0] elif code == 'woocommerce_rest_term_invalid' and message == 'Resource does not exist.': return err_res elif 'products' in endpoint: if code == 'woocommerce_rest_product_invalid_id': return err_res elif 'orders' in endpoint: if code == 'woocommerce_rest_shop_order_invalid_id': return err_res except (socket.gaierror, socket.error, socket.timeout) as err: raise NetworkRetryableError( 'A network error caused the failure of the job: ' '%s' % err) except xmlrpclib.ProtocolError as err: if err.errcode in [ 502, # Bad gateway 503, # Service unavailable 504 ]: # Gateway timeout raise RetryableJobError( 'A protocol error caused the failure of the job:\n' 'URL: %s\n' 'HTTP/HTTPS headers: %s\n' 'Error code: %d\n' 'Error message: %s\n' % (err.url, err.headers, err.errcode, err.errmsg)) else: raise
print('{sku} already done') continue if '<div' in description: print(f'another div found {sku}') # SKU is already in description continue if str(int(sku)) in description: print(f'{sku} already contains number: {description}') continue description = description.strip() description += f'\n\n<div class="hidden">Art.Nr.: {sku}</div>' max(tqdm._instances).set_description( f'adding SKU to description for {sku}') updates.append({'id': id, 'short_description': description}) stop if len(updates) > 50: max(tqdm._instances).set_description('Submitting 50 items') res = wcapi.put('products/batch', data={'update': updates}) max(tqdm._instances).set_description('Verifying') assert res.status_code == 200, 'Status code is not {res}' responses = res.json()['update'] for i1, i2 in zip(responses, updates): assert i1['short_description'] == i2[ 'short_description'], 'Not the same in than out' updates = []
class WooCommerceAPI: """ WooCommerce API """ BASE_URL = "https://catercentral.com.au" def __init__(self, username: str, password: str): self._username: str = username self._password: str = password timeout = os.environ.get("API_TIMEOUT") or 600 self._wcapi = API(url=WooCommerceAPI.BASE_URL, consumer_key=username, consumer_secret=password, version="wc/v3", timeout=timeout) self._error_messages = [] print(f"API timeout set to {timeout}") def get_all_products(self, skus: Optional[List] = None): """ Get a list of all products """ success = True endpoint = "products" params = {"per_page": 100} if skus is None: response = self._wcapi.get(endpoint=endpoint, params=params) return response.json() products = [] while skus: skus_in_request = skus[:100] skus = skus[100:] params["sku"] = ",".join(skus_in_request) params["per_page"] = len(skus_in_request) response = self._wcapi.get(endpoint=endpoint, params=params) if response.ok: products += response.json() else: success = False return products, success def get_all_products_as_dict(self, skus: Optional[List] = None ) -> Tuple[Dict, bool]: """ Convert list of products to dict of products with key as SKU """ products, success = self.get_all_products(skus) return { product[ApiProductFields.Sku]: product for product in products }, success def update_product(self, product_id: int, data: dict): """ Update a product based on id """ success = False endpoint = f"products/{product_id}" response = self._wcapi.put(endpoint=endpoint, data=data) if response.status_code == 200: success = True return response.json(), success def update_multiple_products(self, data: List[dict]): """ Update multiple products in a single API call """ success = False endpoint = f"products/batch" data = {"update": data} response = self._wcapi.post(endpoint=endpoint, data=data) json_response = dict() if response.status_code == 200: success = True json_response = response.json() errors = [] if "update" in json_response: errors += [ item["error"]["message"] for item in json_response["update"] if "error" in item ] if errors: success = False self._error_messages += errors return json_response, success def create_multiple_products(self, data: List[dict]): """ Create multiple products in a single API call """ success = False endpoint = f"products/batch" data = {"create": data} response = self._wcapi.post(endpoint=endpoint, data=data) json_response = dict() if response.status_code in (200, 201): success = True json_response = response.json() errors = [] if "create" in json_response: errors += [ item["error"]["message"] for item in json_response["create"] if "error" in item ] if errors: success = False self._error_messages += errors return json_response, success def create_or_update_products(self, create_data: List[Dict], update_data: List[Dict]): """ Create or Update products in batch in a single API call """ # Max of 100 objects can be created or updated success = False endpoint = f"products/batch" data = {"create": create_data, "update": update_data} response = self._wcapi.post(endpoint=endpoint, data=data) json_response = dict() if response.ok: success = True json_response = response.json() errors = [] if "create" in json_response: errors += [ item["error"]["message"] for item in json_response["create"] if "error" in item ] if "update" in json_response: errors += [ item["error"]["message"] for item in json_response["update"] if "error" in item ] if errors: success = False self._error_messages += errors return json_response, success def get_all_categories(self, search: Optional[str] = None, per_page: Optional[int] = None, parent: Optional[int] = None): """ Get list of categories """ success = False endpoint = f"products/categories" params = {} if search is not None: params["search"] = search if per_page is not None: params["per_page"] = per_page if parent is not None: params["parent"] = parent response = self._wcapi.get(endpoint=endpoint, params=params) if response.status_code == 200: success = True return response.json(), success def get_all_attributes(self): """ Get a list of product attributes """ success = False endpoint = f"products/attributes" response = self._wcapi.get(endpoint=endpoint) if response.status_code == 200: success = True return response.json(), success @property def errors(self): return self._error_messages
class start(QDialog): def __init__(self): QDialog.__init__(self) self.wcapi = API( url="http://psdev.pskiss.com", consumer_key='ck_12dcbc50569e1c4f994e380803fb9e21fc3064e7', consumer_secret='cs_3ebf311de7bb97add41418f8e55e25982d6f8664', # consumer_key="ck_73006715ef68ce4ddf79d156419291e53b27d2ee", # consumer_secret="cs_c672c6b24e0f6bc33ba676918f51fda163aafecd", timeout=30, ) if hasattr(sys, '_MEIPASS'): palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap(os.path.join(sys._MEIPASS, "Installer.png")))) self.setAutoFillBackground(True) self.setPalette(palette) self._filenewIcon = QIcon(QPixmap(os.path.join(sys._MEIPASS, "pmlogo.jpg"))) css_path = os.path.join(sys._MEIPASS, "style.css") with open(css_path, "r") as fh: self.setStyleSheet(fh.read()) else: self._filenewIcon = QIcon(QPixmap("pmlogo.jpg")) palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap("Installer.png"))) self.setAutoFillBackground(False) self.setPalette(palette) sshFile = "style.css" sub = "resource" with open(sshFile, "r") as fh: self.setStyleSheet(fh.read()) self.setWindowTitle("Presets Mentor Installer") self.setWindowIcon(self._filenewIcon) self.layout = QGridLayout() self.text = QLabel() QDialog.setFixedSize(self, 800, 600) self.thread_count = threading.active_count() self.threads = [] self.fileId = list() self.username = getpass.getuser() self.msg_queue = Queue() self.settext() self.button2 = QPushButton("submit") self.button2.setFixedWidth(180) self.setLayout(self.layout) self.progress = QProgressBar(self) self.progress.setGeometry(260, 385, 280, 20) self.progress.hide() self.setFocus() def setback(self, img): if hasattr(sys, '_MEIPASS'): palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap(os.path.join(sys._MEIPASS, img)))) self.setAutoFillBackground(True) self.setPalette(palette) else: palette = QPalette() palette.setBrush(QPalette.Background, QBrush(QPixmap(img))) self.setAutoFillBackground(False) self.setPalette(palette) def settext(self): self.button = QPushButton("Next") self.button.setMaximumSize(180, 180) self.button.setMinimumSize(150, 30) self.layout.addWidget(self.button, 0, 1) self.setContentsMargins(100, 100, 100, 100) self.button.clicked.connect(self.setOrderId) self.layout.setAlignment(self.button, Qt.AlignBottom) def setOrderId(self): self.setback("Installer2.png") self.button.hide() # self.btn1=QGroupBox() # self.btnc=QVBoxLayout() self.line_edit = QLineEdit() self.line_edit.setFixedHeight(30) self.line_edit.setFixedWidth(180) self.button1 = QPushButton("submit") self.line_edit.setFocus() self.button1.clicked.connect(self.get_order_id) self.button1.setFixedHeight(30) self.layout.addWidget(self.line_edit, 1, 1) self.layout.addWidget(self.button1, 2, 1) self.setContentsMargins(100, 340, 100, 180) def get_order_id(self): self.order_id = self.line_edit.text() try: j = (self.wcapi.get("orders/" + self.order_id + "?filter[meta]=true").json()) for id in j[u'order'][u'line_items']: self.fileId.append(id[u'product_id']) if j[u'order'][u'status'] == "completed": d = j[u'order'][u'order_meta'][u'number'] self.x = int(float(d)) if self.x < 3: self.line_edit.hide() self.button1.hide() self.button2.hide() self.text.hide() self.setback("Installer4.png") self.http_call() self.last_scren() else: self.setback("Installer6.png") self.line_edit.hide() self.button1.hide() self.text.setText("<a href='mailto:[email protected]'>[email protected]</a>") self.text.setOpenExternalLinks(True) self.layout.addWidget(self.text, 1, 1) self.setContentsMargins(100, 450, 100, 70) self.button1.setFixedWidth(180) self.layout.setAlignment(self.button1, Qt.AlignJustify) self.layout.setAlignment(self.line_edit, Qt.AlignJustify) self.text.setStyleSheet("font-size:35px;margin-right: 80px;font-family: Open sans-serif;") except: self.setback("Installer6.png") self.line_edit.hide() self.button1.hide() self.text.setText("<a href='mailto:[email protected]'>[email protected]</a>") self.text.setOpenExternalLinks(True) self.layout.addWidget(self.text, 1, 1) self.setContentsMargins(100, 450, 100, 70) self.button1.setFixedWidth(180) self.layout.setAlignment(self.button1, Qt.AlignJustify) self.layout.setAlignment(self.line_edit, Qt.AlignJustify) self.text.setStyleSheet("font-size:35px;margin-right: 80px;font-family: Open sans-serif;") def http_call(self): self.progress.show() text = [] for fileId in self.fileId: input_file = "http://psdev.pskiss.com/wp-content/installer/" + str(fileId) + ".json" try: f = requests.get(input_file, auth=('test', 'test1234')) f.close() text.append(f.text) except requests.exceptions.HTTPError as err: self.setback("Installer6.png") self.text.setText("<a href='mailto:[email protected]'>[email protected]</a>") self.text.setOpenExternalLinks(True) self.layout.addWidget(self.text, 1, 1) self.setContentsMargins(100, 450, 100, 70) self.button1.setFixedWidth(180) self.layout.setAlignment(self.button1, Qt.AlignJustify) self.layout.setAlignment(self.line_edit, Qt.AlignJustify) self.text.setStyleSheet("font-size:35px;margin-right: 80px;font-family: Open sans-serif;") for filesName in text: tj = list(json.loads(filesName)) for url in tj[::-1]: clean_url = url[u'url'] file_path = self.username + "\\" + url[u'path'] short_url = '/'.join(clean_url.split('/')[:-1]) local_name = url[u'file'] self.download(clean_url, local_name, file_path) self.progress.reset() self.progress.deleteLater() def download(self, url, local_name, file_path): s = requests.session() self.completed = 0 self.progress.setValue(0) try: with closing(s.get(url, auth=('test', 'test1234'), stream=True))as response: if response.ok: response.raise_for_status() total_size = int(response.headers.get('content-length', 0)); self.progress.setMaximum(total_size) local_file = open(local_name, 'wb') for chunk in response.iter_content(chunk_size=80024): if chunk: QCoreApplication.processEvents() local_file.write(chunk) self.completed += 80024 self.progress.setValue(self.completed) s.close() w = Worker(Queue) work = Thread(target=Worker.zip, args=(w,), kwargs={'file': local_name, 'file_path': file_path}, name='zip') work.setDaemon(True) work.start() except requests.exceptions.HTTPError as err: self.setback("Installer6.png") self.text.setText("<a href='mailto:[email protected]'>[email protected]</a>") self.text.setOpenExternalLinks(True) self.layout.addWidget(self.text, 1, 1) self.setContentsMargins(100, 450, 100, 70) self.button1.setFixedWidth(180) self.layout.setAlignment(self.button1, Qt.AlignJustify) self.layout.setAlignment(self.line_edit, Qt.AlignJustify) self.text.setStyleSheet("font-size:35px;margin-right: 80px;font-family: Open sans-serif;") def last_scren(self): self.x = self.x + 1 data = {"order": { "order_meta": {"number": self.x} }} self.wcapi.put("orders/" + self.order_id + "?filter[meta]=true", data).json() self.progress.hide() self.setback("Installer5.png") self.text5 = QLabel('<a href="https://youtu.be/TA79vbBCPsE">Click Here To Watch our Tutorial</a>') self.text5.setOpenExternalLinks(True) self.layout.addWidget(self.text5) self.setContentsMargins(30, 400, 100, 80) self.layout.setAlignment(self.text5, Qt.AlignCenter)
class Woocommerce: """ Small wrapper around the Woocommerce API responsible for getting the data required for the ML algorithm and uploading the cross-sell recommendations :param consumer_key: Woocommerce Rest API credentials (read/write) :param consumer_secret: Woocommerce Rest API credentials (read/write) """ VERSION = "wc/v3" def __init__(self, url: str, consumer_key: str, consumer_secret: str): self.url = url self.consumer_key = consumer_key self.consumer_secret = consumer_secret self._api = API( url=url, consumer_key=consumer_key, consumer_secret=consumer_secret, timeout=10, version=self.VERSION, ) def download_data(self, path: str): """ Downloads transactional data from the Woocommerce online shop via the Rest API :param path: path where the data is going to be stored """ orders_df = self._get_orders() products_df = self._get_products() orders_df = pd.merge(orders_df, products_df, on="SKUID") orders_df = orders_df.sort_values(by="InvoiceID", ascending=True) orders_df["InvoiceID"] = orders_df["InvoiceID"].astype(str) orders_df["SKUID"] = orders_df["SKUID"].astype(str) orders_df.to_csv(path, index=False, encoding="utf-8") def upload_recommendations(self, path, top_n: int = 10): """ Uploads products recommendations to Woocommerce via the Rest API :param path: path where the recommendations are stored :param top_n: to retrieve at most top_n recommendations """ recommendations_df = pd.read_csv(path) products_df = self._get_products() product_mapping = dict(zip(products_df["Item"], products_df["SKUID"])) recommendations_df["Item in cart"] = recommendations_df[ "Item in cart"].map(product_mapping) recommendations_df["Recommendation"] = recommendations_df[ "Recommendation"].map(product_mapping) recommendations_df = recommendations_df.dropna( subset=["Item in cart", "Recommendation"]) recommendations_df = recommendations_df.sort_values( by=["Item in cart", "Support"], ascending=False) recommendations_df = recommendations_df.groupby("Item in cart").head( top_n) recommendations_df = (recommendations_df.groupby("Item in cart") ["Recommendation"].apply(list).reset_index()) for _, row in recommendations_df.iterrows(): data = {"cross_sell_ids": row["Recommendation"]} self._api.put(f"products/{row['Item in cart']}", data).json() def _get_products(self) -> pd.DataFrame: """ Gets products data from Woocommerce via the Rest API """ page = 1 response = self._api.get("products", params={ "per_page": 100, "page": page }).json() products = response.copy() while response: page += 1 response = self._api.get("products", params={ "per_page": 100, "page": page }).json() products += response products = [{ "SKUID": str(product["id"]), "Item": product["name"].upper() } for product in products] products_df = pd.DataFrame(products) return products_df def _get_orders(self) -> pd.DataFrame: """ Get orders data from the Woocommerce Rest API """ page = 1 response = self._api.get("orders", params={ "per_page": 100, "page": page }).json() orders = response.copy() while response: page += 1 response = self._api.get("orders", params={ "per_page": 100, "page": page }).json() orders += response orders = [{ "InvoiceID": str(order["id"]), "SKUID": list({str(item["product_id"]) for item in order["line_items"]}), } for order in orders] orders_df = pd.DataFrame(orders) orders_df = orders_df.explode("SKUID") return orders_df
class SlaveDB: def __init__(self, key, password, address): logger.debug('Connecting to Wordpress...') self.wcapi = API(url=address, consumer_key=key, consumer_secret=password, wp_api=True, version="wc/v3") response = self.wcapi.get("products?per_page=1") if not response.ok: logger.debug('Error: {}'.format(response.json())) raise Exception('Wordpress connection error') @backoff def get_orders(self): logger.debug('[SLAVE] Fetching orders') response = self.wcapi.get("orders?per_page=100") if not response.ok: raise RestException(response.json()) orders = response.json() # we are interested only in unresolved orders orders = [obj for obj in orders if obj['status'] == 'processing'] logger.debug('[SLAVE] Fetched: {} orders'.format(len(orders))) return orders @backoff def get_variation(self, id, variation): text = '[SLAVE] Fetching variation id={} for product: id={}' logger.debug(text.format(variation, id)) url = "products/{}/variations/{}".format(id, variation) response = self.wcapi.get(url) if not response.ok: raise RestException(response.json()) return response.json() @backoff def get_items(self): logger.debug('[SLAVE] Fetching all products') response = self.wcapi.get("products?per_page=100") if not response.ok: raise RestException(response.json()) products = response.json() for product in products: # products may include ids of its variations, thus we have to # download them via separate rest request... parsed_variations = [] for variation in product['variations']: var = self.get_variation(product['id'], variation) parsed_variations.append(var) product['variations'] = parsed_variations logger.debug('[SLAVE] Fetched: {} products'.format(len(products))) return products @backoff def update_order_status(self, order_id, status): logger.debug('[SLAVE] Updating order: id={}'.format(order_id)) data = {"status": status} response = self.wcapi.put("orders/{}".format(order_id), data) if not response.ok: raise RestException(response.json()) return response.json() @backoff def update_item_stock(self, item, stock): if item.get('variation_id', False): return self.update_item_variation_stock(item, stock) logger.debug('[SLAVE] Updating product: id={} stock: {}'.format( item['id'], stock)) data = {"stock_quantity": stock} response = self.wcapi.put("products/{}".format(item['id']), data) if not response.ok: raise RestException(response.json()) return response.json() @backoff def update_item_variation_stock(self, item, stock): text = '[SLAVE] Updating product_variation: id={}/{} stock: {}' logger.debug(text.format(item['id'], item['variation_id'], stock)) data = {"stock_quantity": stock} url = "products/{}/variations/{}".format(item['id'], item['variation_id']) response = self.wcapi.put(url, data) if not response.ok: raise RestException(response.json()) return response.json()