# -*- coding: utf-8 -*-
# See LICENSE file for full copyright and licensing details.

"""
Added class and
"""

import html
import math
import time
from datetime import datetime, timedelta

from odoo import models, fields, api, _
from odoo.addons.iap.tools import iap_tools
from odoo.exceptions import UserError

from ..endpoint import DEFAULT_ENDPOINT

class AmazonProductEpt(models.Model):
    _name = "amazon.product.ept"
    _description = "Amazon Product Mapping with Odoo Products"

    name = fields.Char()
    product_id = fields.Many2one('product.product', string='Odoo Product', ondelete="cascade",
                                 help="ERP Product Reference")
    instance_id = fields.Many2one('amazon.instance.ept', string='Instance', required=True,
                                  copy=False, help="Recognise Products to unique Amazon Instances")
    product_asin = fields.Char(copy=False, help="Amazon Product ASIN")
    asin_qty = fields.Integer("Number Of Items In One Package", default=1,
                              help="Amazon Product's Number of Quantity in One Packet")
    product_upc = fields.Char("UPC", copy=False)
    fulfillment_by = fields.Selection(
        [('FBM', 'Manufacturer Fulfillment Network'), ('FBA', 'Amazon Fulfillment Network')],
        default='FBM', help="Amazon Fulfillment Type")
    fix_stock_type = fields.Selection([('fix', 'Fix'), ('percentage', 'Percentage')])
    fix_stock_value = fields.Float(digits="Product UoS")
    exported_to_amazon = fields.Boolean(default=False, copy=False,
                                        help="True:Product exported to Amazon or False: "
                                             "Product is not exported to Amazon.")
    seller_sku = fields.Char(help="Amazon Seller SKU")
    barcode = fields.Char(related="product_id.barcode")
    long_description = fields.Text("Product Description", help="Long description of the product")
    active = fields.Boolean("Active", related="product_id.active")
    fulfillment_channel_sku = fields.Char()
    last_feed_submission_id = fields.Char(readonly=True, copy=False)
    error_in_export_product = fields.Boolean(default=False, copy=False)
    _sql_constraints = [('amazon_instance_seller_sku_unique_constraint',
                         'unique(instance_id,seller_sku,fulfillment_by)',
                         "Seller sku must be unique per instance & Fulfillment By")]

    @api.model
    def search_product(self, seller_sku):
        product_obj = self.env['product.product']
        if self.env.ref('product.product_comp_rule').active:
            cur_usr = self.env.user
            company_id = cur_usr.company_id.id or False
            product = product_obj.search(
                ['|', ('company_id', '=', False), ('company_id', '=', company_id),
                 ('default_code', '=', seller_sku)])
            if len(product) > 1:
                raise UserError(_('Duplicate Product Found For Sku (%s)' % seller_sku))
            if not product:
                product = product_obj.search( \
                    ['|', ('company_id', '=', False), ('company_id', '=', company_id),
                     ('default_code', '=', seller_sku), ('active', '=', False)])
                if len(product) > 1:
                    raise UserError(_('Duplicate Product Found For Sku (%s)' % seller_sku))
                if product and not product.active:
                    product.write({'active': True})
            return product or False
        product = product_obj.search([('default_code', '=', seller_sku)])
        if len(product) > 1:
            raise UserError(_('Duplicate Product Found For Sku (%s)' % seller_sku))
        if not product:
            product = product_obj.search( \
                [('default_code', '=', seller_sku), ('active', '=', False)])
            if len(product) > 1:
                raise UserError(_('Duplicate Product Found For Sku (%s)' % seller_sku))
            if product and not product.active:
                product.write({'active': True})
        return product or False

    @api.model
    def search_amazon_product(self, instance_id, seller_sku, fulfillment_by='FBM'):
        """
        This method will find the amazon product
        """
        seller_sku = seller_sku.strip()
        product = self.search( \
            ['|', ('active', '=', False), ('active', '=', True), ('seller_sku', '=', seller_sku),
             ('instance_id', '=', instance_id), ('fulfillment_by', '=', fulfillment_by)], limit=1)
        if not product:
            return False
        if not product.active:
            product.write({'active': True})
        return product[0]

    standard_product_id_type = fields.Selection(
        [('EAN', 'EAN'), ('ASIN', 'ASIN'), ('GTIN', 'GTIN'), ('UPC', 'UPC')],
        string="Standard Product ID", default='ASIN')
    related_product_type = fields.Selection([('UPC', 'UPC'), ('EAN', 'EAN'), ('GTIN', 'GTIN')])
    related_product_value = fields.Char()
    launch_date = fields.Datetime(help="Controls when the product appears in search and "
                                       "browse on the Amazon website")
    release_date = fields.Datetime(help="The date a product is released for sale")
    discontinue_date = fields.Datetime(help="The date a product is Discontinue for sale")
    condition = fields.Selection([('New', 'New'),
                                  ('UsedLikeNew', 'UsedLikeNew'),
                                  ('UsedVeryGood', 'UsedVeryGood'),
                                  ('UsedGood', 'UsedGood'),
                                  ('CollectibleLikeNew', 'CollectibleLikeNew'),
                                  ('CollectibleVeryGood', 'CollectibleVeryGood'),
                                  ('CollectibleGood', 'CollectibleGood'),
                                  ('CollectibleAcceptable', 'CollectibleAcceptable'),
                                  ('Club', 'Club')], default='New', copy=False)

    item_package_qty = fields.Integer(string="Package Quantity", default=1,
                                      help="Number of the same product contained within"
                                           "one package. "
                                           "For example, "
                                           "if you are selling a case of 10 packages of socks, "
                                           "ItemPackageQuantity would be 10.")

    brand = fields.Char(string="Product Brand",
                        related='product_id.product_tmpl_id.product_brand_id.name', readonly=True)
    designer = fields.Char(help="Designer of the product")

    bullet_point_ids = fields.One2many('amazon.product.bullet.description', 'amazon_product_id',
                                       string="Bullet Point Description")

    package_weight = fields.Float(help="Weight of the package", digits="Stock Weight")
    shipping_weight = fields.Float(help="Weight of the product when packaged to ship",
                                   digits="Stock Weight")
    max_order_quantity = fields.Integer("Maximum Order Quantity",
                                        help="Maximum quantity of the product that a customer can "
                                             "order")

    manufacturer = fields.Char(related='product_id.product_tmpl_id.product_brand_id.partner_id.name',
                               readonly=True)

    search_term_ids = fields.One2many('amazon.product.search.term', 'amazon_product_id',
                                      string="Search Term")
    is_gift_wrap_available = fields.Boolean("Is Gift Wrap Available ?",
                                            help="Indicates whether gift wrapping is available for "
                                                 "the product")

    is_gift_message_available = fields.Boolean("Is Gift Message Available ?",
                                               help="Indicates whether gift messaging is available "
                                                    "for the product")
    gtin_exemption_reason = fields.Selection([('bundle', 'Bundle'), ('part', 'Part')],
                                             string="GtinExemptionReason")
    package_weight_uom = fields.Selection(
        [('GR', 'GR'), ('KG', 'KG'), ('OZ', 'OZ'), ('LB', 'LB'), ('MG', 'MG')], default='KG')

    shipping_weight_uom = fields.Selection(
        [('GR', 'GR'), ('KG', 'KG'), ('OZ', 'OZ'), ('LB', 'LB'), ('MG', 'MG')], default='KG')
    item_dimensions_uom = fields.Selection(
        [('CM', 'CM'), ('FT', 'FT'), ('M', 'M'), ('IN', 'IN'), ('MM', 'MM')],
        string="Item Dimension", default='CM')

    item_height = fields.Float(help="Height of the item dimension", digits="Stock Height")
    item_length = fields.Float(help="Length of the item dimension", digits="Stock Height")
    item_width = fields.Float(help="Width of the item dimension", digits="Stock Height")

    package_dimensions_uom = fields.Selection(
        [('CM', 'CM'), ('FT', 'FT'), ('M', 'M'), ('IN', 'IN'), ('MM', 'MM')],
        string="Package Dimension", default='CM')
    package_height = fields.Float(help="Height of the package dimension", digits="Stock Height")
    package_length = fields.Float(help="Length of the package dimension", digits="Stock Height")
    package_width = fields.Float(help="Width of the package dimension", digits="Stock Height")
    allow_package_qty = fields.Boolean(default=False)
    fulfillment_latency = fields.Integer()

    def get_amazon_product_request_data_ept(self, instance, data, emipro_api):
        """
        Defined common method to prepare the amazon product request data
        """
        account = self.env['iap.account'].search([('service_name', '=', 'amazon_ept')])
        dbuuid = self.env['ir.config_parameter'].sudo().get_param('database.uuid')

        kwargs = {'merchant_id': instance.merchant_id and str(instance.merchant_id) or False,
                  'auth_token': instance.auth_token and str(instance.auth_token) or False,
                  'app_name': 'amazon_ept',
                  'account_token': account.account_token,
                  'emipro_api': emipro_api,
                  'dbuuid': dbuuid,
                  'amazon_marketplace_code': instance.country_id.amazon_marketplace_code or
                                             instance.country_id.code,
                  'data': data,
                  'instance_id': instance.id, }
        return kwargs

    def export_product_amazon(self, instance):
        """
        This Method Relocates export amazon product listing in amazon.
        :param instance:This argument relocates instance of amazon.
        :return: This Method return Boolean(True/False).
        """
        data = self.create_product_envelope(instance)

        kwargs = self.get_amazon_product_request_data_ept(instance, data,
                                                          'export_product_amazon_v13')
        response = iap_tools.iap_jsonrpc(DEFAULT_ENDPOINT + '/iap_request', params=kwargs)
        if response.get('reason'):
            raise UserError(_(response.get('reason')))
        results = response.get('result')
        self.process_export_product_amazon_result(instance, data, results)
        return True

    def process_export_product_amazon_result(self, instance, data, results):
        feed_submission_obj = self.env['feed.submission.history']
        if results.get('FeedSubmissionInfo', {}).get('FeedSubmissionId', {}).get('value', False):
            last_feed_submission_id = results.get(
                'FeedSubmissionInfo', {}).get('FeedSubmissionId', {}).get('value', False)
            for amazon_product in self:
                amazon_product.write( \
                    {'exported_to_amazon': True, 'last_feed_submission_id': last_feed_submission_id, \
                     'error_in_export_product': False})

            vals = {'message': data, 'feed_result_id': last_feed_submission_id,
                    'feed_submit_date': time.strftime("%Y-%m-%d %H:%M:%S"),
                    'instance_id': instance.id, 'user_id': self._uid,
                    'feed_type': 'export_product',
                    'seller_id': instance.seller_id.id}
            feed_submission_obj.create(vals)
        return True

    def create_product_envelope(self, instance):
        """
        This Method relocates prepare envelope for amazon.
        :param amazon_products: This arguments relocates product listing of amazon.
        :param instance: This argument relocates instance of amazon.
        :return: This argument return envelope of amazon.
        """
        message_id = 0
        messages = ''
        for product in self:
            message_id = message_id + 1
            messages = "%s %s" % (messages, self.get_message(message_id, product))
        header = self.get_header(instance)
        data = "%s %s %s" % (header, messages, '</AmazonEnvelope>')
        return data

    def get_message(self, message_id, product):
        """
        This Method relocates prepare envelop message for amazon product.
        :param message_id:This argument relocates message id of amazon because of amazon
        depends on message id.
        :param product:This arguments relocates product listing of amazon
        :return: This Method return amazon envelope message.
        """
        message = """
                <MessageID>%s</MessageID>
                <OperationType>PartialUpdate</OperationType>
                <Product>""" % (message_id)
        message = "%s %s" % (message, self.standard_product_code(product))
        if product.standard_product_id_type == 'GTIN':
            message = "%s %s" % (message, "<GtinExemptionReason>%s</GtinExemptionReason>" % ( \
                product.gtin_exemption_reason))
        if product.related_product_type:
            message = "%s %s" % (message, self.get_related_product_type(product))

        luanch_date = self.get_lanuch_date(product)
        if luanch_date:
            message = "%s %s" % (message, luanch_date)
        discontinue_date = self.get_discontinue_date(product)
        if discontinue_date:
            message = "%s %s" % (message, discontinue_date)
        release_date = self.get_release_date(product)
        if release_date:
            message = "%s %s" % (message, release_date)
        condition = self.get_condition(product)
        if condition:
            message = "%s %s" % (message, condition)
        message = "%s %s" % (message, self.item_package_qty_and_no_of_items(product))
        description_data = self.get_description_data(product)
        message = "%s %s" % (message, description_data)

        item_dimension = self.get_item_dimension(product)
        if item_dimension:
            message = "%s %s" % (message, item_dimension)

        package_dimension = self.get_package_dimension(product)
        if package_dimension:
            message = "%s %s" % (message, package_dimension)

        amazon_only = "<Amazon-Only>"
        if len(amazon_only) > 14:
            amazon_only = "%s %s" % (amazon_only, "</Amazon-Only>")
            message = "%s %s" % (message, amazon_only)
        message = "%s </Product>" % (message)
        return "<Message>%s</Message>" % (message)

    def get_header(self, instnace):
        """
        This Method relocates prepare header of envelope for amazon product listing.
        :param instnace: This argument relocates instance of amazon.
        :return: This Method return header of envelope for amazon product listing.
        """
        return """<?xml version="1.0"?>
            <AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
            <Header>
                <DocumentVersion>1.01</DocumentVersion>
                <MerchantIdentifier>%s</MerchantIdentifier>
            </Header>
            <MessageType>Product</MessageType>
            <PurgeAndReplace>false</PurgeAndReplace>
         """ % (instnace.merchant_id)

    def standard_product_code(self, product):
        """
        This Method prepare envelope message of standard product type for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return standard product type envelope message for amazon.
        """
        product_code, product_type = '', ''
        if product.standard_product_id_type in ['GTIN']:
            return """<SKU>%s</SKU>""" % product.seller_sku
        if product.standard_product_id_type == 'ASIN':
            product_code, product_type = product.product_asin, 'ASIN'
        elif product.standard_product_id_type == 'EAN':
            product_code, product_type = product.barcode, 'EAN'
        elif product.standard_product_id_type == 'GTIN':
            product_code, product_type = product.product_upc, 'GTIN'
        elif product.standard_product_id_type == 'UPC':
            product_code, product_type = product.product_upc, 'UPC'
        return """<SKU>%s</SKU>
                         <StandardProductID>
                             <Type>%s</Type>
                             <Value>%s</Value>
                         </StandardProductID>
                       """ % (product.seller_sku, product_type, product_code)

    def get_lanuch_date(self, product):
        """
        This Method prepare envelope message of lunch date for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return lunch date envelope message for amazon.
        """
        launch_date = product.launch_date and datetime.strftime(product.launch_date,
                                                                "%Y-%m-%d %H:%M:%S") or False
        return launch_date and " <LaunchDate>%s</LaunchDate>" % (launch_date) or False

    def get_related_product_type(self, product):
        """
        This Method prepare envelope message of related product type for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return related product type envelope message for amazon.
        """
        return """<RelatedProductID>
                          <Type>%s</Type>
                          <Value>%s</Value>
                      </RelatedProductID>""" % ( \
            product.related_product_type, product.related_product_value)

    def get_discontinue_date(self, product):
        """
        This Method prepare envelope message of discontinue date for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return discontinue date envelope message for amazon.
        """
        discontinue_date = product.discontinue_date and datetime.strftime( \
            product.discontinue_date, "%Y-%m-%d %H:%M:%S") or False
        return discontinue_date and " <DiscontinueDate>%s</DiscontinueDate>" % ( \
            discontinue_date) or False

    def get_release_date(self, product):
        """
        This Method prepare envelope message of release date for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return release date envelope message for amazon.
        """
        release_date = product.release_date and datetime.strftime( \
            product.release_date, "%Y-%m-%d %H:%M:%S") or False
        return release_date and " <ReleaseDate>%s</ReleaseDate>" % release_date or False

    def get_item_dimension(self, product):
        """
        This Method prepare envelope message of item dimension for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return item dimension envelope message for amazon.
        """
        if product.item_dimensions_uom:
            return """
                    <ItemDimensions>
                        <Length unitOfMeasure='%s'>%s</Length>
                        <Width unitOfMeasure='%s'>%s</Width>
                        <Height unitOfMeasure='%s'>%s</Height>

                    </ItemDimensions>
                    """ % ( \
                product.item_dimensions_uom, str(round(float(product.item_length), 2)),
                product.item_dimensions_uom, \
                str(round(float(product.item_width), 2)), product.item_dimensions_uom, \
                str(round(float(product.item_width), 2)))

    def get_package_dimension(self, product):
        """
        This Method prepare envelope message of package dimension for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return package dimension envelope message for amazon.
        """
        if product.package_dimensions_uom:
            return """
                    <PackageDimensions>
                        <Length unitOfMeasure='%s'>%s</Length>
                        <Width unitOfMeasure='%s'>%s</Width>
                        <Height unitOfMeasure='%s'>%s</Height>

                    </PackageDimensions>
                    """ % ( \
                product.package_dimensions_uom, str(round(float(product.package_length), 2)),
                product.item_dimensions_uom,
                str(round(float(product.package_width), 2)), product.item_dimensions_uom,
                str(round(float(product.package_width), 2)))

    def get_condition(self, product):
        """
        This Method prepare envelope message of condition for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return condition envelope message for amazon.
        """
        if product.condition:
            return """<Condition>
                            <ConditionType>%s</ConditionType>
                      </Condition>""" % product.condition
        return False

    def item_package_qty_and_no_of_items(self, product):
        """
        This Method prepare envelope message of item package qty and no of items for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return item package qty and no of items envelope message for amazon.
        """
        item_pack = ''
        if product.item_package_qty > 0:
            item_pack = "%s %s" % (
                item_pack,
                "<ItemPackageQuantity>%s</ItemPackageQuantity>" % (product.item_package_qty))
        if product.asin_qty > 0:
            item_pack = "%s %s" % (
                item_pack, "<NumberOfItems>%s</NumberOfItems>" % (product.asin_qty))
        return item_pack

    def get_description_data(self, product):
        """
        This Method prepare envelope message of description data for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return description data envelope message for amazon.
        """
        data = []
        if product.name:
            data.append("<Title>%s</Title>" % (html.escape(product.name)))  # .encode("utf-8")
        else:
            data.append("<Title>%s</Title>" % (
                html.escape(product.name)))  # .encode("utf-8")
        product.brand and data.append(
            "<Brand>%s</Brand>" % (html.escape(product.brand)))  # .encode("utf-8")
        product.designer and data.append(
            "<Designer>%s</Designer>" % (html.escape(product.designer)))  # .encode("utf-8")
        description = product.long_description or False
        description and data.append("<Description>%s</Description>" % (html.escape(description)))

        product.bullet_point_ids and data.append(self.get_bullet_points(product))
        if product.package_weight > 0.0:
            data.append("""<PackageWeight unitOfMeasure='%s'>%s</PackageWeight>""" % (
                product.package_weight_uom, str(round(float(product.package_weight), 2))))

        if product.shipping_weight > 0.0:
            data.append("""<ShippingWeight unitOfMeasure='%s'>%s</ShippingWeight>""" % ( \
                product.shipping_weight_uom, str(round(float(product.shipping_weight), 2))))
        if product.max_order_quantity > 0:
            data.append("<MaxOrderQuantity>%s</MaxOrderQuantity>" % product.max_order_quantity)
        product.manufacturer and data.append(
            "<Manufacturer>%s</>" % (html.escape(product.manufacturer)))
        product.search_term_ids and data.append(self.get_search_terms(product))
        data.append("<IsGiftWrapAvailable>%s</IsGiftWrapAvailable>" % (
            str(product.is_gift_wrap_available).lower()))
        data.append("<IsGiftMessageAvailable>%s</IsGiftMessageAvailable>" % (
            str(product.is_gift_message_available).lower()))

        description_data = ''
        for tag in data:
            description_data = "%s %s" % (description_data, tag)
        return "<DescriptionData>%s</DescriptionData>" % (str(description_data))

    def get_search_terms(self, product):
        """
        This Method prepare envelope message of search term for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return search term envelope message for amazon.
        """
        search_terms = ''
        for search_term in product.search_term_ids:
            search_term = """<SearchTerms>%s</SearchTerms>""" % (html.escape(search_term.name))
            search_terms = "%s %s" % (search_terms, search_term)
        return search_terms

    def get_bullet_points(self, product):
        """
        This Method prepare envelope message of bullet points description for amazon.
        :param product: This arguments relocates product of amazon.
        :return: This method return bullet points description envelope message for amazon.
        """
        bullet_points = ''
        for bullet in product.bullet_point_ids:
            bullet_point = """<BulletPoint>%s</BulletPoint>""" % (html.escape(bullet.name))
            bullet_points = '%s %s' % (bullet_points, bullet_point)
        if product.bullet_point_ids:
            return bullet_points

    def export_stock_levels(self, instance):
        """
        This method used to export product stock to amazon
        """
        warehouse_ids = instance.warehouse_id.ids
        if instance.stock_update_warehouse_ids:
            warehouse_ids += instance.stock_update_warehouse_ids.ids
            warehouse_ids = list(set(warehouse_ids))

        product_ids = self.mapped('product_id')
        amazon_products = self.ids
        self.process_export_stock_message_info_ept( \
            instance, product_ids.ids, amazon_products, warehouse_ids)
        return True

    def export_amazon_stock_levels_operation(self, instance):
        """
        This Method relocates prepare envelop for inventory.
        :return: This Method return Boolean(True/False).
        """
        prod_obj = self.env['product.product']
        from_datetime = instance.inventory_last_sync_on
        company = instance.company_id

        warehouse_ids = instance.warehouse_id.ids
        warehouse_ids += instance.stock_update_warehouse_ids.ids if \
            instance.stock_update_warehouse_ids else []
        warehouse_ids = list(set(warehouse_ids))

        if not from_datetime:
            from_datetime = datetime.today() - timedelta(days=365)
        product_ids = prod_obj.get_products_based_on_movement_date_ept(from_datetime, company)
        amazon_products = self.env['amazon.product.ept'].search([('exported_to_amazon', '=', True),
                                                                 ('instance_id', '=', instance.id),
                                                                 ('fulfillment_by', '=', 'FBM'),
                                                                 ('product_id', 'in', product_ids)])
        product_ids = amazon_products.mapped('product_id')
        self.process_export_stock_message_info_ept( \
            instance, product_ids.ids, amazon_products.ids, warehouse_ids)
        return True

    def process_export_stock_message_info_ept(self, instance, product_ids, amazon_products_ids,
                                              warehouse_ids):
        product_listing_stock = self.check_stock_type(instance, product_ids, warehouse_ids)
        if product_listing_stock:
            message_information = ''
            message_id = 1
            for amazon_product_id in amazon_products_ids:
                amazon_product = self.browse(amazon_product_id)
                stock = product_listing_stock.get(amazon_product.product_id.id)
                message_information = self.prepare_export_stock_level_dict_operation( \
                    amazon_product, instance, stock, message_information, message_id)
                message_id += 1
            if message_information:
                self.process_amazon_export_stock_dict_ept(instance, message_information)
        return True

    def process_amazon_export_stock_dict_ept(self, instance, message_information):
        """
        This method will prepare the message information
        """
        merchant_string = "<MerchantIdentifier>%s</MerchantIdentifier>" % ( \
            instance.merchant_id)
        data = """<?xml version="1.0" encoding="utf-8"?><AmazonEnvelope xmlns:xsi=
        "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
        <Header><DocumentVersion>1.01</DocumentVersion>""" + merchant_string + """</Header>
        <MessageType>Inventory</MessageType>""" + message_information + """</AmazonEnvelope>"""
        kwargs = self.get_amazon_product_request_data_ept(instance, data,
                                                          'export_stock_levels_v13')
        kwargs.update({'marketplaceids': [instance.market_place_id]})

        response = iap_tools.iap_jsonrpc(DEFAULT_ENDPOINT + '/iap_request', params=kwargs)
        self.process_amazon_export_stock_response_ept(instance, data, response)
        return True

    def process_amazon_export_stock_response_ept(self, instance, data, response):
        amazon_process_job_log_obj = self.env['common.log.book.ept']
        amazon_feed_submit_history = self.env['feed.submission.history']

        if response.get('reason'):
            amazon_process_job_log_obj.create({
                'module': 'amazon_ept',
                'type': 'export',
                'res_id': self.id,
                'model_id': self.env['ir.model']._get('amazon.product.ept').id,
                'active': True,
                'log_lines': [(0, 0, {'message': response.get('reason')})]
            })
        else:
            result = response.get('result')
            seller_id = self._context.get('seller_id', False) or instance.seller_id
            if seller_id:
                last_feed_submission_id = result.get('FeedSubmissionInfo', {}).get( \
                    'FeedSubmissionId', {}).get('value', False)
                vals = {'message': data, 'feed_result_id': last_feed_submission_id,
                        'feed_submit_date': time.strftime("%Y-%m-%d %H:%M:%S"),
                        'instance_id': instance.id, 'user_id': self._uid,
                        'feed_type': 'export_stock',
                        'seller_id': instance.seller_id.id}
                amazon_feed_submit_history.create(vals)
        return True

    def check_stock_type(self, instance, product_ids, warehouse_ids):
        """
        This Method relocates check type of stock.
        :param instance: This arguments relocates instance of amazon.
        :param product_ids: This arguments product listing id of odoo.
        :param warehouse_ids:This arguments relocates warehouses of amazon.
        :return: This Method return product listing stock.
        """
        product_listing_stock = []
        prod_obj = self.env['product.product']
        ware_obj = self.env['stock.warehouse']
        if product_ids:
            warehouses = ware_obj.browse(warehouse_ids)
            if instance.stock_field == 'free_qty':
                product_listing_stock = prod_obj.get_free_qty_ept(warehouses, product_ids)
            elif instance.stock_field == 'virtual_available':
                product_listing_stock = prod_obj.get_forecasted_qty_ept(warehouses, product_ids)
        return product_listing_stock

    def prepare_export_stock_level_dict_operation(self, amazon_product, instance, actual_stock,
                                                  message_information, message_id):

        """
        This Method relocates prepare envelope of export stock value.
        :param amazon_product: This arguments relocates product of amazon.
        :param instance: This arguments relocates instance of amazon.
        :param actual_stock : stock
        :param message_information: This arguments relocates message information.
        :param message_id: This arguments relocates message id of amazon envelope.
        :return: This method return message envelope for amazon.
        """
        seller_sku = html.escape(amazon_product['seller_sku'])
        stock = self.stock_ept_calculation(actual_stock,
                                           amazon_product['fix_stock_type'],
                                           amazon_product['fix_stock_value'])

        if amazon_product['allow_package_qty']:
            asin_qty = amazon_product['asin_qty']
            stock = math.floor(stock / asin_qty) if asin_qty > 0.0 else stock

        stock = 0 if int(stock) < 1 else int(stock)
        fullfillment_latency = amazon_product['fulfillment_latency'] or \
                               instance.seller_id.fulfillment_latency
        message_information += """<Message><MessageID>%s</MessageID><OperationType>Update
        </OperationType> <Inventory><SKU>%s</SKU><Quantity>%s</Quantity><FulfillmentLatency>%s
        </FulfillmentLatency> </Inventory></Message>""" % (message_id, seller_sku, stock,
                                                           fullfillment_latency)
        return message_information

    def stock_ept_calculation(self, actual_stock, fix_stock_type=False, fix_stock_value=0):
        """
        This mehod relocates calculate stock.
        :param actual_stock: This arguments relocates actual stock.
        :param fix_stock_type: This arguments relocates type of stock type.
        :param fix_stock_value: This arguments relocates value of stock value.
        :return:
        """
        try:
            if actual_stock >= 1.00:
                if fix_stock_type == 'fix':
                    if fix_stock_value >= actual_stock:
                        return actual_stock
                    return fix_stock_value

                if fix_stock_type == 'percentage':
                    quantity = int((actual_stock * fix_stock_value) / 100.0)
                    if quantity >= actual_stock:
                        return actual_stock
                    return quantity
            return actual_stock
        except Exception as e:
            raise UserError(e)

    def update_price(self, instance):
        """
        This Method relocates create envelope for update price in amazon.
        :param instance: This arguments relocates instance of amazon.
        :return:This Method return boolean(True/False).
        """
        message_id = 1
        message_information = ''
        merchant_string = "<MerchantIdentifier>%s</MerchantIdentifier>" % instance.merchant_id
        message_type = """<MessageType>Price</MessageType>"""
        for amazon_products in self:
            message_information = self.update_price_dict(instance, amazon_products,
                                                         message_information, message_id)
            message_id += 1
        if message_information:
            data = """<?xml version="1.0" encoding="utf-8"?><AmazonEnvelope xmlns:xsi=
            "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
            <Header><DocumentVersion>1.01</DocumentVersion>""" + merchant_string + """
            </Header>""" + message_type + """""" + message_information + """</AmazonEnvelope>"""
            kwargs = self.get_amazon_product_request_data_ept(instance, data,
                                                              'update_price_v13')
            kwargs.update({'marketplaceids': [instance.market_place_id]})
            response = iap_tools.iap_jsonrpc(DEFAULT_ENDPOINT + '/iap_request', params=kwargs)
            if response.get('reason'):
                raise UserError(_(response.get('reason')))
            results = response.get('result')
            self.process_amazon_update_price_result(instance, data, results)
        return True

    def process_amazon_update_price_result(self, instance, data, results):
        amazon_feed_submit_history = self.env['feed.submission.history']

        if results.get('FeedSubmissionInfo', {}).get('FeedSubmissionId', \
                                                     {}).get('value', False):
            last_feed_submission_id = results.get('FeedSubmissionInfo', {}).get( \
                'FeedSubmissionId', {}).get('value', False)
            self.write({'last_feed_submission_id': last_feed_submission_id,
                        'error_in_export_price': False})
            vals = {'message': data, 'feed_result_id': last_feed_submission_id,
                    'feed_submit_date': time.strftime("%Y-%m-%d %H:%M:%S"),
                    'instance_id': instance.id, 'user_id': self._uid,
                    'feed_type': 'export_price',
                    'seller_id': instance.seller_id.id}
            amazon_feed_submit_history.create(vals)
        return True

    def update_price_dict(self, instance, amazon_product, message_information, message_id):
        """
        This Method relocates Prepare price dictionary for amazon.
        :param instance: This arguments relocates instance of amazon.
        :param amazon_product: This arguments relocates product listing of amazon.
        :param message_information: This arguments prepare message envelope of amazon.
        :param message_id: This arguments relocates message of amazon.
        :return:This Method return envelope message of amazon.
        """
        price = instance.pricelist_id.get_product_price_ept(amazon_product.product_id)
        price = price and round(price, 2) or 0.0
        seller_sku = html.escape(amazon_product.seller_sku)
        price_string = """<Message><MessageID>%(message_id)s</MessageID><Price><SKU>%(sku)s</SKU>
        <StandardPrice currency="%(currency)s">%(price)s</StandardPrice></Price></Message>"""
        price_string = price_string % {'currency': instance.pricelist_id.currency_id.name,
                                       'message_id': message_id, 'sku': seller_sku, 'price': price}
        message_information += price_string
        return message_information

    def update_images(self, instance):
        """
        This Method relocates prepare image envelope for amazon.
        :param instance: This arguments relocates instance of amazon.
        :return: This Method return boolean(True/False).
        """
        message_id = 1
        merchant_string = "<MerchantIdentifier>%s</MerchantIdentifier>" % (instance.merchant_id)
        message_information = ''
        for amazon_product in self:
            if not amazon_product.exported_to_amazon:
                continue
            for image_obj in amazon_product.product_id.ept_image_ids:
                message_information = self.create_image_dict(amazon_product, image_obj,
                                                             message_information, message_id)
                message_id += 1
        if message_information:
            data = """<?xml version="1.0" encoding="utf-8"?>
            <AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="amzn-envelope.xsd"><Header><DocumentVersion>1.01
            </DocumentVersion>""" + merchant_string + """</Header><MessageType>
            ProductImage</MessageType>""" + message_information + """</AmazonEnvelope>"""
            kwargs = self.get_amazon_product_request_data_ept(instance, data, 'update_images_v13')
            kwargs.update({'marketplaceids': [instance.market_place_id]})

            response = iap_tools.iap_jsonrpc(DEFAULT_ENDPOINT + '/iap_request', params=kwargs)
            if response.get('reason'):
                raise UserError(_(response.get('reason')))
            results = response.get('result')
            self.process_amazon_update_image_result(instance, results, data)

        return True

    def process_amazon_update_image_result(self, instance, results, data):
        amazon_process_job_log_obj = self.env['common.log.book.ept']
        feed_submission_obj = self.env['feed.submission.history']

        if results.get('FeedSubmissionInfo', {}).get('FeedSubmissionId', {}).get('value', False):
            last_feed_submission_id = results.get('FeedSubmissionInfo', {}).get( \
                'FeedSubmissionId', {}).get('value', False)
            self.write({'error_in_export_image': False,
                        'last_feed_submission_id': last_feed_submission_id})

            vals = {'message': data, 'feed_result_id': last_feed_submission_id,
                    'feed_submit_date': time.strftime("%Y-%m-%d %H:%M:%S"),
                    'instance_id': instance.id, 'user_id': self._uid,
                    'feed_type': 'export_image',
                    'seller_id': instance.seller_id.id}
            feed = feed_submission_obj.create(vals)
            amazon_process_job_log_obj.create({
                'module': 'amazon_ept',
                'type': 'import',
                'res_id': self.id,
                'model_id': self.env['ir.model']._get('amazon.product.ept').id,
                'active': True,
                'log_lines': [(0, 0, {'message': 'Requested Feed Id' + feed and feed.id or False})]
            })
        return True

    def create_image_dict(self, amazon_product, image_obj, message_information, message_id):
        """
        This Method relocates prepare image envelope for amazon.
        :param amazon_product: This arguments relocates product listing of amazon.
        :param image_obj: This arguments relocates image object of amazon.
        :param message_information: This arguments prepare message envelope of amazon.
        :param message_id:This arguments relocates message of amazon.
        :return: This Method return envelope message of amazon.
        """
        seller_sku = amazon_product.seller_sku
        amazon_image_type = 'Main'
        amazon_image_url = image_obj.url

        message_information += """<Message><MessageID>%s</MessageID><OperationType>Update
        </OperationType><ProductImage><SKU>%s</SKU><ImageType>%s</ImageType><ImageLocation>%s
        </ImageLocation></ProductImage></Message>""" % ( \
            message_id, seller_sku, amazon_image_type, amazon_image_url)
        return message_information

    def name_get(self):
        """
        Use: Display Product title
        Added By: Dhaval Sanghani [@Emipro Technologies]
        Added On: 17-Jun-2020
        @param: {}
        @return: {}
        """
        if self._context.get('show_product_title', False):
            res = []
            for product in self:
                name = product.name or False
                res.append((product.id, name))
            return res
        return super(AmazonProductEpt, self).name_get()


