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()
Esempio n. 3
0
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
Esempio n. 4
0
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()
Esempio n. 8
0
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()
Esempio n. 10
0
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"]))
Esempio n. 11
0
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)
Esempio n. 12
0
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())
Esempio n. 13
0
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
Esempio n. 14
0
    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)        
Esempio n. 15
0
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:
# -----------------------------------------------------------------------------
Esempio n. 16
0
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
Esempio n. 17
0
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
Esempio n. 19
0
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]
Esempio n. 20
0
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())
Esempio n. 21
0
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)
Esempio n. 22
0
    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
Esempio n. 23
0
        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
Esempio n. 25
0
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)
Esempio n. 26
0
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
Esempio n. 27
0
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()