import schedule as scd
import time
import rest.client as cl
import plotly.graph_objects as go
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as dates
import mpl_finance as mpf
import datetime
import matplotlib.dates as dates
import matplotlib.cm as cm

class FTXTrader:
    """
    FTX Trader class.

    Stores the trading variables and parameters
    """

    # Initialize variables
    def __init__(self, **kwargs):
        self.market = kwargs['market']  # Coin pair to trade
        self.api_key = kwargs['api_key']  # API key for ftx
        self.api_secret = kwargs['api_secret']  # API secret for ftx
        if('subaccount' in kwargs):
            self.subaccount = kwargs['subaccount']
        else:
            self.subaccount = None
        self.window = kwargs['window']  # Window / lookback of data to consider
        # Granularity of data requested from ftx API
        self.resolution = kwargs['resolution']
        self.client = cl.FtxClient(
            api_key=self.api_key, api_secret=self.api_secret,subaccount_name=self.subaccount)  # FTX API Client
        self.state = kwargs['state']  # Initial coin state
        if('demo' in kwargs):
            self.demo = kwargs['demo']  # Boolean to run off the market
        else:
            self.demo = False
        self.currency = {'volatile': self.market.split('/')[0],
                         'stable': self.market.split('/')[1]}  # Dictionary of coin pairs
        self.maximum_loss = kwargs['maximum_loss']  # Minimum roi threshold

        # Initialize graph
        self.last_datum = None
        plt.style.use('dark_background')
        plt.ion()
        self.line_dict = {}
        self.fig = plt.figure()
        self.ax = self.fig.add_subplot(211)
        self.axslope = self.fig.add_subplot(212)

        initial_data =self.client.get_window(self.market,self.resolution,'quarterday')
        initial_data = self.make_timeseries_df(initial_data)
        initial_indicators = self.make_indicators_df(initial_data)
        initial_indicators['index'] = list(range(0,len(initial_indicators)))
        self.x, self.avg_EMA26, self.avg_EMA9, self.ohlc_list = initial_indicators['index'].tolist(), initial_indicators['avg_EMA26'].tolist(),\
             initial_indicators['avg_EMA9'].tolist(), [[date,o,h,l,c] for date,o,h,l,c in initial_indicators[['index','open','high','low','close']].values]
        self.graph_dict= {
            'x':list(range(0,len(initial_indicators))),
            'date':initial_indicators['date'].tolist(),
            'avg_EMA26':initial_indicators['avg_EMA26'].tolist(),
            'avg_EMA9': initial_indicators['avg_EMA9'].tolist(),
            'avg_EMA20':initial_indicators['avg_EMA20'].tolist(),
            #'avg_EMA20_slope':initial_indicators['avg_EMA20_slope'].tolist(),
            'avg_20_bollinger_bottom':initial_indicators['avg_20_bollinger_bottom'].tolist(),
            'avg_20_bollinger':initial_indicators['avg_20_bollinger'].tolist(),
            #'avg_20_bollinger_slope':initial_indicators['avg_20_bollinger_slope'].tolist(),
            'avg_EMAcross(9,26)_slope':initial_indicators['avg_EMAcross(9,26)_slope'].tolist(),
            'avg_EMAcross(9,26)_std_20_bollinger_up_slope':initial_indicators['avg_EMAcross(9,26)_std_20_bollinger_up_slope'].tolist(),
            'avg_EMAcross(9,26)_std_20_bollinger_bottom_slope':initial_indicators['avg_EMAcross(9,26)_std_20_bollinger_bottom_slope'].tolist(),
            'ohlc_list':[[date,o,h,l,c] for date,o,h,l,c in initial_indicators[['index','open','high','low','close']].values]
        }
        self.update_graph()

        if(self.demo):
            self.market_price = 0

        if('last_buy_price' in kwargs):
            # Last price at which a coin was bought
            self.last_buy_price = kwargs['last_buy_price']
        else:
            self.last_buy_price = -1
        if(self.demo):
            if('balance' in kwargs):
                # Initial value for the account balance of the coin pair
                self.balance = kwargs['balance']
            else:
                self.demo = False
                self.balance = self.get_balance()
                self.demo = True

        # Scheduler runs the trading function every dt seconds
        scd.every(kwargs['dt']).seconds.do(self.trading)
        scd.every(kwargs['dt']).seconds.do(self.update_graph)

    def make_timeseries_df(self, data):
        """
        Generates dataframe from data with added mean column
        """

        df = pd.DataFrame(data)
        df['avg'] = df[['open', 'close']].mean(axis=1, skipna=True)

        return df

    def update_graph(self):
        last_row = self.last_datum
        ## Update graph
        self.ax.cla()
        self.axslope.cla()
        if(last_row is not None):
            if(last_row['date'] == self.graph_dict['date'][-1]):
                self.graph_dict['ohlc_list'][-1] = [self.graph_dict['ohlc_list'][-1][0],last_row['open'],last_row['high'],last_row['low'],last_row['close']]
                for key in self.graph_dict:
                    if(key in ['x','ohlc_list','date']):
                        continue
                    self.graph_dict[key][-1] = last_row[key]
            else:
                self.graph_dict['x'].pop(0)
                self.graph_dict['ohlc_list'].pop(0)

                self.graph_dict['x'].append(self.graph_dict['x'][-1]+1)
                self.graph_dict['ohlc_list'].append([self.graph_dict['ohlc_list'][-1][0]+1,last_row['open'],last_row['high'],last_row['low'],last_row['close']])
                for key in self.graph_dict:
                    if(key in ['x','ohlc_list']):
                        continue
                    self.graph_dict[key].append(last_row[key])
                    self.graph_dict[key].pop(0)

        # Update line graph
        colors = cm.rainbow(np.linspace(0,1,len(self.graph_dict)))
        self.line_dict['ohlc_list'] = mpf.candlestick_ohlc(self.ax, self.graph_dict['ohlc_list'], width=0.4, colorup='#77d879', colordown='#db3f3f')
        for color,key in zip(colors,self.graph_dict):
            if(key in ['x','ohlc_list','date']):
                continue
            if key.split('_')[-1]=='slope':
                if key.split('_')[-2]=='up':
                    im = key.split('_')
                    im[-2]='bottom'
                    bot = '_'.join(im)
                    self.line_dict[key] =  self.axslope.fill_between(self.graph_dict['x'],self.graph_dict[bot],self.graph_dict[key],color = 'grey')
                elif key.split('_')[-2]=='bottom':
                    pass
                else:
                    self.line_dict[key] = self.axslope.plot(self.graph_dict['x'], self.graph_dict[key], color = color[:-1],label = key)
            elif key.split('_')[-1]=='bollinger':
                self.line_dict[key] =  self.ax.fill_between(self.graph_dict['x'],self.graph_dict[key+'_bottom'],self.graph_dict[key],color = 'grey')
            elif key.split('_')[-1]=='bottom':
                continue
            else:
                self.line_dict[key] = self.ax.plot(self.graph_dict['x'], self.graph_dict[key], color = color[:-1],label = key)

        self.ax.legend()
        self.ax.set_xlabel('Últimas 6 horas')
        self.ax.set_ylabel(self.market)

        self.axslope.legend()
        self.axslope.set_ylabel('Slope')
        self.axslope.set_xlabel('Últimas 6 horas')

        # Redraw figure
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()

    def make_indicators_df(self,data):
        ### Sets indicators dataframe
        indicators = pd.DataFrame({'date': pd.to_datetime(data['startTime'])})
        # Calculates EMAs and adds them as indicators
        for col in ['open', 'high', 'low', 'close', 'avg']:
            indicators[col] = data[col]
            indicators[col + '_EMA26'] = data[col].ewm(span=26).mean()
            indicators[col + '_EMA20'] = data[col].ewm(span=20).mean()
            indicators[col + '_EMA9'] = data[col].ewm(span=9).mean()
            indicators[col+'_EMAcross(9,26)'] = indicators[col+'_EMA9']-indicators[col+'_EMA26']
            indicators[col+'_std20'] = data[col].rolling(window=20).std()
            indicators[col+'_20_bollinger'] = indicators[col+'_EMA20']+2*indicators[col+'_std20']
            indicators[col+'_20_bollinger_bottom'] = indicators[col+'_EMA20']-2*indicators[col+'_std20']
            #indicators[col+'_EMA20_slope'] = indicators[col+'_EMA20'].diff()
            indicators[col+'_EMAcross(9,26)_slope'] = indicators[col+'_EMAcross(9,26)'].diff()
            indicators[col+'_EMAcross(9,26)_std_slope'] = indicators[col+'_EMAcross(9,26)'].rolling(window=20).std()
            indicators[col+'_EMAcross(9,26)_std_20_bollinger_up_slope'] = indicators[col+'_EMAcross(9,26)_slope'] + 2*indicators[col+'_EMAcross(9,26)_std_slope']
            indicators[col+'_EMAcross(9,26)_std_20_bollinger_bottom_slope'] = indicators[col+'_EMAcross(9,26)_slope'] - 2*indicators[col+'_EMAcross(9,26)_std_slope']
            #indicators[col+'_20_bollinger_slope'] = indicators[col+'_20_bollinger'].diff()

        indicators['market'] = indicators['close']
        return indicators

    def trading(self):
        """
        Trading functions.
        Calculates EMAs, checks for crosses, predicts a state and buys or sells
        """
        try:
            # Gets historical data from a certain time window and resolution
            historical = self.client.get_window(self.market, self.resolution, self.window)

            # Gets data into dataframe from the windowed historical
            data = self.make_timeseries_df(historical)

            ### Sets indicators dataframe
            results = self.make_indicators_df(data)

            # Creates results DataFrame with the EMA indicators
            results['short'] = results['avg_EMA26'] <= results['avg_EMA9'] # Determines times when EMAs cross
            results['separate'] = np.abs(results['avg_EMA9'] - results['avg_EMA26']) > 0.5 # Determines times with differences greater than 0.65 units

            # Gets the last row of the results
            last_row = results.iloc[-1]
            self.last_datum = last_row

            self.market_price = last_row['market']
            roi = (self.market_price-self.last_buy_price) / self.last_buy_price
            zscore = (self.market_price - last_row['avg_EMA20'])/last_row['avg_std20']

            print(f'''{datetime.datetime.today()}''')
            if(self.demo):
                print('##DEMO MODE##')
            print('State: {}'.format(self.state))
            print('Balance: ', self.get_balance())
            print(f'Zscore: {zscore}')
            print('')

            if(self.state == 'volatile'):
                if(roi > 0 and zscore>1.2):
                    self.sell_volatile(self.balance[self.currency['volatile']])
                    self.state = 'stable HODL'

                elif ((not last_row['short']) and last_row['separate']):

                    if(roi < -self.maximum_loss and zscore>-1.2):
                        self.sell_volatile(
                            self.balance[self.currency['volatile']])
                        self.state = 'stable'

            elif self.state == 'stable HODL':

                if ((not last_row['short']) and last_row['separate']):
                    self.state = 'stable'

                if(zscore <-1.7):
                    self.buy_volatile(self.balance[self.currency['stable']])
                    self.state = 'volatile'
            elif self.state == 'stable':

                if((last_row['short'] and last_row['separate'] and zscore < 0.5) or zscore < -1.7 ):
                    self.buy_volatile(self.balance[self.currency['stable']])
                    self.state = 'volatile'
        except Exception as e:
            print('ERROR:',e)

    def get_balance(self):
        if(self.demo):
            return self.balance
        else:
            balances = self.client.get_balances()
            balances = pd.DataFrame(balances)
            bal_volatile = balances[balances['coin']==self.currency['volatile']]
            bal_stable = balances[balances['coin']==self.currency['stable']]
            if(len(bal_volatile)!=0):
                bal_volatile = bal_volatile.loc[0]['free']
            else:
                bal_volatile = 0
            if(len(bal_stable)!=0):
                bal_stable = bal_stable.loc[0]['free']
            else:
                bal_stable = 0
        return {self.currency['volatile']:bal_volatile,self.currency['stable']:bal_stable}

    def buy_volatile(self, stable_amount):
        """
        This function buys the volatile coin and sells all the stable one
        """
        if self.demo:
            buy_intend = stable_amount/self.market_price
            self.get_balance()[self.currency['volatile']] += buy_intend
            self.get_balance()[self.currency['stable']] = 0
            buy_price = stable_amount/buy_intend
        else:
            buy_intend = stable_amount/self.market_price

            order_start = datetime.datetime.today().timestamp()
            order_data = self.client.place_order(self.market,'buy',None,buy_intend,'market')
            time.sleep(0.2)
            history_data = self.client.get_order_history(self.market,'buy','market',order_start)
            order_status = next(order for order in history_data if order['id'] == order_data['id'])

            buy_price = order_status['avgFillPrice']

        print(f'Bought {buy_intend} {self.currency["volatile"]} for {buy_price*buy_intend} {self.currency["stable"]}')
        print(f'({buy_price} {self.market})')
        print(self.get_balance())
        print('')
        self.last_buy_price = buy_price

    def sell_volatile(self, volatile_amount):
        """
        This function sells the volatile coin and buys all the stable one
        """
        if(self.demo):
            sell_intend = volatile_amount
            self.balance[self.currency['stable']] += volatile_amount*self.market_price
            self.balance[self.currency['volatile']] = 0
            sell_price = self.market_price
        else:
            sell_intend = volatile_amount
            order_start = datetime.datetime.today.timestamp()
            order_data = self.client.place_order(self.market,'sell',None,sell_intend,'market')
            time.sleep(0.2)
            history_data = self.client.get_order_history(self.market,'sell','market',order_start)
            order_status = next(order for order in history_data if order['id'] == order_data['id'])
            sell_price = order_status['avgFillPrice']

        print(f'Sold {sell_intend} {self.currency["volatile"]} for {sell_price*sell_intend} {self.currency["stable"]}')
        print(f'({sell_price} {self.market})')
        print(self.balance)
        print('')

from settings import settings_dict

t = FTXTrader(**settings_dict)

while True:
    scd.run_pending()
    plt.pause(1)


from django.conf.urls import url
from . import views
app_name = "art"
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^errorlogin$', views.error, name='error'),
    url(r'^produits$', views.produits_view, name='produits_view'),
    url(r'^logout$', views.logout_view, name='logout_view'),
    url(r'^tunisie$', views.tunisie_view, name='tunisie_view'),
    url(r'^about$', views.about_view, name='about_view'),
    url(r'^contact$', views.contact_view, name='contact_view'),
    url(r'^event$', views.event_view, name='event_view'),
    url(r'^produit/tapis/$', views.cat_view_tapis, name='cat_view_tapis'),
    url(r'^produit/habits/$', views.cat_view_habits, name='cat_view_habits'),
    url(r'^produit/posaique/$', views.cat_view_mosaique, name='cat_view_mosaique'),
    url(r'^produit/bijoux/$', views.cat_view_bijoux, name='cat_view_bijoux'),
    url(r'^produit/cuterie/$', views.cat_view_poterie, name='cat_view_poterie'),
    url(r'^produit/moivre/$', views.cat_view_cuivre, name='cat_view_cuivre'),
    url(r'^produit/ceramique/$', views.cat_view_ceramique, name='cat_view_ceramique'),
    url(r'^produit/tapis/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^produit/habits/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^produit/poterie/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^produit/mosaique/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^produit/bijoux/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^produit/cuivre/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^produit/ceramique/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^produit/(?P<id>\w{0,50})/$', views.single_view, name='single_view'),
    url(r'^savePanier$', views.savePanier, name='savePanier'),
    url(r'^reclamation$', views.reclamation, name='reclamation'),
    url(r'^facture$', views.facture, name='facture'),
]


#!/usr/bin/env python
# coding: utf-8

import MySQLdb as mdb

def writeImage(data):
	fout = open('dump_picture.jpg', 'wb')
	with fout:
		fout.write(data)

con = mdb.connect('localhost', 'org', 'org666', 'orgdb')
with con:
	cur = con.cursor()
	cur.execute("SELECT Data FROM image WHERE Id=1")

	data = cur.fetchone()[0]
	writeImage(data)


#!/usr/bin/python3
# -*- coding: utf-8 -*-
''' Main module'''

import argparse
import curses_io as cio
import line
from interactive_mode import Interactive
import settings
import time

def check_arguments():
    '''Argument parsung duh'''
    parser = argparse.ArgumentParser(description='This is an rythm metre counter app')
    parser.add_argument("-l", "--line", help="Analyse one line of text")
    parser.add_argument("-f", "--file", help="Input file")
    parser.add_argument("-p", "--pattern", help="Describe matching pattern (H|r)")
    parser.add_argument("-m", "--metre", help="Name of the used metre defined in config")
    parser.add_argument("-i", "--interactive", dest='interactive', action='store_true', \
        help="Start interactive mode")
    parser.add_argument("-t", "--test", dest='test', action='store_true', \
        help="Enable test mode")
    return parser.parse_args()

def interactive_mode(expected_pattern):
    ''' Screen handling (TBD)'''
    try:
        stdscr = cio.init_screen()
        inter = Interactive(stdscr, expected_pattern)
        inter.main_loop()
    except Exception as err:
        cio.close_screen()
        print(err)
    finally:
        cio.close_screen()

def one_line_check(oneline, expected_pattern):
        lin = line.Line(oneline, expected_pattern)
        print(lin.get_metre_string())
        print(lin.text)
        print(lin.create_search_pattern())
        if lin.check_pattern():
            print('MATCH')
        else:
            print('nope')

def get_chosen_metre_pattern(args):
    if args.pattern:
        settings.pattern_input = 'pattern'
        return args.pattern
    if args.metre:
        settings.pattern_input = 'metre'
        return settings.config['patterns'][args.metre]
    return ''

def main():
    ''' Main function'''
    args = check_arguments()
    pattern = get_chosen_metre_pattern(args)
    if args.line:
        settings.mode = 'oneline'
        if args.test:
            settings.mode = 'interactive'
        one_line_check(args.line, pattern.split('X')[0])
    if args.interactive:
        settings.mode = 'interactive'
        interactive_mode(pattern.split('X'))

if __name__ == '__main__':
    main()


"""cache_to_disk: Cache the results of functions persistently on disk

Original Work, Copyright (c) 2018 Stewart Renehan, MIT License
    Author: https://github.com/sarenehan
    Project: https://github.com/sarenehan/cache_to_disk

Modifications:
    Author: https://github.com/mzpqnxow
    Project: https://github.com/mzpqnxow/cache_to_disk/tree/feature/nocache

    This modified version adds the following:
        - Accounting of hits, misses and nocache events
        - cache_info(), cache_clear(), cache_size(), cache_get_raw() interfaces accessible
          via the function itself for convenience
        - NoCacheCondition exception, simple interface for a user to prevent a
          specific function result to not be cached, while still passing a return
          value to the caller
        - Minor refactoring of the decorator, for easier reading
        - Minor refactoring of delete_old_disk_caches(), to reduce logical blocks
          and depth of indentation
        - Default cache age value (DEFAULT_CACHE_AGE)
        - Special unlimited age value (UNLIMITED_CACHE_AGE)
        - Use of logging module (but defaulting to NullAdapter)
        - Minor PEP8 / cosmetic changes
        - Minor cosmetic changes to file path generation (use of os.path.join, a constant
          for the directory/file path)
        - Support getting cache directory or filename from environment:
            Cache metadata filename: $DISK_CACHE_FILENAME
            Base directory for cache files: $DISK_CACHE_DIR
        - Expansion of shell variables and tilde-user values for directories/files
"""
# Standard Library
import json
import logging
import os
import pickle
import warnings
from collections import namedtuple
from copy import deepcopy
from datetime import datetime
from os import getenv
from os.path import (
    dirname,
    exists as file_exists,
    expanduser, expandvars, getmtime,
    isfile,
    join as join_path,
    realpath)

logger = logging.getLogger(__name__)

if logger.handlers is None:
    # Don't log unless user explicitly adds a handler
    logger.addHandler(logging.NullHandler())

MAX_PICKLE_BYTES = 2 ** 31 - 1
DISK_CACHE_DIR = expanduser(expandvars(
    getenv('DISK_CACHE_DIR', join_path(dirname(realpath(__file__)), 'disk_cache'))))
DISK_CACHE_FILE = expanduser(expandvars(join_path(
    DISK_CACHE_DIR, getenv('DISK_CACHE_FILENAME', 'cache_to_disk_caches.json'))))

# Specify 0 for cache age days to keep forever; not recommended for obvious reasons
UNLIMITED_CACHE_AGE = 0
DEFAULT_CACHE_AGE = 7

_TOTAL_NUMCACHE_KEY = 'total_number_of_cache_to_disks'

# Run-time cache data, stolen from Python functools.lru_cache implementation
# Events resulting in nocache are cache misses that complete, but instruct cache_to_disk to
# not store the result. Useful, for example, in a function that makes a network request and
# experiences a failure that is considered likely to be temporary. This is accomplished in
# the user function by raising NoCacheCondition
_CacheInfo = namedtuple('CacheInfo', ['hits', 'misses', 'nocache'])

# This is probably unnecessary ...
# logger.debug('cache_to_disk package loaded; using DISK_CACHE_DIR=%s',
#             os.path.relpath(DISK_CACHE_DIR, '.'))

class NoCacheCondition(Exception):
    """Custom exception for user function to raise to prevent caching on a per-call basis

    The function_value kwarg can be set as a kwarg to return a value other than None to the
    original caller

    Example
    -------
    The following contrived example will return a value to the caller but avoids it being
    cached. In this example, a socket exception is considered a failure, but there is some
    value in returning a partial response to the caller in cases such as SIGPIPE/EPIPE in
    the read loop

    On a socket exception, the function will effectively return either an empty bytes
    buffer or a bytes buffer with partial response data, depending on where the network
    exception occurred

    @cache_to_disk(7)
    def network_query(hostname, port, query):
        response = b''
        try:
            socket = tcp_connect(hostname)
            socket.send(query)
            while True:
                # Build the response incrementally
                buf = read_bytes(socket, 1024)
                if buf is None:
                    break
                response += buf
        except socket.error:
            raise NoCacheCondition(function_value=buf)

        return response
    """
    __slots__ = ['function_value']

    def __init__(self, function_value=None):
        self.function_value = function_value
        logger.info('NoCacheCondition caught in cache_to_disk')

def write_cache_file(cache_metadata_dict):
    """Dump an object as JSON to a file"""
    with open(DISK_CACHE_FILE, 'w') as f:
        return json.dump(cache_metadata_dict, f)

def load_cache_metadata_json():
    """Load a JSON file, create it with empty cache structure if it doesn't exist"""
    try:
        with open(DISK_CACHE_FILE, 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        write_cache_file({_TOTAL_NUMCACHE_KEY: 0})
        return {_TOTAL_NUMCACHE_KEY: 0}

def ensure_dir(directory):
    """Create a directory tree if it doesn't already exist"""
    if not file_exists(directory):
        os.makedirs(directory)
        write_cache_file({_TOTAL_NUMCACHE_KEY: 0})

def pickle_big_data(data, file_path):
    """Write a pickled Python object to a file in chunks"""
    bytes_out = pickle.dumps(data, protocol=4)
    with open(file_path, 'wb') as f_out:
        for idx in range(0, len(bytes_out), MAX_PICKLE_BYTES):
            f_out.write(bytes_out[idx:idx + MAX_PICKLE_BYTES])

def unpickle_big_data(file_path):
    """Return a Python object from a file containing pickled data in chunks"""
    try:
        with open(file_path, 'rb') as f:
            return pickle.load(f)
    except Exception:  # noqa, pylint: disable=broad-except
        bytes_in = bytearray(0)
        input_size = os.path.getsize(file_path)
        with open(file_path, 'rb') as f_in:
            for _ in range(0, input_size, MAX_PICKLE_BYTES):
                bytes_in += f_in.read(MAX_PICKLE_BYTES)
        return pickle.loads(bytes_in)

def get_age_of_file(filename, unit='days'):
    """Return relative age of a file as a datetime.timedelta"""
    age = (datetime.today() - datetime.fromtimestamp(getmtime(filename)))
    return getattr(age, unit)

def get_files_in_directory(directory):
    """Return all files in a directory, non-recursive"""
    return [
        f for f in os.listdir(directory) if
        isfile(join_path(directory, f))
    ]

def delete_old_disk_caches():
    cache_metadata = load_cache_metadata_json()
    new_cache_metadata = deepcopy(cache_metadata)
    cache_changed = False
    for function_name, function_caches in cache_metadata.items():
        if function_name == _TOTAL_NUMCACHE_KEY:
            continue
        to_keep = []
        for function_cache in function_caches:
            max_age_days = int(function_cache['max_age_days'])
            file_name = join_path(DISK_CACHE_DIR, function_cache['file_name'])
            if not file_exists(file_name):
                cache_changed = True
                continue
            if not get_age_of_file(file_name) > max_age_days != UNLIMITED_CACHE_AGE:
                to_keep.append(function_cache)
                continue
            logger.info('Removing stale cache file %s, > %d days', file_name, max_age_days)
            cache_changed = True
            os.remove(file_name)
        if to_keep:
            new_cache_metadata[function_name] = to_keep
    if cache_changed:
        write_cache_file(new_cache_metadata)

def get_disk_cache_for_function(function_name):
    cache_metadata = load_cache_metadata_json()
    return cache_metadata.get(function_name, None)

def get_disk_cache_size_for_function(function_name):
    """Return the current number of entries in the cache for a function by name"""
    function_cache = get_disk_cache_for_function(function_name)
    return None if function_cache is None else len(function_cache)

def delete_disk_caches_for_function(function_name):
    logger.debug('Removing cache entries for %s', function_name)
    n_deleted = 0
    cache_metadata = load_cache_metadata_json()
    if function_name not in cache_metadata:
        return

    functions_to_delete_cache_for = cache_metadata.pop(function_name)
    for function_cache in functions_to_delete_cache_for:
        file_name = join_path(DISK_CACHE_DIR, function_cache['file_name'])
        os.remove(file_name)
        n_deleted += 1
    logger.debug('Removed %s cache entries for %s', n_deleted, function_name)
    write_cache_file(cache_metadata)

def cache_exists(cache_metadata, function_name, *args, **kwargs):
    if function_name not in cache_metadata:
        return False, None
    new_caches_for_function = []
    cache_changed = False
    for function_cache in cache_metadata[function_name]:
        if function_cache['args'] == str(args) and (
                function_cache['kwargs'] == str(kwargs)):
            max_age_days = int(function_cache['max_age_days'])
            file_name = join_path(DISK_CACHE_DIR, function_cache['file_name'])
            if file_exists(file_name):
                if get_age_of_file(file_name) > max_age_days != UNLIMITED_CACHE_AGE:
                    os.remove(file_name)
                    cache_changed = True
                else:
                    function_value = unpickle_big_data(file_name)
                    return True, function_value
            else:
                cache_changed = True
        else:
            new_caches_for_function.append(function_cache)
    if cache_changed:
        if new_caches_for_function:
            cache_metadata[function_name] = new_caches_for_function
        else:
            cache_metadata.pop(function_name)
        write_cache_file(cache_metadata)
    return False, None

def cache_function_value(
        function_value,
        n_days_to_cache,
        cache_metadata,
        function_name,
        *args,
        **kwargs):
    if function_name == _TOTAL_NUMCACHE_KEY:
        raise Exception(
            'Cant cache function named %s' % _TOTAL_NUMCACHE_KEY)
    function_caches = cache_metadata.get(function_name, [])
    new_file_name = str(int(cache_metadata[_TOTAL_NUMCACHE_KEY]) + 1) + '.pkl'
    new_cache = {
        'args': str(args),
        'kwargs': str(kwargs),
        'file_name': new_file_name,
        'max_age_days': n_days_to_cache
    }
    pickle_big_data(function_value, join_path(DISK_CACHE_DIR, new_file_name))
    function_caches.append(new_cache)
    cache_metadata[function_name] = function_caches
    cache_metadata[_TOTAL_NUMCACHE_KEY] = int(cache_metadata[_TOTAL_NUMCACHE_KEY]) + 1
    write_cache_file(cache_metadata)

def cache_to_disk(n_days_to_cache=DEFAULT_CACHE_AGE):
    """Cache to disk"""
    if n_days_to_cache == UNLIMITED_CACHE_AGE:
        warnings.warn('Using an unlimited age cache is not recommended', stacklevel=3)
    if isinstance(n_days_to_cache, int):
        if n_days_to_cache < 0:
            n_days_to_cache = 0
    elif n_days_to_cache is not None:
        raise TypeError('Expected n_days_to_cache to be an integer or None')

    def decorating_function(original_function):
        wrapper = _cache_to_disk_wrapper(original_function, n_days_to_cache, _CacheInfo)
        return wrapper

    return decorating_function

def _cache_to_disk_wrapper(original_func, n_days_to_cache, _CacheInfo):  # noqa, pylint: disable=invalid-name
    hits = misses = nocache = 0

    def wrapper(*args, **kwargs):
        nonlocal hits, misses, nocache
        cache_metadata = load_cache_metadata_json()
        already_cached, function_value = cache_exists(
            cache_metadata, original_func.__name__, *args, **kwargs)
        if already_cached:
            logger.debug('Cache HIT on %s (hits=%s, misses=%s, nocache=%s)',
                         original_func.__name__, hits, misses, nocache)
            hits += 1
            return function_value

        logger.debug('Cache MISS on %s (hits=%s, misses=%s, nocache=%s)',
                     original_func.__name__, hits, misses, nocache)
        logger.debug(' -- MISS ARGS:    (%s)', ','.join(
            [str(arg) for arg in args]))
        logger.debug(' -- MISS KWARGS:  (%s)', ','.join(
            ['{}={}'.format(str(k), str(v)) for k, v in kwargs.items()]))
        misses += 1

        try:
            function_value = original_func(*args, **kwargs)
        except NoCacheCondition as err:
            nocache += 1
            logger.debug('%s() threw NoCacheCondition exception; no new cache entry', original_func.__name__)
            function_value = err.function_value
        else:
            logger.debug('%s() returned, adding cache entry', original_func.__name__)
            cache_function_value(
                function_value,
                n_days_to_cache,
                cache_metadata,
                original_func.__name__,
                *args,
                **kwargs)
        return function_value

    def cache_info():
        """Report runtime cache statistics"""
        return _CacheInfo(hits, misses, nocache)

    def cache_clear():
        """Clear the cache permanently from disk for this function"""
        logger.info('Cache clear requested for %s(); %s items in cache ...', original_func.__name__, )
        delete_disk_caches_for_function(original_func.__name__)

    def cache_size():
        """Return the number of cached entries for this function"""
        return get_disk_cache_size_for_function(original_func.__name__)

    def cache_get_raw():
        """Return the raw cache object for this function as a list of dicts"""
        warnings.warn('This is an internal interface and should not be used lightly', stacklevel=3)
        return get_disk_cache_for_function(original_func.__name__)

    wrapper.cache_info = cache_info
    wrapper.cache_clear = cache_clear
    wrapper.cache_size = cache_size
    wrapper.cache_get_raw = cache_get_raw
    return wrapper

ensure_dir(DISK_CACHE_DIR)
delete_old_disk_caches()


# PE11 :
# Create a database to track your daily exercise. It should have columns to track date,
# time, activity, and duration.

# Populate your database with some entries

# Print just the date and activity columns of your database

# Print selected records from your database (i.e. activities where you got more than 30 minutes of exercise)
# Delete a row from your database.

import sqlite3                                  #Import SQL

conn = sqlite3.connect('daily.sqlite3')         #Make a connection to  Database FILE
cur = conn.cursor()                             # Perform operations on the data stored

cur.execute('DROP TABLE IF EXISTS Exercise')        # REMOVE Exercise if it exist in database to use information.
cur.execute('CREATE TABLE Exercise(Day Text,Activity Text, Time Text, Duration Integer, Date Text)')    #Create a table

cur.execute('INSERT INTO Exercise (Day,Activity,Time,Duration,Date) VALUES (?,?,?,?,?)',('Day 1 ','Cardio','10:45pm',28,'7/2/15'))
cur.execute('INSERT INTO Exercise (Day,Activity,Time,Duration,Date) VALUES (?,?,?,?,?)',('Day 2 ','Abs','8:05am',45,'7/4/15'))
cur.execute('INSERT INTO Exercise (Day,Activity,Time,Duration,Date) VALUES (?,?,?,?,?)',('Day 3 ','Legs','10:30pm',20,'7/6/15'))
cur.execute('INSERT INTO Exercise (Day,Activity,Time,Duration,Date) VALUES (?,?,?,?,?)',('Day 4 ','Back','1:05pm',39,'7/6/15'))
cur.execute('INSERT INTO Exercise (Day,Activity,Time,Duration,Date) VALUES (?,?,?,?,?)',('Day 5 ','Arms','9:10am',20,'7/9/15'))

########### ^^^^^^^^^^ Popluating INFORMATION ^^^^^^^^^^^^^^

conn.commit()   # Update database

print ('Exercise:')

cur.execute('SELECT Day,Activity FROM Exercise')  # Print the day and activity

for row in cur:             # Sort Through Database
    print (row)

conn.commit()   # Update database

cur.execute('DELETE FROM Exercise WHERE Duration > 30')      # Delete from the database where the duration is longer then 30 mins
conn.commit()

cur.execute('SELECT Day,Activity,Time,Duration,Date FROM Exercise')         # Print Selected Table after

print("")
for row in cur:
    print (row)

cur.close()             # Close File AND Database


# Brian Blaylock
# 8 December 2016                              Day of the Horel Christmas Party

"""
Peter Sinks rim and valley temperatures
"""
import matplotlib.pyplot as plt
style_path = '/uufs/chpc.utah.edu/common/home/u0553130/pyBKB_v2/BB_mplstyle/'
style_path = 'B:/pyBKB_v2/BB_mplstyle/'
plt.style.use([style_path+'publications.mplstyle',
                   style_path+'width_50.mplstyle',
                   style_path+'dpi_medium.mplstyle']
                   )
from matplotlib.dates import DateFormatter
from datetime import datetime
import numpy as np
import sys
sys.path.append('B:/pyBKB_v2')
sys.path.append('/uufs/chpc.utah.edu/common/home/u0553130/pyBKB_v2')

from BB_MesoWest.MesoWest_climo import get_mesowest_climatology

import matplotlib.pyplot as plt

MONTH = np.array([])
AVG_Temp = np.array([])
MAX_Temp = np.array([])
MIN_Temp = np.array([])

station = 'PSINK'

months = np.arange(1, 13)
for m in months:
    start = '%02d010000' % (m)
    end = '%02d280000' % (m)

    a = get_mesowest_climatology(station, start, end)
    a['air_temp'][a['air_temp']<-100] = np.nan
    MONTH = np.append(MONTH, m)

    avg_temp = np.nanmean(a['air_temp'])
    AVG_Temp = np.append(AVG_Temp, avg_temp)
    MAX_Temp = np.append(MAX_Temp, np.nanmax(a['air_temp']))
    MIN_Temp = np.append(MIN_Temp, np.nanmin(a['air_temp']))

plt.plot(MONTH, MAX_Temp, label='Max')
plt.plot(MONTH, AVG_Temp, label='Mean')
plt.plot(MONTH, MIN_Temp, label='Min')
plt.title(station
            + "\n"
            + a['DATETIME'][0].strftime('%b %Y')
            + ' to '
            + a['DATETIME'][-1].strftime('%b %Y'))
plt.xlabel('Month')
plt.ylabel('Temperature (C)')
plt.legend()
plt.xlim([1, 12])

plt.savefig(station + '_'
            + a['DATETIME'][0].strftime('%b %Y') + '_'
            + a['DATETIME'][-1].strftime('%b %Y'))


import requests
from bs4 import BeautifulSoup
import re

from checkGE import CheckGE
from alternativeName import AlternativeName

def GetIDAndName(item):
    url = "https://www.runelocus.com/tools/rs-item-id-list/?search="+str(item)
    source = requests.get(url).text
    soup = BeautifulSoup(source, 'lxml')
    data = html_filter(soup)
    readData = interpretData(data)
    return readData

def html_filter(soup):
    item_id_and_name = []
    soup = soup.find('body')
    soup = soup.find('section',class_="main-wrapper")
    soup = soup.find('table')
    for item in soup.findAll('tr'):
        item_id_and_name.append(item.text)

    item_id_and_name = item_id_and_name[1:]
    return item_id_and_name

def interpretData(data):
    item_id_list = []
    item_name_list = []
    for item in data:
        match = splitIDAndName(item)
        item_id = match.group(1)
        item_name = match.group(2)
        if AlternativeName(item_name) not in item_name_list and CheckGE(item_id):
            item_id_list.append(item_id)
            item_name_list.append(AlternativeName(item_name))

    item_id_and_name_split = [item_id_list,item_name_list]

    return item_id_and_name_split

def splitIDAndName(string):
    match = re.match(r"(\d+)(.*)",string)
    return match