from typing import Union

from lark import Lark
from lark.tree import Tree as LarkTree

from .expression import Expression
from .variable import Variable
from .qm import QM

class Tree:
    """A Logic Tree contains information relating to a logical expression,
    also known as a boolean algebra expression.

    :param expression: The logical expression to load into the Tree
    """

    BOOLEAN = Lark(
        """
        start: orexpr
        ?orexpr: (orexpr ("+" | "|" ~ 1..2 | "or" | "OR"))? andexpr
        ?andexpr: (andexpr ("*" | "&" ~ 1..2 | "and" | "AND"))? xorexpr
        ?xorexpr: (xorexpr ("^" | "xor" | "XOR"))? xnorexpr
        ?xnorexpr: (xnorexpr ("-^" | "xnor" | "XNOR"))? norexpr
        ?norexpr: (norexpr ("-+" | "nor" | "NOR"))? nandexpr
        ?nandexpr: (nandexpr ("-*" | "nand" | "NAND"))? term
        ?term: nexpr
            | pexpr
            | IDENT
        ?nexpr: TILDE orexpr
        ?pexpr: "(" orexpr ")"
            | "[" orexpr "]"
        TILDE: "~" | "!" | "not" | "NOT"

        %import common.CNAME -> IDENT
        %import common.WS
        %ignore WS
        """
    )

    @staticmethod
    def __create_dict(parse_tree):
        """Creates a parsable JSON object that can be parsed through LogicNode and LogicVar
        objects to be used to represent a boolean expression

        :param parse_tree: A Lark grammar tree object to parse through
        :returns: A tuple containing the parse expression and a list of variables in the expression
            The first entry in the tuple is the parsed expression
            The second entry in the tuple is a list of variables in the expression
        """

        variables = []

        # Check if the parse_tree is a LarkTree
        if isinstance(parse_tree, LarkTree):

            # Check if the expression is an orexpr (OR) or andexpr (AND)
            if parse_tree.data != "nexpr":

                # Get the left and right expression along with any new variables that may exist
                left, variables_new_left = Tree.__create_dict(parse_tree.children[0])
                right, variables_new_right = Tree.__create_dict(parse_tree.children[1])
                for variable in variables_new_left + variables_new_right:
                    if variable not in variables:
                        variables.append(variable)

                # Return the expression and the variables
                return {
                    "operator": parse_tree.data[ : parse_tree.data.find("expr")].upper(),
                    "left": left,
                    "right": right,
                    "has_not": False
                }, variables

            # Check if the expression is an nexpr (NOT)
            else:

                # Get the expression along with any new variables that may exist
                expression, variables_new = Tree.__create_dict(parse_tree.children[1])
                expression["has_not"] = not expression["has_not"]
                for variable in variables_new:
                    if variable not in variables:
                        variables.append(variable)

                # Return the expression and the variables
                return expression, variables

        # The parse_tree is an ident (Variable)
        return {
            "variable": parse_tree.value,
            "has_not": False
        }, [parse_tree.value]

    # # # # # # # # # # # # # # # # # # # #

    def __init__(self, expression: str):

        # Try to parse the expression
        try:
            expression, self.__variables = Tree.__create_dict(
                Tree.BOOLEAN.parse(expression).children[0] # This ignores the "start" Tree
            )
            self.__variables.sort()

            # Check if the expression is a LogicNode or LogicVar
            if "variable" in expression:
                self.__root = Variable(json = expression)
            else:
                self.__root = Expression(json = expression)

        # If parsing the expression fails, the boolean expression is invalid
        except:
            raise ValueError("The expression given is invalid")

    def __str__(self) -> str:
        """Returns the string representation of this Tree"""
        return str(self.root)

    # # # # # # # # # # # # # # # # # # # #

    @property
    def root(self) -> Union[Expression, Variable]:
        """Returns the root of this Tree"""
        return self.__root

    @property
    def variables(self) -> list:
        """Returns the list of variables in this Tree"""
        return self.__variables

    # # # # # # # # # # # # # # # # # # # #

    def functional(self) -> str:
        """Returns a functional representation of this Expression"""
        return self.root.functional()

    def evaluate(self) -> bool:
        """Evaluates the root of this tree to get values for where the root
        evaluates to True and False (1 and 0)

        :returns: A list of evaluations and their truth values that make
            up the evaluation
        """

        # Iterate through all the integer values from 2 ** len(variables)
        evaluations = []
        for binary in range(2 ** len(self.variables)):

            # Create a dict for each variable and whether or not this variable
            #   is true at the current binary value
            truth_values = {}
            for i in range(len(self.variables)):
                key = self.variables[i]
                shift_amount = len(self.variables) - i - 1
                value = (binary & (1 << shift_amount)) != 0
                truth_values[key] = value

            # Add the evaluation for this binary value to the evaluations
            evaluations.append({
                "truth_values": truth_values,
                "truth_value": self.root.evaluate(truth_values)
            })
        return evaluations

    def get_table(self, *, as_list = False) -> Union[str, list]:
        """Creates a truth table out of the root node

        :param as_list: Whether or not to retrieve the table as
            a list of rows

        :returns: A string containing the variables and where the root expression
            evaluates to True and False (1 and 0, respectively)
            or A list of rows for the truth table
        """

        # Create the header row
        #   which holds the variables and the result like the following:
        #   | a | b | c | (a * b) + c |
        header = "| {} | {} |".format(
            " | ".join(self.variables),
            str(self)
        )

        # Create the separator row
        #   which contains proper separation characters to create a more
        #   tabular look like the following:
        #   +---+---+---+-------------+
        separator = "+-{}-+-{}-+".format(
            "-+-".join([
                "-" * len(variable)
                for variable in self.variables
            ]),
            "-" * len(str(self))
        )

        # Create the truth values and their evaluations
        #   which contains each boolean value and the final evaluation:
        #   | 0 | 1 | 0 |      0      |
        evaluations = self.evaluate()
        values = "\n".join([
            "| {} | {} |".format(
                " | ".join([
                    ("1" if evaluation["truth_values"][value] else "0").center(len(value))
                    for value in evaluation["truth_values"]
                ]),
                ("1" if evaluation["truth_value"] else "0").center(len(str(self)))
            )
            for evaluation in evaluations
        ])

        if as_list:
            return [
                header, separator, values
            ]
        return "{}\n{}\n{}".format(header, separator, values)

    def simplify(self, *, get_minterm=None) -> 'Tree':
        """Simplifies the boolean expression at the root
        and returns the most simplified expression obtained from either minterm or maxterm
        evaluation

        :param get_minterm: Whether or not to get the Minterm or Maxterm of this Tree
            By default, this will return the shortest one between the two.
        :returns: The simplifed boolean expression
        """

        # Get the minterm and maxterm true-at values
        #   Note that a minterm expression is true where the expression evaluates
        #   to True (1) and a maxterm expression is true where the expression evaluates
        #   to False (0)
        evaluations = self.evaluate()
        true_at_minterms = [
            decimal
            for decimal in range(len(evaluations))
            if evaluations[decimal]["truth_value"]
        ]
        true_at_maxterms = [
            decimal
            for decimal in range(len(evaluations))
            if not evaluations[decimal]["truth_value"]
        ]

        # Get the minterm and maxterm
        minterm_qm = QM(
            self.variables,
            true_at_minterms
        ).solve()

        maxterm_qm = QM(
            self.variables,
            true_at_maxterms,
            is_maxterm = True
        ).solve()

        # Either get the minterm or the maxterm
        #   if neither is specified, get the shortest one
        if get_minterm is not None:
            if get_minterm:
                return Tree(minterm_qm)
            return Tree(maxterm_qm)
        return Tree(min(minterm_qm, maxterm_qm, key = lambda qm: len(qm)))


# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Lint as: python2, python3
"""Calculation of the Black-Scholes implied volatility via Newton's method."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import tensorflow as tf
import tensorflow_probability as tfp

from tf_quant_finance.volatility.implied_vol import polya_approx

def implied_vol(forwards,
                strikes,
                expiries,
                discount_factors,
                prices,
                option_signs,
                initial_volatilities=None,
                validate_args=True,
                tolerance=1e-8,
                max_iterations=20,
                name=None,
                dtype=None):
  """Finds the implied volatilities of options under the Black Scholes model.

  This method first estimates, using the Radiocic-Polya approximation, the
  volatility of an option implied by observed market prices under the Black
  Scholes model. This estimate is then used to initialise Newton's algorithm for
  locating the root of a differentiable function.

  The implementation assumes that each cell in the supplied tensors corresponds
  to an independent volatility to find.

  #### Examples
  ```python
  forwards = np.array([1.0, 1.0, 1.0, 1.0])
  strikes = np.array([1.0, 2.0, 1.0, 0.5])
  expiries = np.array([1.0, 1.0, 1.0, 1.0])
  discount_factors = np.array([1.0, 1.0, 1.0, 1.0])
  option_signs = np.array([1.0, 1.0, -1.0, -1.0])
  volatilities = np.array([1.0, 1.0, 1.0, 1.0])
  prices = black_scholes.option_price(
      forwards,
      strikes,
      volatilities,
      expiries,
      discount_factors=discount_factors,
      is_call_options=is_call_options)
  implied_vols = newton_vol.implied_vol(forwards,
                                        strikes,
                                        expiries,
                                        discount_factors,
                                        prices,
                                        option_signs)
  with tf.Session() as session:
    print(session.run(implied_vols[0]))
  # Expected output:
  # [ 1.  1.  1.  1.]

  Args:
    forwards: A real `Tensor`. The current forward prices to expiry.
    strikes: A real `Tensor` of the same shape and dtype as `forwards`. The
      strikes of the options to be priced.
    expiries: A real `Tensor` of same shape and dtype as `forwards`. The expiry
      for each option. The units should be such that `expiry * volatility**2` is
      dimensionless.
    discount_factors: A real `Tensor` of the same shape and dtype as `forwards`.
      The total discount factors to apply.
    prices: A real `Tensor` of the same shape and dtype as `forwards`. The
      observed market prices to match.
    option_signs: A real `Tensor` of same shape and dtype as `forwards`.
      Positive one where option is a call, negative one where option is a put.
    initial_volatilities: A real `Tensor` of the same shape and dtype as
      `forwards`. The starting postions for Newton's method.
      Default value: None. If not supplied, the starting point is chosen using
        the Stefanica-Radoicic scheme. See `polya_approx.implied_vol` for
        details.
    validate_args: A Python bool. If True, indicates that arguments should be
      checked for correctness before performing the computation. The checks
      performed are: (1) Forwards and strikes are positive. (2) The prices
        satisfy the arbitrage bounds (i.e. for call options, checks the
        inequality `max(F-K, 0) <= Price <= F` and for put options, checks that
        `max(K-F, 0) <= Price <= K`.). (3) Checks that the prices are not too
        close to the bounds. It is numerically unstable to compute the implied
        vols from options too far in the money or out of the money.
    tolerance: `float`. The root finder will stop where this tolerance is
      crossed.
    max_iterations: `int`. The maximum number of iterations of Newton's method.
    name: `str`, default "implied_vol", to be prefixed to the name of TensorFlow
      ops created by this function.
    dtype: optional `tf.DType`. If supplied the `forwards`, `strikes`,
      `expiries`, `discounts`, `prices`, `initial_volatilities` and
      `option_signs` will be coerced to this type.

  Returns:
    A three tuple of `Tensor`s, each the same shape as `forwards`. It
    contains the implied volatilities (same dtype as `forwards`), a boolean
    `Tensor` indicating whether the corresponding implied volatility converged,
    and a boolean `Tensor` which is true where the corresponding implied
    volatility is not a finite real number.
  ```
  """
  with tf.compat.v1.name_scope(
      name, "implied_vol",
      [prices, forwards, strikes, expiries, discount_factors, option_signs]):
    forwards = tf.convert_to_tensor(forwards, dtype=dtype, name="forwards")
    strikes = tf.convert_to_tensor(strikes, dtype=dtype, name="strikes")
    expiries = tf.convert_to_tensor(expiries, dtype=dtype, name="expiries")
    discount_factors = tf.convert_to_tensor(
        discount_factors, dtype=dtype, name="discount_factors")
    prices = tf.convert_to_tensor(prices, dtype=dtype, name="prices")
    option_signs = tf.convert_to_tensor(
        option_signs, dtype=dtype, name="option_signs")
    is_call_options = tf.convert_to_tensor(
        option_signs > 0.0, name="is_call_options")
    if initial_volatilities is None:
      initial_volatilities = polya_approx.polya_implied_vol(
          prices,
          forwards,
          strikes,
          expiries,
          discount_factors=discount_factors,
          is_call_options=is_call_options,
          validate_args=validate_args)
    else:
      initial_volatilities = tf.convert_to_tensor(
          initial_volatilities, dtype=dtype, name="initial_volatilities")

    implied_vols, converged, failed = _newton_implied_vol(
        forwards,
        strikes,
        expiries,
        discount_factors,
        prices,
        initial_volatilities,
        option_signs,
        max_iterations=max_iterations,
        tolerance=tolerance)
    return implied_vols, converged, failed

# TODO(b/139566435): Extract the Newton root finder to a separate module.
def newton_root_finder(value_and_grad_func,
                       initial_values,
                       max_iterations=20,
                       tolerance=1e-8,
                       dtype=None,
                       name=None):
  """Uses Newton's method to find roots of scalar functions of scalar variables.

  This method uses Newton's algorithm to find values `x` such that `f(x)=0` for
  some real-valued differentiable function `f`. Given an initial value `x_0` the
  values are iteratively updated as:

    `x_{n+1} = x_n - f(x_n) / f'(x_n),`

  for further details on Newton's method, see [1]. The implementation accepts
  array-like arguments and assumes that each cell corresponds to an independent
  scalar model.

  #### Examples
  ```python
  # Set up the problem of finding the square roots of three numbers.
  constants = np.array([4.0, 9.0, 16.0])
  initial_values = np.ones(len(constants))
  def objective_and_gradient(values):
    objective = values**2 - constants
    gradient = 2.0 * values
    return objective, gradient

  # Obtain and evaluate a tensor containing the roots.
  roots = newton_vol.newton_newton_newton_root_finder(objective_and_gradient,
                                        initial_values)
  with tf.Session() as sess:
    root_values, converged, failed = sess.run(roots)
    print(root_values)  # Expected output: [ 2.  3.  4.]
    print(converged)  # Expected output: [ True  True  True]
    print(failed)  # Expected output: [False False False]
  ```

  #### References
  [1] Luenberger, D.G., 1984. 'Linear and Nonlinear Programming'. Reading, MA:
  Addison-Wesley.

  Args:
    value_and_grad_func: A python callable that takes a `Tensor` of the same
      shape and dtype as the `initial_values` and which returns a two-`tuple` of
      `Tensors`, namely the objective function and the gradient evaluated at the
      passed parameters.
    initial_values: A real `Tensor` of any shape. The initial values of the
      parameters to use (`x_0` in the notation above).
    max_iterations: positive `int`, default 100. The maximum number of
      iterations of Newton's method.
    tolerance: positive scalar `Tensor`, default 1e-8. The root finder will
      judge an element to have converged if `|f(x_n) - a|` is less than
      `tolerance` (using the notation above), or if `x_n` becomes `nan`. When an
      element is judged to have converged it will no longer be updated. If all
      elements converge before `max_iterations` is reached then the root finder
      will return early.
    dtype: optional `tf.DType`. If supplied the `initial_values` will be coerced
      to this data type.
    name: `str`, default "newton_root_finder", to be prefixed to the name of
      TensorFlow ops created by this function.

  Returns:
    A three tuple of `Tensor`s, each the same shape as `initial_values`. It
    contains the found roots (same dtype as `initial_values`), a boolean
    `Tensor` indicating whether the corresponding root results in an objective
    function value less than the tolerance, and a boolean `Tensor` which is true
    where the corresponding 'root' is not finite.
  """
  with tf.compat.v1.name_scope(
      name,
      default_name="newton_root_finder",
      values=[initial_values, tolerance]):

    initial_values = tf.convert_to_tensor(
        initial_values, dtype=dtype, name="initial_values")

    starting_position = (tf.constant(0, dtype=tf.int32), initial_values,
                         tf.zeros_like(initial_values, dtype=tf.bool),
                         tf.is_nan(initial_values))

    def _condition(counter, parameters, converged, failed):
      del parameters
      early_stop = tf.reduce_all(converged | failed)
      return ~((counter >= max_iterations) | early_stop)

    def _updater(counter, parameters, converged, failed):
      """Updates each parameter via Newton's method."""
      values, gradients = value_and_grad_func(parameters)
      converged = tf.abs(values) < tolerance
      # Used to zero out updates to cells that have converged.
      update_mask = tf.cast(~converged, dtype=parameters.dtype)
      increment = -update_mask * values / gradients
      updated_parameters = parameters + increment
      failed = ~tf.is_finite(updated_parameters)

      return counter + 1, updated_parameters, converged, failed

    return tf.while_loop(_condition, _updater, starting_position)[1:]

def _make_black_objective_and_vega_func(prices, forwards, strikes, expiries,
                                        option_signs, discount_factors):
  """Produces an objective and vega function for the Black Scholes model.

  The returned function maps volatilities to a tuple of objective function
  values and their gradients with respect to the volatilities. The objective
  function is the difference between Black Scholes prices and observed market
  prices, whereas the gradient is called vega of the option. That is:

  ```
  g(s) = (f(s) - a, f'(s))
  ```

  Where `g` is the returned function taking volatility parameter `s`, `f` the
  Black Scholes price with all other variables curried and `f'` its derivative,
  and `a` the observed market prices of the options. Hence `g` calculates the
  information necessary for finding the volatility implied by observed market
  prices for options with given terms using first order methods.

  ### References
  [1] Hull, J., 2018. Options, Futures, and Other Derivatives. Harlow, England.
  Pearson. (p.358 - 361)

  Args:
    prices: A real `Tensor` of any shape. The observed market prices of the
      assets.
    forwards: A real `Tensor` of the same shape and dtype as `prices`. The
      current forward prices to expiry.
    strikes: A real `Tensor` of the same shape and dtype as `prices`. The strike
      prices of the options.
    expiries: A real `Tensor` of same shape and dtype as `forwards`. The expiry
      for each option. The units should be such that `expiry * volatility**2` is
      dimensionless.
    option_signs: A real `Tensor` of same shape and dtype as `forwards`.
      Positive one where option is a call, negative one where option is a put.
    discount_factors: A real `Tensor` of the same shape and dtype as `forwards`.
      The total discount factors to apply.

  Returns:
    A function from volatilities to a Black Scholes objective and its
    derivative (which is coincident with Vega).
  """
  dtype = prices.dtype.base_dtype
  phi = tfp.distributions.Normal(
      loc=tf.zeros(1, dtype=dtype), scale=tf.ones(1, dtype=dtype))
  # orientations will decide the normalization strategy.
  orientations = strikes >= forwards
  # normalization is the greater of strikes or forwards
  normalization = tf.where(orientations, strikes, forwards)
  normalized_prices = prices / normalization
  units = tf.ones_like(forwards)
  # y is 1 when strikes >= forwards and strikes/forwards otherwise
  y = tf.where(orientations, units, strikes / forwards)
  # x is forwards/strikes when strikes >= forwards and 1 otherwise
  x = tf.where(orientations, forwards / strikes, units)
  lnz = tf.log(forwards) - tf.log(strikes)
  sqrt_t = tf.sqrt(expiries)

  def _black_objective_and_vega(volatilities):
    """Calculate the Black Scholes price and vega for a given volatility.

    This method returns normalized results.

    Args:
      volatilities: A real `Tensor` of same shape and dtype as `forwards`. The
        volatility to expiry.

    Returns:
      A tuple containing (value, gradient) of the black scholes price, both of
        which are `Tensor`s of the same shape and dtype as `volatilities`.
    """
    v = volatilities * sqrt_t
    d1 = option_signs * (lnz / v + v / 2)
    d2 = option_signs * (option_signs * d1 - v)
    val = option_signs * discount_factors * (
        (x * phi.cdf(d1) - y * phi.cdf(d2))) - normalized_prices
    grad = option_signs * discount_factors * (
        y * phi.prob(d2) * (d1 / v) - x * phi.prob(d1) * (d2 / v))
    return val, grad

  return _black_objective_and_vega

def _newton_implied_vol(forwards,
                        strikes,
                        expiries,
                        discount_factors,
                        prices,
                        initial_volatilities,
                        option_signs,
                        max_iterations=100,
                        tolerance=1e-8,
                        name=None,
                        dtype=None):
  """Uses Newton's method to find Black Scholes implied volatilities of options.

  Finds the volatility implied under the Black Scholes option pricing scheme for
  a set of European options given observed market prices. The implied volatility
  is found via application of Newton's algorithm for locating the root of a
  differentiable function.

  The implmentation assumes that each cell in the supplied tensors corresponds
  to an independent volatility to find.

  #### Examples
  ```python
  forwards = np.array([1.0, 1.0, 1.0, 1.0])
  strikes = np.array([1.0, 2.0, 1.0, 0.5])
  expiries = np.array([1.0, 1.0, 1.0, 1.0])
  discounts = np.array([1.0, 1.0, 1.0, 1.0])
  initial_volatilities = np.array([2.0, 0.5, 2.0, 0.5])
  option_signs = np.array([1.0, 1.0, -1.0, -1.0])
  is_call_options = np.array([True, True, False, False])
  volatilities = np.array([1.0, 1.0, 1.0, 1.0])
  prices = black_scholes.option_price(
      forwards,
      strikes,
      volatilities,
      expiries,
      discount_factors=discounts,
      is_call_options=is_call_options)
  implied_vols, converged, failed = newton_vol.newton_implied_vol(
      forwards,
      strikes,
      expiries,
      discounts,
      prices,
      initial_volatilities,
      option_signs,
      max_iterations=100)
  with tf.Session() as session:
    print(session.run(implied_vols))
  # Expected output:
  # [ 1.  1.  1.  1.]

  Args:
    forwards: A real `Tensor`. The current forward prices to expiry.
    strikes: A real `Tensor` of the same shape and dtype as `forwards`. The
      strikes of the options to be priced.
    expiries: A real `Tensor` of same shape and dtype as `forwards`. The expiry
      for each option. The units should be such that `expiry * volatility**2` is
      dimensionless.
    discount_factors: A real `Tensor` of the same shape and dtype as `forwards`.
      The total discount factors to apply.
    prices: A real `Tensor` of the same shape and dtype as `forwards`. The
      observed market prices to match.
    initial_volatilities: A real `Tensor` of the same shape and dtype as
      `forwards`. The starting postions for Newton's method.
    option_signs: A real `Tensor` of same shape and dtype as `forwards`.
      Positive one where option is a call, negative one where option is a put.
    max_iterations: `int`. The maximum number of iterations of Newton's method.
    tolerance: `float`. The root finder will stop where this tolerance is
      crossed.
    name: `str`, default "newton_implied_vol", to be prefixed to the name of
      TensorFlow ops created by this function.
    dtype: optional `tf.DType`. If supplied the `forwards`, `strikes`,
      `expiries`, `discounts`, `prices`, `initial_volatilities` and
      `option_signs` will be coerced to this type.

  Returns:
    A three tuple of `Tensor`s, each the same shape as `forwards`. It
    contains the implied volatilities (same dtype as `forwards`), a boolean
    `Tensor` indicating whether the corresponding implied volatility converged,
    and a boolean `Tensor` which is true where the corresponding implied
    volatility is not a finite real number.
  ```
  """
  with tf.compat.v1.name_scope(name, "newton_implied_vol", [
      forwards, strikes, expiries, discount_factors, prices,
      initial_volatilities, option_signs, max_iterations, tolerance
  ]):

    forwards = tf.convert_to_tensor(forwards, dtype=dtype, name="forwards")
    strikes = tf.convert_to_tensor(strikes, dtype=dtype, name="strikes")
    expiries = tf.convert_to_tensor(expiries, dtype=dtype, name="expiries")
    discount_factors = tf.convert_to_tensor(
        discount_factors, dtype=dtype, name="discount_factors")
    prices = tf.convert_to_tensor(prices, dtype=dtype, name="prices")
    # discounted_prices = discounts * prices
    initial_volatilities = tf.convert_to_tensor(
        initial_volatilities, dtype=dtype, name="initial_volatilities")
    option_signs = tf.convert_to_tensor(
        option_signs, dtype=dtype, name="option_signs")
    pricer = _make_black_objective_and_vega_func(prices, forwards, strikes,
                                                 expiries, option_signs,
                                                 discount_factors)
    results = newton_root_finder(
        pricer,
        initial_volatilities,
        max_iterations=max_iterations,
        tolerance=tolerance)
    return results


#!/usr/bin/env python

import boto3
import BaseHTTPServer
import ConfigParser
import os
import urllib2
import json
import hashlib
from pprint import pprint

from os import curdir,sep
import sys

client = boto3.client('sts', aws_access_key_id="",aws_secret_access_key="")

class AuthenticationError(Exception):
    pass

class AuthHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_HEAD(s):
        s.send_response(200)
        s.send_header("Content-type", "text/html")
        s.end_headers()
    def do_GET(s):
        global auth_code
        global KEEP_RUNNING
        """Respond to a GET request."""
        if ('code' in s.path):
            KEEP_RUNNING = False
            s.send_response(200)
            s.send_header("Content-type", "text/html")
            s.end_headers()
            s.wfile.write("<title>Authorization successful</title>")
            s.wfile.write("
Successfully authorized
")
            auth_code = s.path
            s.wfile.write("")
        else:
            f = open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'index.html'))
            s.send_response(200)
            s.send_header('Content-type', 'text/html')
            s.end_headers()
            s.wfile.write(f.read())
            f.close()

server_class = BaseHTTPServer.HTTPServer
httpd = server_class(('', 8000), AuthHandler)
KEEP_RUNNING = True

config_file = 'google_aws.cfg'
config = ConfigParser.SafeConfigParser({
            'client_id':''
            })
config.read( [ config_file, os.path.expanduser('~/.google_aws.cfg') ])
if not config.has_section('google'):
    config.add_section('google')

client_id = config.get('google','client_id')

def get_credentials():

    state = hashlib.sha256(os.urandom(1024)).hexdigest()

    auth_url = "https://accounts.google.com/o/oauth2/auth?scope=email&state={1}&redirect_uri=http%3A%2F%2Flocalhost:8000%2F&response_type=id_token%20token&client_id={0}".format(client_id,state)

    sys.stderr.write(auth_url+"\n")

    def keep_running():
        global KEEP_RUNNING
        return KEEP_RUNNING
    try:
        while keep_running():
            httpd.handle_request()
    except KeyboardInterrupt:
        pass

    httpd.server_close()

    code = auth_code
    tokens = code.replace('/code?','').split('&')
    token = None
    access_token = None
    for a_token in tokens:
        token_bits = a_token.split('=')
        if token_bits[0] == 'access_token':
            access_token = token_bits[1]
        if token_bits[0] == 'id_token':
            token = token_bits[1]
        if token_bits[0] == 'state':
            if (token_bits[1] != state):
                raise AuthenticationError('State mismatch expecting {0}, is {1}'.format(state,token_bits[1]))

    req = urllib2.Request("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={0}".format(access_token))
    response = urllib2.urlopen(req)
    user_info = json.load(response)
    if user_info['audience'] != client_id:
        raise AuthenticationError('Mismatching client ids')

    response = client.assume_role_with_web_identity(
        RoleArn=config.get('aws','role'),
        RoleSessionName=user_info['user_id'],
        WebIdentityToken=token,
        DurationSeconds=900
    )

    return(response['Credentials'])

if __name__ == '__main__':
    credentials = get_credentials()
    sys.stdout.write('AWS_ACCESS_KEY_ID={0} AWS_SECRET_ACCESS_KEY={1} AWS_SESSION_TOKEN={2} AWS_SECURITY_TOKEN={2}\n'.format(credentials['AccessKeyId'],credentials['SecretAccessKey'],credentials['SessionToken']))


def Binarizer_new(Y):
    from collections import Counter
    COUNT_before = (Counter(Y))
    for I,J in enumerate(Y):
        if J == 1:
            Y[I] = 0
        elif J==2:
            Y[I] = 1
        elif J==3:
            Y[I] = 1
        else:
            'WE GOT A PROBLEM'
    COUNT_after = (Counter(Y))
    print('before',COUNT_before, '\nafter:',COUNT_after)

Binarizer_new(y_test)


# coding: utf-8

get_ipython().magic(u'matplotlib inline')
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Read dataset Data format:
# date, variation, session id, total minutes watched
dt = pd.read_csv('experiment_granular.csv',sep=',',header=0)
dt.head()

# mean, SD, N per variation
dt_all = dt.groupby('v.variation')
dt_all.describe()

# mean, SD, N per variation after filtering out outliers
no_outl = dt[dt.minute_view_cnt <= 720]
no_outl_var = no_outl.groupby('v.variation')
no_outl_var.describe()

# plotting entire distrubution of min views
no_outl.minute_view_cnt.hist(bins=30)

# Examining distribution in sessions with less than 100 minutes watched
no_outl[(no_outl.minute_view_cnt <= 100)].minute_view_cnt.hist(bins=30)

# Examining sessions with less than 100 minutes watched
# but without the skew due to the ones who didn't watch anything at all
no_outl[(no_outl.minute_view_cnt <= 100) & (no_outl.minute_view_cnt >0)].minute_view_cnt.hist(bins=30)


from flask import request, current_app, jsonify, json
from app.models import *
from app import utils
from . import getData

# 获取所有评论内容
@getData.route("/get_comments")
def get_comments():
    """选择性进行查询评论,不传参数查询所有用户所有课程的评论，
    可以通过nickName和course_id 限制所查询的结果
    url: http://39.108.166.132:8888/get_comments?nickName=xxx&course_id=xxx
    :return 评论的列表，所有学生的所有评论
    """
    current_app.logger.info("get all comments info")
    nickName = request.values.get("nickName")
    course_id = request.values.get("course_id")
    comment_list = list()
    # 查询所有评论
    if nickName is None and course_id is None:
        comments = Comments.query.all()
        for comment in comments:
            user_id = comment.user_id
            user = Users.query.filter_by(id=user_id).first()
            course_id = comment.course_id
            course = Courses.query.filter_by(id=course_id).first()
            comment_info = {
                "id": comment.id,
                "content": comment.content,
                "user_id": user_id,
                "nickName": user.nickName,
                "course_id": course_id,
                "course_name": course.name,
                "course_category": course.category,
                "comment_time": comment.comment_time,
                "photo": user.photo
            }
            comment_list.append(comment_info)
        current_app.logger.info("get all comments ok, data-->%s" % comment_list)

    # 用户名存在 课程为空
    elif nickName and course_id is None:
        user = db.session.query(Users).filter_by(nickName=nickName).first()
        if not user:
            errmsg = "用户%s不存在" % nickName
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp
        comments = db.session.query(Comments).filter_by(user_id=user.id).all()
        comment_list = list()
        for comment in comments:
            course_id = comment.course_id
            course = Courses.query.filter_by(id=course_id).first()
            comment_info = {
                "id": comment.id,
                "content": comment.content,
                "user_id": user.id,
                "nickName": nickName,
                "course_id": course_id,
                "course_name": course.name,
                "course_category": course.category,
                "comment_time": comment.comment_time,
                "photo": user.photo
            }
            comment_list.append(comment_info)
        current_app.logger.info("get comments of user <%s> ok, data-->%s" % (nickName, comment_list))

    # 课程存在 用户名为空
    elif nickName is None and course_id:
        course = db.session.query(Courses).filter_by(id=course_id).first()
        if not course:
            errmsg = "课程%s不存在" % course_id
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp
        comments = db.session.query(Comments).filter_by(course_id=course_id).all()
        for comment in comments:
            user_id = comment.user_id
            user = Users.query.filter_by(id=user_id).first()
            comment_info = {
                "id": comment.id,
                "content": comment.content,
                "user_id": user_id,
                "nickName": user.nickName,
                "course_id": course_id,
                "course_name": course.name,
                "course_category": course.category,
                "comment_time": comment.comment_time,
                "photo": user.photo
            }
            comment_list.append(comment_info)
        current_app.logger.info("get comments of course <%s> ok, data-->%s" % (course_id, comment_list))

    # 课程存在 用户名存在
    elif nickName and course_id:
        user = db.session.query(Users).filter_by(nickName=nickName).first()
        if not user:
            errmsg = "用户%s不存在" % nickName
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp

        course = db.session.query(Courses).filter_by(id=course_id).first()
        if not course:
            errmsg = "课程%s不存在" % course_id
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp

        comments = db.session.query(Comments).filter_by(user_id=user.id, course_id=course_id).all()
        for comment in comments:
            comment_info = {
                "id": comment.id,
                "content": comment.content,
                "user_id": user.id,
                "nickName": nickName,
                "course_id": course_id,
                "course_name": course.name,
                "course_category": course.category,
                "comment_time": comment.comment_time,
                "photo": user.photo
            }
            comment_list.append(comment_info)

        current_app.logger.info("get comments of <%s> ok, data-->%s" % (nickName, comment_list))
    return jsonify(comment_list)

# 获取脑电数据
@getData.route("/get_waves")
def get_waves():
    """选择性进行查询脑电数据
    http://39.108.166.132:8888/get_waves?nickName=xxx&course_id=xxx
    :return 脑电数据的列表
    """
    current_app.logger.info("get waves info")
    nickName = request.values.get("nickName")
    course_id = request.values.get("course_id")
    data_list = list()

    if nickName and course_id:
        user = db.session.query(Users).filter_by(nickName=nickName).first()
        if not user:
            errmsg = "用户%s不存在, 请核对" % nickName
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp

        course = db.session.query(Courses).filter_by(id=course_id).first()
        if not course:
            errmsg = "课程%s不存在, 请核对" % course_id
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp
        wave = Waves.query.filter_by(user_id=user.id, course_id=course_id).first()
        if not wave:
            errmsg = "学生%s + 课程%s 的脑电数据不存在, 请核对" % (nickName, course_id)
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp

        wave_info = {
            "id": wave.id,
            "data": wave.data,
            "test_time": wave.test_time,
            "user_id": user.id,
            "nickName": nickName,
            "photo": user.photo,
            "course_id": course_id,
            "course_name": course.name,
            "course_category": course.category,
            "course_avg_data": course.avg_data,
        }
        current_app.logger.info("get waves of student<%s> + course<%s> ok, data-->%s" % (nickName, course_id, data_list))
        data_list.append(wave_info)

    elif nickName and not course_id:
        user = db.session.query(Users).filter_by(nickName=nickName).first()
        if not user:
            errmsg = "用户%s不存在, 请核对" % nickName
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp
        waves = user.waves
        if not waves:
            errmsg = "学生%s 的脑电数据不存在, 请核对" % nickName
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp

        for wave in waves:
            course_id = wave.course_id
            course = db.session.query(Courses).filter_by(id=course_id).first()
            wave_info = {
                "id": wave.id,
                "data": wave.data,
                "test_time": wave.test_time,
                "user_id": user.id,
                "nickName": nickName,
                "photo": user.photo,
                "course_id": course_id,
                "course_name": course.name,
                "course_category": course.category,
                "course_avg_data": course.avg_data,
            }
            current_app.logger.info("get  waves of student<%s> ok, data-->%s" % (nickName, data_list))
            data_list.append(wave_info)

    elif not nickName and course_id:
        course = db.session.query(Courses).filter_by(id=course_id).first()
        if not course:
            errmsg = "课程%s不存在, 请核对" % course_id
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp

        waves = course.waves
        if not waves:
            errmsg = "课程%s 的脑电数据不存在, 请核对" % nickName
            resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
            return resp

        for wave in waves:
            user_id = wave.user_id
            user = db.session.query(Users).filter_by(id=user_id).first()

            wave_info = {
                "id": wave.id,
                "data": wave.data,
                "test_time": wave.test_time,
                "user_id": user_id,
                "nickName": user.nickName,
                "photo": user.photo,
                "course_id": course_id,
                "course_name": course.name,
                "course_category": course.category,
                "course_avg_data": course.avg_data,
            }
            current_app.logger.info("get  waves of course<%s> ok, data-->%s" % (nickName, data_list))
            data_list.append(wave_info)

    else:
        waves = Waves.query.all()
        for wave in waves:
            course_id = wave.course_id
            course = db.session.query(Courses).filter_by(id=course_id).first()

            user_id = wave.user_id
            user = db.session.query(Users).filter_by(id=user_id).first()

            wave_info = {
                "id": wave.id,
                "data": wave.data,
                "test_time": wave.test_time,
                "user_id": wave.user_id,
                "nickName": user.nickName,
                "photo": user.photo,
                "course_id": course_id,
                "course_name": course.name,
                "course_category": course.category,
                "course_avg_data": course.avg_data,
            }
            current_app.logger.info("get all waves ok, data-->%s" % data_list)
            data_list.append(wave_info)
    # return jsonify(data_list)
    return jsonify(data_list)

@getData.route("/get_user_info")
def get_user_info():
    """获取用户信息，get请求中参数携带nickName + user_type
    此接口中角色必须绑定了微信名才可查询
    url: http://39.108.166.132:8888/get_user_info? nickName=xxx & user_type=xxx
    :return 查询到的用户信息, 如果没信息则时空列表
    """
    current_app.logger.info("get user info")
    nickName = request.values.get("nickName")
    user_type = request.values.get("user_type")
    user = db.session.query(Users).filter_by(nickName=nickName, user_type=user_type).first()
    if not user:
        errmsg = "用户%s不存在" % nickName
        resp = utils.make_resp(msg=json.dumps(errmsg), status=201)
        return resp

    user_info = {
        "nickName": nickName,
        "user_type": user_type,
        "phone_num": user.phone_num,
        "intro": user.intro,
        'photo': user.photo
    }

    if user_type == "students":
        # user_info["waves"] = user.students.waves
        pass
    elif user_type == "teachers":
        # user_info["courses"] = user.teachers.courses
        user_info["school_id"] = user.teacher.school_id
    else:
        user_info["principal"] = user.school.principal
        user_info["building"] = user.school.building

    current_app.logger.info("get <%s>'s info ok, data-->%s" % (nickName, user_info))
    return jsonify(user_info)

@getData.route("/get_schools")
def get_schools():
    schools = db.session.query(Schools).all()
    schools_list = list()
    for school in schools:
        schools_info = {
            "id": school.id,
            "name": school.name,
            "intro": school.intro,
            'photo': school.photo,
            "principal": school.principal,
            "building": school.building,
        }
        schools_list.append(schools_info)
    current_app.logger.info("get all schools ok, data-->%s" % schools_list)
    return jsonify(schools_list)

@getData.route("/get_teachers")
def get_teachers():
    teachers = db.session.query(Teachers).all()
    teachers_list = list()

    for teacher in teachers:
        school_id = teacher.school_id
        school = db.session.query(Schools).filter_by(id=school_id).first()
        teacher_info = {
            "id": teacher.id,
            "name": teacher.name,
            "intro": teacher.intro,
            "photo": teacher.photo,
            "course_name": teacher.course.name,
            "course_category": teacher.course.category,
            "school_id": school_id,
            "school_name": school.name,
        }
        teachers_list.append(teacher_info)
    current_app.logger.info("get all teachers ok, data-->%s" % teachers_list)
    return jsonify(teachers_list)

@getData.route("/get_courses")
def get_courses():
    courses = db.session.query(Courses).all()
    courses_list = list()

    for course in courses:
        teacher_id = course.teacher_id
        teacher = db.session.query(Teachers).filter_by(id=teacher_id).first()
        courses_info = {
            "id": course.id,
            "name": course.name,
            "avg_data": course.avg_data,
            "category": course.category,
            "content_url": course.content_url,
            "teacher": teacher.name,
            "teacher_id": teacher_id,
        }
        courses_list.append(courses_info)
    current_app.logger.info("get all courses ok, data-->%s" % courses_list)
    return jsonify(courses_list)


#The Insertion sort algorithm has a complexity of O(n^2) because we're still doing comparisons with two nested lists.
#However, this one is preferable to the bubble and selection sort algorithms.
#We take the "best" parts of both of those and combine them to insertion sort.

#The insertion sort algorithm breaks a list into two sublists, one with sorted values and the other with unsorted values.
#We move one unsorted value to the sorted sublist, and compare its value to the values to the left.
#We move it into the correct position by switching, each time the item to the left is larger.

def insertion_sort(list_a):
    indexing_length = range(1, len(list_a))
    for i in indexing_length:
        value_to_sort = list_a[i]

        while list_a[i-1] > value_to_sort and i>0:
            list_a[i], list_a[i-1] = list_a[i-1], list_a[i]
            i = i -1

    return list_a

print(insertion_sort([7,8,9,8,7,6,5,6,7,8,9,8,7,6,5,6,7,8]))


import vtk
from vtk import *
import string
import sys

def ReadZoneHeader(f):
    l = f.readline().strip()
    params = {}
    tokens = l.split(' ')
    for token in tokens:
        pair = token.split('=')
        if len(pair)==2:
            params[pair[0]] = pair[1].strip('\'')

    NnodesPerElt = -1
    try:
        if params['F'] == 'FEPOINT':
            try:
                if params['ET'] == 'TRIANGLE':
                    #print('Found ET=TRIANGLE F=FEPOINT')
                    NnodesPerElt = 3
                elif params['ET'] == 'SEGMENT':
                    #print('Found ET=SEGMENT F=FEPOINT')
                    NnodesPerElt = 2
                else:
                    print('F=FEPOINT requires ET=TRIANGLE format specifier')
                    sys.exit(0)
            except KeyError:
                print('F=FEPOINT requires ET=TRIANGLE format specifier')
                sys.exit(0)
    except KeyError:
        try:
            if (params['DATAPACKING'] == 'POINT'):
                try:
                    if (params['ZONETYPE'] == 'FETRIANGLE'):
                        #print('Found ZONETYPE=FETRIANGLE DATAPACKING=POINT')
                        NnodesPerElt = 3
                    elif (params['ZONETYPE'] == 'FELINESEG'):
                        #print('Found ZONETYPE=FELINESEG DATAPACKING=POINT')
                        NnodesPerElt = 2
                    else:
                        print('DATAPACKING=POINT requires ZONETYPE=FETRIANGLE format specifier')
                except KeyError:
                    print('DATAPACKING=POINT requires ZONETYPE=FETRIANGLEformat specifier')
            else:
                print('Neither F=FEPOINT nor DATAPACKING=POINT format specified in input')
        except KeyError:
            return 0, 0, 0
            #print('Neither F or DATAPACKING format specified in input')

    Nnodes = int(params['N'])
    Nelts = int(params['E'])
    return Nnodes, Nelts,NnodesPerElt

def ReadNodes(Nnodes,Nscalars,varnames,f):
    Points = vtk.vtkPoints()
    PointScalars = []
    for i in range(Nscalars):
        PointScalars.append(vtk.vtkDoubleArray())
        PointScalars[-1].SetName(varnames[3+i]);

    for i in range(Nnodes):
        line = f.readline().strip(' \n')
        d = line.split(' ')
        d = [ x for x in d if x != '' ]
        Nscalars_this = len(d) - 3
        if Nscalars != Nscalars_this:
            print('Number of scalars inconsistent across points '+str(Nscalars)+' vs '+str(Nscalars_this))
            print(d)
            sys.exit(0)
        id = Points.InsertNextPoint(float(d[0]),float(d[1]),float(d[2]))
        for i in range(Nscalars):
            PointScalars[i].InsertNextTuple([float(d[3+i])])

    return Points, PointScalars

def ReadElts(Nelts,NnodesPerElt,f):
    polys = vtk.vtkCellArray()

    if NnodesPerElt==2:
        #print('Reading segments')
        for i in range(Nelts):
            polys.InsertNextCell(2)
            polys.InsertCellPoint(i)
            polys.InsertCellPoint(i+1)
            line = f.readline().strip() # Read and toss, do not need for PolyLine structs

    elif NnodesPerElt==3:
        #print('Reading triangles')
        Triangle = vtk.vtkTriangle()
        for i in range(Nelts):
            line = f.readline().strip()
            d = line.split(' ')
            Triangle.GetPointIds().SetId(0,int(d[0])-1)
            Triangle.GetPointIds().SetId(1,int(d[1])-1)
            Triangle.GetPointIds().SetId(2,int(d[2])-1)
            polys.InsertNextCell(Triangle)

    return polys

f = open(sys.argv[1],'r')

l = f.readline().strip()
tokens = l.split(' ')
if tokens[0] != 'VARIABLES':
    print('File not in correct format')
    sys.exit(0)
varnames = tokens[2:]
Nscalars = len(varnames) - 3

allpolys = vtk.vtkAppendPolyData()
EOF = False
while(not EOF):
    try:
        Nnodes, Nelts, NnodesPerElt = ReadZoneHeader(f)
        if Nnodes==0 | Nelts==0:
            EOF = True
        else:
            #print('has Nnodes, Nelts: '+str(Nnodes)+' '+str(Nelts))
            Points, PointScalars = ReadNodes(Nnodes, Nscalars,varnames,f)
            polys = ReadElts(Nelts,NnodesPerElt,f)

            polydata = vtk.vtkPolyData()
            polydata.SetPoints(Points)
            if NnodesPerElt==2:
                polydata.SetLines(polys)
            else:
                polydata.SetPolys(polys)
            if (Nscalars>0):
                for scalar in PointScalars:
                    polydata.GetPointData().AddArray(scalar)
                polydata.GetPointData().SetActiveScalars(varnames[-1])

            polydata.Modified()
            if vtk.VTK_MAJOR_VERSION <= 5:
                polydata.Update()

            allpolys.AddInputData(polydata)
            allpolys.Update()

    except:
       EOF=True

mapper = vtk.vtkDataSetMapper()
mapper.SetInputData(allpolys)


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

#import section
import curses
import random
import config

from apple import Apple
from gameboard import GameBoard
from snake import Snake

turn_dict = {ord('s'):(1,0), ord('w'):(-1,0),         #keyboard controls
            ord('a'):(0,-1), ord('d'):(0,1)}          #keyboard controls

winx = 42                                             #window width
winy = 22                                             #window heigth

screen = (winy, winx)                                 #screen tuple

how_often = 5                                         #how often the snake appears

class Game(object):
    '''
    Class containing whole snake game.  Actual game loop is put into
    game(self, stdscr) function.
    '''
    def __init__(self):

        self.game_is_on = True                        #self-explanatory
        self.score = 0                                #starting score of player
        self.player = Snake(st_coords = (11,31),      #players snake
                            direction = (0,-1),       #players snake
                            screen = screen)          #players snake

        self.cpu = Snake(st_coords = (11,11),         #cpus snake
                            direction = (0,1),        #cpus snake
                            screen = screen)          #cpus snake

        self.level = GameBoard(screen)                #making gameboard
        self.morsel = Apple(screen)                   #making apple
        self.colors = {}                              #colors dictionary
        self.apples_eaten = 0                         #number of apples eaten

    def curses_initialize(self):
        '''
        All nessescary curses parameters set straight
        '''
        curses.noecho()                               #disabling input confirmation
        curses.cbreak()                               #one char input
        curses.curs_set(0)                            #making cursor non-visible
        curses.start_color()                          #initialising colours

        #colors:

        color_tags = ['blk', 'red', 'grn', 'blu', 'cyn', 'mag', 'wht', 'ylw']
        snake_colors = [curses.COLOR_BLACK,
                        curses.COLOR_RED,
                        curses.COLOR_GREEN,
                        curses.COLOR_BLUE,
                        curses.COLOR_CYAN,
                        curses.COLOR_MAGENTA,
                        curses.COLOR_WHITE,
                        curses.COLOR_YELLOW]

        for i in range(1, 7):
            curses.init_pair(i, snake_colors[i], snake_colors[0])
            self.colors[color_tags[i]] = curses.color_pair(i)

    def player_action(self, key):
        '''
        Function for handling player keyboard inputs.
        '''
        #exit cause
        if key == ord('q'):
            self.game_is_on = False

        #snake turning (W, S, A and D)
        if key in turn_dict.keys() and self.player.can_turn(key):
            self.player.direction = turn_dict[key]

    def generate_new_snake(self, obstacles):
        '''
        Function for generating new snake in random place
        '''
        new_snake_x = random.randint(1, winx - 2)
        new_snake_y = random.randint(1, winy - 2)

        while (new_snake_y, new_snake_x) in obstacles:
            new_snake_x = random.randint(1, winx - 2)
            new_snake_y = random.randint(1, winy - 2)

        return Snake(st_coords = (new_snake_y,new_snake_x),
                    direction = random.choice(list(turn_dict.values())),
                    screen = screen,
                    color = random.choice(list(self.colors.values()))
                    )

    def game(self, stdscr):
        '''
        Actual game
        '''

        stdscr = curses.initscr()                     #let the game begin!
        self.curses_initialize()                      #curses nessesceties
        self.level.initialize(stdscr)                 #gameboard nesseseties

        self.player.color = self.colors['grn']        #time to add some color
        self.cpu.color = self.colors['blu']           #to snakes!
        self.morsel.initialize(self.player.body)      #apple a day maked doctor away

        self.cpus = [self.cpu]

        #game loop
        while self.game_is_on:

            snakes = self.cpus + [self.player]             #creating list of all snakes
            pressed_key = self.level.board.getch()    #getting key from player

            #CPUs SECTIONS
            for cpu in self.cpus:
                cpu.thinks(self.player, self.morsel)  #cpu is thinking its move
                self.level.move(cpu)                  #cpu is moving

                if cpu.eats(self.morsel):             #taking away points from
                    self.score -= 20                  #player
                    self.apples_eaten +=1

                if cpu.collides(snakes):              #CPU hitting player
                    self.level.unprint(cpu)           #erasing
                    self.cpus.remove(cpu)

                if not self.cpus:
                   self.level.end('cpu', self.score)
                   self.game_is_on = False

            #PLAYER SECTION
            self.level.move(self.player)              #player is moving

            if self.player.eats(self.morsel):         #giving point to player
                self.score += 100
                self.apples_eaten +=1

            if self.player.collides(self.cpus):       #player hitting CPU
                self.level.end('player', self.score)
                self.game_is_on = False

            self.player_action(pressed_key)           #player controls

            #NEW SNAKE SECTION
            if self.apples_eaten == how_often:
                self.cpus.append(self.generate_new_snake(snakes))
                self.apples_eaten = 0

            #SCREEN SECTION
            self.level.print_apple(self.morsel)       #apple is printed on board
            self.level.print_score(self.score)        #printing score on board
            self.level.board.refresh()                #refreshing the screen

    def main(self):
        curses.wrapper(self.game)

game = Game()
game.main()


from app.api.business.validators import ApplicationValidator
from app.models import Application

def test_can_get_errors_with_empty_string():
    application = Application(
        data={
            'representative': '',
            'email': '',
            'phone': '',
            'contact_name': '',
            'contact_email': '',
            'contact_phone': ''
        }
    )
    errors = ApplicationValidator(application).validate_contacts()

    assert len(errors) == 6


import pandas as pd
import dataframe_image as dfi
import shutil
from os import path

class Course:
    courseID= "CS2401"
    teacher = "Dan DeBlasio"
    RMPRating = -10
    Start = 2355
    End = 2400
    Days = ['M', 'T']
    ADAAccess = False;
    lecture = '0'
    CRN = 25554

    # parameterized constructor
    def __init__(self):
        self.courseID = self.courseID
        self.teacher = self.teacher
        self.RMPRating = self.RMPRating
        self.Start = self.Start
        self.End = self.End
        self.Days = self.Days
        self.ADAAccess = self.ADAAccess
        self.lecture = self.lecture
        self.CRN = self.CRN

    def setAll(self,courseID, teacher, RMPRating, Start, End, Days, ADAAccess, lecture, CRN):
        self.teacher = teacher
        self.courseID=courseID
        self.RMPRating = RMPRating
        self.Start = Start
        self.End = End
        self.Days = Days
        self.ADAAccess = ADAAccess
        self.lecture = lecture
        self.CRN = CRN

    def setCourseId(self,courseID):
        self.courseID=courseID
    def setTeacher(self,teacher):
        self.teacher=teacher
    def setRMPating(self,RMPRating):
        self.RMPRating=RMPRating
    def setStart(self,Start):
        self.Start=Start
    def setEnd(self,End):
        self.End=End
    def setADAAcess(self,ADAAccess):
        self.ADAAccess=ADAAccess
    def setDays(self,Days):
        self.Days = Days
    def setLecture(self,lecture):
        self.lecture=lecture
    def setCRN(self,CRN):
        self.CRN=CRN

    def getCourseId(self):
        return self.courseID
    def getTeacher(self):
        return self.teacher
    def getRMPRating(self):
        return self.RMPRating
    def getStart(self):
        return self.Start
    def getEnd(self):
        return self.End
    def getADAAcess(self):
        return self.ADAAccess
    def getDays(self):
        return self.Days
    def getLecture(self):
        return self.lecture
    def getCRN(self):
        return self.CRN

    def display(self):
        print(" Course: " + str(self.courseID))
        print(" Teacher " + str(self.teacher))
        print(" CRN: " + str(self.CRN))
        print

    def displaySolution(self):
        print(str(self.RMPRating)+" Course: " + str(self.courseID)+"   CRN: " + str(self.CRN)+"   Start: "+str(self.Start)+"   End: "+str(self.End)+"     "+str(self.Days[0]))

def makeGM():

    c1 = Course()
    c1.setAll("CS 2302","Diego Aguirre",5,1330,1450,['M','W'],True,"In-Person",21736)
    c1.display()
    c2 = Course()
    c2.setAll("CS 2302","Olac Fuentes",3.4,1200,1320,['T','R'],True,"In-Person",25686)
    c2.display()
    c3 = Course()
    c3.setAll("CS 2302"	,	"Olac Fuentes"	,	3.4	,	1330	,	1450	,	['T','R']	,	True	,	"In-Person"	,	27090)
    c3.display()
    cs = [c1,c2,c3]
    c4 = Course()
    c4.setAll("MATH 3323"	,	"Luis Valdez-Sanchez"	,	2	,	1330	,	1450	,	['T','R']	,	True	,	"In-Person"	,	22534)
    c4.display()
    c5 = Course()
    c5.setAll("MATH 3323"	,	"Luis Valdez-Sanchez"	,	2	,	1030	,	1150	,	['M','W']	,	True	,	"In-Person"	,	21021)
    c5.display()
    c6 = Course()
    c6.setAll("MATH 3323"	,	"Miguel Argaez"	,	2.9	,	1030	,	1150	,	['T','R']	,	True	,	"In-Person"	,	21022)
    c6.display()
    c7 = Course()
    c7.setAll("MATH 3323"	,	"Juio Cesar Urenda"	,	3.3	,	900	,	1020	,	['M','W']	,	False	,	"In-Person"	,	21178)
    c7.display()
    c8 = Course()
    c8.setAll("MATH 3323"	,	"Juio Cesar Urenda"	,	3.3	,	-1	,	-1	,	[]	,	True	,	"ONLINE"	,	22449)
    c8.display()
    c9 = Course()
    c9.setAll("MATH 3323"	,	"Juio Cesar Urenda"	,	3.3	,	-1	,	-1	,	[]	,	True	,	"ONLINE"	,	22739)
    c9.display()
    c10 = Course()
    c10.setAll("MATH 3323"	,	"Juio Cesar Urenda"	,	3.3	,	-1	,	-1	,	[]	,	True	,	"ONLINE"	,	25661)
    c10.display()
    math=[c4,c5,c6,c7,c8,c9,c10]
    c11 = Course()
    c11.setAll("EE 2169"	,	"TBA"	,	-1	,	730	,	1020	,	['R']	,	True	,	"ONLINE"	,	28762)
    c11.display()
    c12 = Course()
    c12.setAll("EE 2169"	,	"TBA"	,	-1	,	1030	,	1320	,	['T']	,	True	,	"ONLINE"	,	25554)
    c12.display()
    c13 = Course()
    ee1=[c11,c12]
    c13.setAll("EE 2369"	,	"Virgilio Ernesto Gonzalez"	,	-1	,	1330	,	1450	,	['M','W']	,	True	,	"In-Person"	,	23278)
    c13.display()
    c14= Course()
    c14.setAll("EE 2369"	,	"Tahsin Rahman"	,	5	,	1500	,	1620	,	['T','R']	,	True	,	"In-Person"	,	25137)
    c14.display()
    ee2=[c13,c14]

    GM=[cs,math,ee1,ee2]
    return GM

def isValid(arrOfCourses):
    monday=[]
    for x in arrOfCourses:
        if x.Days.__contains__('M'):
            monday.append(x)
    tuesday = []
    for x in arrOfCourses:
        if x.Days.__contains__('T'):
            tuesday.append(x)
    wednesday = []
    for x in arrOfCourses:
        if x.Days.__contains__('W'):
            wednesday.append(x)
    thursday = []
    for x in arrOfCourses:
        if x.Days.__contains__('R'):
            thursday.append(x)
    friday = []
    for x in arrOfCourses:
        if x.Days.__contains__('F'):
           friday.append(x)
    week = [monday,tuesday,wednesday,thursday,friday]
    temp=Course()
    for d in week:
        temp = Course()
        gms =[]
        while len(d)>0:
            for x in range(0,len(d)-1):
                if d[x].getStart() < temp.getStart():
                    temp=d[x]
            gms.append(temp)
            temp = d[0]
            d.remove(temp)
        for x in range(0,len(gms)-2):
            if gms[x].getEnd() >= gms[x+1].getStart():
                return False
    return True

def getValids(GM):
    valids=[]
    for a in range(0,len(GM[0])):
        for b in range(0, len(GM[1])):
            for c in range(0, len(GM[2])):
                for d in range(0, len(GM[3])):
                    if isValid([GM[0][a],GM[1][b],GM[2][c],GM[3][d]]):
                        valids.append([GM[0][a],GM[1][b],GM[2][c],GM[3][d]])
    return valids

def getBest(valids):
    tempMax=[]
    for c in valids:
        if sumRating(c)>sumRating(tempMax):
            tempMax = c
    return tempMax

def sumRating(c):
    sum =0
    for a in c:
        sum = sum + a.getRMPRating()
    return sum

def getFinal():
    valids=getValids(makeGM())
    bestChoice =getBest(valids)
    print("RESULT")
    print(sumRating(bestChoice))
    for c in bestChoice:
        c.displaySolution()
    return bestChoice

def toTable(bestChoice):
    M = ['.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.']
    T = ['.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.']
    W = ['.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.']
    R = ['.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.']
    F = ['.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.']

    for course1 in bestChoice:
        if('M' in course1.Days):
            if course1.Start == -1:
                M[16] = f'{course1.CRN} {course1.courseID}'
            else:
                M[int(course1.Start/100)-7] = f'{course1.CRN} {course1.courseID}'
        if('T' in course1.Days):
            if course1.Start == -1:
                T[16] = f'{course1.CRN} {course1.courseID}'
            else:
                T[int(course1.Start / 100) - 7] = f'{course1.CRN} {course1.courseID}'
        if('W' in course1.Days):
            if course1.Start == -1:
                W[16] = f'{course1.CRN} {course1.courseID}'
            else:
                W[int(course1.Start / 100) - 7] = f'{course1.CRN} {course1.courseID}'
        if('R' in course1.Days):
            if course1.Start == -1:
                R[16] = f'{course1.CRN} {course1.courseID}'
            else:
                R[int(course1.Start / 100) - 7] = f'{course1.CRN} {course1.courseID}'
        if('F' in course1.Days):
            if course1.Start == -1:
                F[16] = f'{course1.CRN} {course1.courseID}'
            else:
                F[int(course1.Start / 100) - 7] = f'{course1.CRN} {course1.courseID}'

    stopplaying = pd.DataFrame({'Time' : [7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,'TBA'], 'M' : M, 'T' : T, 'W' : W, 'R' : R, 'F' : F})
    if not path.exists("/Users/sebi/PycharmProjects/utepSchedulerWebsite/website/schedule.png") and not path.exists("/Users/sebi/PycharmProjects/utepSchedulerWebsite/website/static/schedule.png"):
        dfi.export(stopplaying, 'schedule.png')
        shutil.move("/Users/sebi/PycharmProjects/utepSchedulerWebsite/schedule.png", "/Users/sebi/PycharmProjects/utepSchedulerWebsite/website/static")


import subprocess, os

def process(image, path):
	# cmd_str = "python D:/python/flask/bihack/SheetVision-master/sheetvis.py " + image + " " + path
	# res = os.system(cmd_str)
	# print(res)
	os.chdir("D:/python/flask/bihack/SheetVision-master")
	os.system("cd /d D:/python/flask/bihack/SheetVision-master")
	cmd = ["python", "D:/python/flask/bihack/SheetVision-master/sheetvis.py", image, path]
	process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
	while True:
		output = process.stdout.readline().decode('ascii')
		if output == '' and process.poll() is not None:
			break
		if output:
			print(output.strip())

	# print(subprocess.check_output(cmd))


# -*- coding: utf-8 -*-
import scrapy
from tencentSpider.items import TencentspiderItem

class TencentSpider(scrapy.Spider):
    name = 'tencent'
    allowed_domains = ['tencent.com']

    base_url = 'https://hr.tencent.com/position.php?&start='
    offset = 0
    start_urls = [base_url + str(offset)]

    def parse(self, response):

        for node in response.xpath("//tr[@class='even'] | //tr[@class='odd']"):
            # 构建item对象，用来保存数据
            item = TencentspiderItem()

            item['positionName'] = node.xpath("./td[1]/a/text()").extract()[0]

            item['positionLink'] = node.xpath("./td[1]/a/@href").extract()[0]
            # 防止由于该项为空时导致引起获取连接范围越界
            if len(node.xpath("./td[2]/text()")):
                item['positionType'] = node.xpath("./td[2]/text()").extract()[0]
            else:
                item['positionType'] = ""

            item['peopleNumber'] = node.xpath("./td[3]/text()").extract()[0]

            item['workLocation'] = node.xpath("./td[4]/text()").extract()[0]

            item['publishTime'] = node.xpath("./td[5]/text()").extract()[0]

            yield item

        # 第一种写法，拼接url，适用场景：页面没有可以点击的请求连接（如：下一页），必须通过拼接url才能获取响应
        # if self.offset < 2990:
        #     self.offset += 10
        #     url = self.base_url + str(self.offset)
        #     yield scrapy.Request(url,callback=self.parse)

        #第二种写法：直接从response获取需要爬取的连接，并发送请求处理，直到连接全部提取完
        #表示当“下一页”按钮处于active激活状态时，继续执行下一页，来依次获取信息
        if len(response.xpath("//a[@class='noactive' and @id='next']")) == 0:
            url = response.xpath("//a[@id='next']/@href").extract()[0]
            yield scrapy.Request("https://hr.tencent.com/" + url,callback=self.parse)


import sys
import datetime
import traceback
import pandas as pd

from os import sys, path
root_path = path.dirname(path.dirname(path.abspath(__file__)))

try:
    from Utiltity.common import *
    from Utiltity.dependency import *
    from Utiltity.df_utility import *
    from Utiltity.time_utility import *
    from Database import NoSqlRw
    from Database import UpdateTableEx
    from Database.DatabaseEntry import DatabaseEntry
    from Utiltity.plugin_manager import PluginManager
except Exception as e:
    sys.path.append(root_path)

    from Utiltity.common import *
    from Utiltity.dependency import *
    from Utiltity.df_utility import *
    from Utiltity.time_utility import *
    from Database import NoSqlRw
    from Database import UpdateTableEx
    from Database.DatabaseEntry import DatabaseEntry
    from Utiltity.plugin_manager import PluginManager
finally:
    logger = logging.getLogger('')

def wrap_list(val: any):
    return list(val) if isinstance(val, (list, tuple)) else [val]

class FactorCenter:
    FACTOR_PROB_LENGTH = 7

    def __init__(self, data_hub, database: DatabaseEntry, factor_plugin: PluginManager):
        self.__data_hub_entry = data_hub
        self.__database_entry = database
        self.__factor_plugin = factor_plugin
        self.__plugin_probs = []
        self.__factor_probs = {}
        self.__factor_index = {}
        self.__factor_depends = {}

    def get_plugin_manager(self) -> PluginManager:
        return self.__factor_plugin

    def get_all_factors(self) -> [str]:
        return list(self.__factor_depends.keys())

    def get_factor_depends(self, factor: str) -> [str]:
        return self.__factor_depends.get(factor, [])

    def get_factor_comments(self, factor: str) -> str:
        factor_uuid = self.__factor_index.get(factor, '')
        factor_probs = self.__factor_probs.get(factor_uuid, ())
        return factor_probs[2] if len(factor_probs) > 2 else ''

    # -------------------------------------------------------------------------

    def query(self, sotck_identity: str, factor_name: str or [str],
              time_serial: tuple, mapping: dict, extra: dict) -> pd.DataFrame or None:
        # if self.__data_hub_entry is None:
        #     return None
        if not isinstance(factor_name, (list, tuple)):
            factor_name = [factor_name]

        df = pd.DataFrame()
        fields_dependency, factor_dependency = self.calculate_factor_dependency(factor_name)

        for factor in factor_dependency:
            if factor in df.columns:
                continue
            result = self.get_plugin_manager().execute_module_function(
                self.get_plugin_manager().all_modules(), 'calculate', {
                    'factor': factor,
                    'identity': sotck_identity,
                    'time_serial': time_serial,
                    'mapping': mapping,
                    'data_hub': self.__data_hub_entry,
                    'database': self.__database_entry,
                    'extra': extra,
                }, True
            )
            if result is None or len(result) == 0:
                continue

            df = result[0] if len(df) == 0 else pd.merge(df, result[0], how='left', on=['stock_identity', 'period'])
        return df

    def reload_plugin(self):
        self.get_plugin_manager().refresh()
        self.__plugin_probs = self.get_plugin_manager().execute_module_function(
            self.get_plugin_manager().all_modules(), 'plugin_prob', {}, False
        )
        self.__factor_probs.clear()
        self.__factor_index.clear()
        self.__factor_depends.clear()

        for plugin_prob in self.__plugin_probs:
            factor_probs = plugin_prob.get('factor')
            if isinstance(factor_probs, dict):
                for uuid, prob in factor_probs.items():
                    if prob is not None and len(prob) == FactorCenter.FACTOR_PROB_LENGTH and prob[3] is not None:
                        self.__factor_probs[uuid] = prob
                    else:
                        print('Drop factor - ' + str(uuid) + ' : ' + str(prob))

        for uuid, prob in self.__factor_probs.items():
            provides, depends, comments, _, _, _, _ = prob
            provides = wrap_list(provides)
            depends = wrap_list(depends)

            for provide in provides:
                if provide in self.__factor_depends.keys():
                    print('Duplicate factor: ' + provide)
                self.__factor_index[provide] = uuid
                self.__factor_depends[provide] = depends

    def calculate_factor_dependency(self, factor_name: str or [str]) -> (list, list):
        if not isinstance(factor_name, (list, tuple)):
            factor_name = [factor_name]
        dependency = Dependency()
        for name in factor_name:
            dependency.add_dependence(name, self.__factor_depends.get(name, []))
        dependency_list = dependency.sort_by_dependency()

        fields_dependency = []
        factor_dependency = []
        for name in dependency_list:
            if name in self.__factor_depends.keys():
                factor_dependency.append(name)
            else:
                fields_dependency.append(name)
        return fields_dependency, factor_dependency

# ----------------------------------------------------- Test Code ------------------------------------------------------

def __build_factor_center() -> FactorCenter:
    plugin = PluginManager(path.join(root_path, 'Factor'))
    factor = FactorCenter(None, None, plugin)
    return factor

def test_basic():
    factor = __build_factor_center()
    factor.reload_plugin()

def test_entry():
    test_basic()

# ----------------------------------------------------- File Entry -----------------------------------------------------

def main():
    test_entry()

    # If program reaches here, all test passed.
    print('All test passed.')

# ------------------------------------------------- Exception Handling -------------------------------------------------

def exception_hook(type, value, tback):
    # log the exception here
    print('Exception hook triggered.')
    print(type)
    print(value)
    print(tback)
    # then call the default handler
    sys.__excepthook__(type, value, tback)

if __name__ == "__main__":
    sys.excepthook = exception_hook
    try:
        main()
    except Exception as e:
        print('Error =>', e)
        print('Error =>', traceback.format_exc())
        exit()
    finally:
        pass


from openerp.tests import common
import os
import openerp.tools as tools
from openerp import modules
import logging
_logger = logging.getLogger(__name__)

class ReTest(common.TransactionCase):

    """Test againt modules like account, sale, purchase
    """

    def setUp(self):
        super(ReTest, self).setUp()
        self.test_deprecate = [
            # Dont use this test because procurement module changed the
            # make_to_order of product
            'test/picking_order_policy.yml',
            # Dont use this test because product is used like service and try
            # to create picking
            'test/manual_order_policy.yml',
            # Dont use because purchase_double_validation modified flow of
            # purchase module
            'test/process/cancel_order.yml',
            # Dont use because purchase_double_validation modified flow of
            # purchase module
            'test/process/rfq2order2done.yml',

        ]

    def test_reload_test(self):
        if tools.config.options['test_enable']:
            cr = self.cr
            module_info = modules.load_information_from_description_file(
                "l10n_pe_retest")
            for dat in module_info.get('depends', []):
                cr.commit()
                try:
                    mod_info = modules.load_information_from_description_file(
                        dat)
                    for test_module in mod_info.get('test', []):
                        if test_module in self.test_deprecate:
                            continue
                        pathname = os.path.join(dat, test_module)
                        fp = tools.file_open(pathname)
                        _logger.info(
                            "Try againt Test: {} of Module: {}".format(
                                test_module, dat))
                        try:
                            tools.convert_yaml_import(
                                cr, dat, fp, kind="test", idref={},
                                mode="init")
                        except BaseException:
                            _logger.exception(
                                'module %s: an exception occurred in a test',
                                dat)
                finally:
                    if tools.config.options['test_commit']:
                        cr.commit()
                    else:
                        cr.rollback()


from .base import *
import os
import json
import datetime

SECRETS = json.load(open(os.path.join(SECRET_DIR, "base.json")))
SECRET_KEY = SECRETS["SECRET_KEY"]

WSGI_APPLICATION = "config.wsgi.local.application"

DEBUG = True

ALLOWED_HOSTS = []

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
    }
}


import sys
import time
import getpass
from termcolor import colored

import math
import random
import json

from pynput.keyboard import Key, Controller as KeyboardController
keyboard = KeyboardController()
import keyboard

print()
print()

import sys
import math
import random
import json
letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'Z']
number1 = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
number2 = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
number3 = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
number4 = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
flights = int(input(colored("          How many flights are scheduled for today? ", "green")))
flight = 0
code_number = ""
flight_list = {
  "departures" : flights,
  "code_number": code_number
}
for i in range(flights):
  a = random.choice(letters)
  b = random.choice(number1)
  c = random.choice(number2)
  d = random.choice(number3)
  e = random.choice(number4)
  code_number = a+b+c+d+e
  flight += 1
  flight_list.update({flight: code_number})
  def readFile(fileName):
    out = []
    file = open(fileName)
    r = file.readlines()
    file.close()

    for e in r:
      out.append(int(e.replace("\n", "")))

    return out
#a = append -->
r = json.dumps(flight_list)
file = open('./Flight.json', 'a')
file.write((r))
file.close()
print()
print("We are finishing ...")
time.sleep(3)
print()
print("Done ...")


# for those stores with establishment matches, compare/update address and DBA name

import pandas as pd
import numpy as np
import string
from functions.manual_functions import set_blists_1e
from functions.manual_functions import accept_match_1e

ret_df = pd.read_csv('step_1_work/output/retailers_for_manual_verify_complete.csv')
ggl_df = pd.read_csv('step_1_work/output/documentation/google_places_output.csv')
ggl_df = ggl_df.rename({'id':'est_id'},axis = 1)

# get est_ids to manually accept matches of
wlist_ids = accept_match_1e()
ggl_df.loc[[i in wlist_ids for i in ggl_df['est_id'].values.tolist()], 'match'] = 'Active'
ggl_df['match']= np.where((ggl_df['permanently_closed']== True) \
    & (ggl_df['match'] == 'Active'), 'Closed', ggl_df['match'])

# see if we can find anything from manual examiniation of remaining  non-matches in google data
compar_cols = ['est_id','DBA Name','bizname','Full Address','formatted_address', 'name_score',
    'AddressNumber_score','StreetName_score','StreetNamePostType_score',
    'ZipCode_score','dist_miles']
ggl_df.loc[ggl_df['match']=='Not Validated',compar_cols].to_csv( \
    'step_1_work/output/google_rejected_matches.csv', index = False)

# get part of df with those with determined establishments
df = ret_df.merge(ggl_df.loc[:, [i not in ['DBA Name','id','Full Address'] for i in ggl_df.columns]],
    how='inner', on='est_id')

df = df.loc[['establishment' in i for i in df['types']],:]
df = df.loc[((df['match']=='Active')|(df['match']=='Closed'))]
df = df.reset_index(drop=True)

# make sure google name is all caps
df ['bizname'] = df['bizname'].str.upper()
df ['DBA Name'] = df['DBA Name'].str.upper()

# Make sure all biznames from Google Places findings are in DBA name in FDA list
# split by spaces
fda_split=df.loc[:, 'DBA Name'].str.split(pat=" ", expand=True)
ggl_split=df.loc[:, 'bizname'].str.split(pat=" ", expand=True)

# clear out punctuation marks
translator = str.maketrans('', '', string.punctuation)
def punc_rm(s):
    if s!=None:
        return(str(s).translate(translator))
fda_split=fda_split.applymap(punc_rm)
ggl_split=ggl_split.applymap(punc_rm)

# do a comparison of words in the two names
match_df = pd.DataFrame(columns=ggl_split.columns, index=ggl_split.index)
for i,j in ggl_split.iterrows():
    def match_words(from_str, to_series=fda_split.iloc[i,:]):
        if from_str== None:
            return(np.nan)
        elif from_str in [i for i in to_series]:
            return(0)
        else:
            return(1)
    match_df.iloc[i,:] = j.apply(match_words)
match_df['sum'] = match_df.sum(axis=1, skipna=True)

# don't need to update those with same words
df['name_update'] = df['bizname']
df.loc[match_df['sum']==0,'name_update'] = np.nan

# duplicates in ggl_df indicate that the Google has a less specific name than
# existing database
df.loc[df['bizname'].duplicated(),'name_update'] = np.nan

# some words that don't seem to get the right place from looking at comparison list
# this is a manually tailored list, and should be updated with each unique run
blists = set_blists_1e()
ggl_blist = blists[0]
fda_blist = blists[1]
df.loc[df['bizname'].str.contains(ggl_blist,regex=True),'name_update'] = np.nan
df.loc[df['DBA Name'].str.contains(fda_blist,regex=True),'name_update'] = np.nan

# csv for comparing biz names that have extra words in ggl df
df.loc[~df['name_update'].isna(),['bizname','DBA Name']].to_csv('step_1_work/output/name_compare.csv', index=False)

# save updated names in original list, and merge in columns from google results
#new_cols = np.append(df.columns.values[[i not in ret_df.columns for i in df.columns]],'est_id')
ret_df = ret_df.merge(df[['est_id','name_update']], how='left', on='est_id')

# updating addresses
# may or may not mean I have to look at names again?

# flag non identical addresses
df['addr_diff'] = 0
for i in ['AddressNumber_score','StreetName_score','StreetNamePostType_score','ZipCode_score']:
    df.loc[df[i]<1, 'addr_diff'] = 1

# update address and geocoordinates for those non-exact addresses that
# are also somewhat close to original location
df['addr_update'] = np.nan
df['lat_update'] = np.nan
df['lon_update'] = np.nan
df.loc[(df['addr_diff'] == 1) & (df['dist_miles']<10), 'addr_update'] = \
    df.loc[(df['addr_diff'] == 1) & (df['dist_miles']<10), 'formatted_address']
df.loc[(df['addr_diff'] == 1) & (df['dist_miles']<10), 'lat_update'] = \
    df.loc[(df['addr_diff'] == 1) & (df['dist_miles']<10), 'geometry.location.lat']
df.loc[(df['addr_diff'] == 1) & (df['dist_miles']<10), 'lon_update'] = \
    df.loc[(df['addr_diff'] == 1) & (df['dist_miles']<10), 'geometry.location.lng']

# output csv for doing an address comparison
df.loc[(df['addr_diff'] == 1) & (df['dist_miles']<10),compar_cols].to_csv( \
    'step_1_work/output/addr_compare.csv', index = False)

# merge in the updates, and note where we can't update
ret_df = ret_df.merge(df[['est_id','addr_update','lat_update', 'lon_update','match']], how='left', on='est_id')
ret_df['match'] = ret_df['match'].fillna("Can't Validate")
# update operation status based on manual coding
ret_df.loc[ret_df['Manual Result']=='yes', 'match'] = 'Active'

# make new columns combining old and new updates
ret_df['DBA Name_update'] = ret_df['DBA Name']
ret_df['Full Address_update'] = ret_df['Full Address']
ret_df['Latitude_update'] = ret_df['Latitude']
ret_df['Longitude_update'] = ret_df['Longitude']
ret_df['DBA Name_update'].update(ret_df['name_update'])
ret_df['Full Address_update'].update(ret_df['addr_update'])
ret_df['Latitude_update'].update(ret_df['lat_update'])
ret_df['Longitude_update'].update(ret_df['lon_update'])

# fill in blanks with labels
for i in ['name_update', 'addr_update', 'lat_update', 'lon_update']:
    ret_df.loc[ret_df['match']=="Can't Validate",i] = \
        ret_df.loc[ret_df['match']=="Can't Validate",i].fillna("Can't Validate")
    ret_df.loc[ret_df['match']!="Can't Validate",i] = \
        ret_df.loc[ret_df['match']!="Can't Validate",i].fillna('Correct')

ret_df = ret_df.rename({'match':'biz_status'}, axis=1)
ret_df = ret_df.drop(columns='id')

# output FDA csv with updated names, addresses and geocoordinates
ret_df.to_csv('step_1_work/output/retailers_name_addr_update.csv', index=False)


from tkinter import *

class table:
    def __init__ (self, window, colour="black", netColour="green", w=600, h=400, horizNet=False):
        self.w = w
        self.h = h
        self.colour = colour

        self.canvas = Canvas(window, bg=self.colour, height=h, width=w)
        self.canvas.pack()

        av = (w + h) / 2
        dashWidth = av / 200
        hdw=dashWidth / 2 # hdw=HalfDashWidth
        dashTuple = (15,23)

        if not horizNet:
            self.canvas.create_line(w/2 - hdw, 0, w/2 - hdw, h, width=dashWidth, fill=netColour, dash=dashTuple)
        else:
            self.canvas.create_line(0, h/2 - hdw, w, h/2 - hdw, width=dashWidth, fill=netColour, dash=dashTuple)

    def drawRect (self, rect):
        x1 = rect.x
        x2 = x1 + rect.w

        y1 = rect.y
        y2 = y1 + rect.h

        c = rect.c

        return self.canvas.create_rectangle(x1, y1, x2, y2, fill=c)

    def drawOval (self, oval):
        x1 = oval.x
        x2 = x1 + oval.w

        y1 = oval.y
        y2 = y1 + oval.h

        c = oval.colour

        return self.canvas.create_oval(x1, y1, x2, y2, fill=c)

    def moveItem (self, item, x1, y1, x2, y2):
        self.canvas.coords(item, x1, y1, x2, y2)

    def removeItem (self, item):
        self.canvas.delete(item)

    def changeItemColour (self, item, c):
        self.canvas.itemconfigure(item, fill=c)


import pyautogui
from PIL import Image
import time
import win32api, win32con
import math
import os
from bs4 import BeautifulSoup
import requests

# pos
black_pos = (432, 282)
grey_pos = (481, 386)
dark_blue_pos = (533, 388)

white_pos = (437, 435)
light_grey_pos = (486, 438)
light_blue_pos = (528, 443)

dark_green_pos = (428, 484)
dark_brown_pos = (483, 483)
brown_pos = (532, 488)

green_pos = (438, 544)
red_pos = (486, 542)
orange_pos = (534, 543)

golden_pos = (432, 581)
purple_pos = (484, 594)
skin_color_pos = (527, 592)

yellow_pos = (433, 641)
pink_pos = (490, 634)
another_skin_pos = (533, 641)

# color rgb
black = (0, 0, 0)
grey = (102, 102, 102)
dark_blue = (0, 80, 205)

white = (255, 255, 255)
light_grey = (170, 170, 170)
light_blue = (38, 201, 255)

dark_green = (1, 116, 32)
dark_brown = (105, 21, 6)
brown = (150, 65, 18)

green = (17, 176, 60)
red = (255, 0, 19)
orange = (255, 120, 41)

golden = (176, 112, 28)
purple = (153, 0, 78)
skin_color = (203, 90, 87)

yellow = (255, 193, 38)
pink = (255, 0, 143)
another_skin = (254, 175, 168)

class Gartic_Phone_BOT():
    def __init__(self):
        super().__init__()

        self.ColorWithCoords = {}
        self.COLORS = (  # white
            black, grey, dark_blue,
            white, light_grey, light_blue,
            dark_green, dark_brown, brown,
            green, red, orange,
            golden, purple, skin_color,
            yellow, pink, another_skin)

        self.Color_Pos = {"(0, 0, 0)": black_pos,
                          "(102, 102, 102)": grey_pos,
                          "(0, 80, 205)": dark_blue_pos,

                          "(255, 255, 255)": white_pos,
                          "(170, 170, 170)": light_grey_pos,
                          "(38, 201, 255)": light_blue_pos,

                          "(1, 116, 32)": dark_green_pos,
                          "(105, 21, 6)": dark_brown_pos,
                          "(150, 65, 18)": brown_pos,

                          "(17, 176, 60)": green_pos,
                          "(255, 0, 19)": red_pos,
                          "(255, 120, 41)": orange_pos,

                          "(176, 112, 28)": golden_pos,
                          "(153, 0, 78)": purple_pos,
                          "(203, 90, 87)": skin_color_pos,

                          "(255, 193, 38)": yellow_pos,
                          "(255, 0, 143)": pink_pos,
                          "(254, 175, 168)": another_skin_pos,
                          }

        self.SameColor = 1

        self.Google_Image = 'https://www.google.com/search?site=&tbm=isch&source=hp&biw=1873&bih=990&'

        self.u_agnt = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/72.0.3815.473',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
            'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
            'Accept-Encoding': 'none',
            'Accept-Language': 'en-US,en;q=0.8',
            'Connection': 'keep-alive', }

        self.Image_Folder = 'Img'

    def download_images(self):
        data = input('Enter your search keyword: ')
        print(type(data))
        num_images = 1

        print('Searching Images....')

        search_url = self.Google_Image + 'q=' + data

        response = requests.get(search_url, headers=self.u_agnt)
        html = response.text

        b_soup = BeautifulSoup(html, 'html.parser')
        results = b_soup.findAll('img', {'class': 'rg_i Q4LuWd'})

        count = 6
        imagelinks = []
        for res in results:
            try:
                link = res['data-src']
                imagelinks.append(link)
                count = count + 1
                if count >= num_images:
                    break

            except KeyError:
                continue

        print(f'Found {len(imagelinks)} images')
        print('Start downloading...')

        for i, imagelink in enumerate(imagelinks):
            response = requests.get(imagelink)

            self.imagename = self.Image_Folder + '/' + data + str(i + 1) + '.bmp'
            with open(self.imagename, 'wb') as file:
                file.write(response.content)

        print('Download Completed!')

    def Change_Color(self, xyT):
        x, y = xyT
        win32api.SetCursorPos((x, y))
        win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x, y, 0, 0)
        win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0)

    def click(self, New_X, New_Y):
        win32api.SetCursorPos((New_X, New_Y))
        win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, New_X, New_Y, 0, 0)
        win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, New_X, New_Y, 0, 0)

    def closest_color(self, rgb) -> object:
        r, g, b = rgb
        color_diffs = []
        for color in self.COLORS:
            cr, cg, cb = color
            color_diff = math.sqrt(abs(r - cr) ** 2 + abs(g - cg) ** 2 + abs(b - cb) ** 2)
            color_diffs.append((color_diff, color))
        return min(color_diffs)[1]

    def match_color(self, rgb):
        return self.Color_Pos.get(rgb, white_pos)

    def GuessBot(self):
        #
 __ __ __

        r = requests.get("https://skribbl.io")

        print(r.text)

    def Bot(self):
        if not os.path.exists(self.Image_Folder):
            os.mkdir(self.Image_Folder)
        self.download_images()
        print(self.imagename)
        Drawing = Image.open(str(self.imagename))
        Drawing.show()
        Drawing = Drawing.convert('RGB')
        width = Drawing.size[0]
        height = Drawing.size[1]
        print(width)
        print(height)
        time.sleep(3)
        self.x1, self.y1 = pyautogui.position()
        print(self.x1, self.y1)

        for y in range(0, height):
            print("Linie:", y, "von: ", height)
            for x in range(0, width):
                Coords = (x, y)
                rgb = Drawing.getpixel(Coords)
                closest_rgb = self.closest_color(rgb)
                self.ColorWithCoords[str(Coords)] = str(closest_rgb)

        print(len(self.ColorWithCoords), "\t", width * height)
        time.sleep(2)
        self.ColorWithCoordsSorted = sorted(self.ColorWithCoords.items(), key=lambda t: t[1])
        for coords, Color in self.ColorWithCoordsSorted:
            coords = coords.strip("(", )
            coords = coords.strip(" ")
            coords = coords.strip(")")
            x, y = coords.split(",")

            if Color != self.SameColor:
                self.SameColor = Color
                print("test")
                time.sleep(0.1)
                self.Change_Color(self.match_color(str(Color)))
            New_X = self.x1 + int(x)
            New_Y = self.y1 + int(y)
            # time.sleep(0.000000000000000000000000000000000000000000000000000000000000001)
            self.click(New_X, New_Y)

if __name__ == "__main__":
    Bot = Gartic_Phone_BOT()
    Bot.Bot()


# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import paddle
import paddle.nn as nn

from .base_conv import BaseConv

class GATConv(BaseConv):
    def __init__(self,
                 input_size,
                 hidden_size,
                 feat_drop=0.6,
                 attn_drop=0.6,
                 num_heads=1,
                 concat=True,
                 activation=None):

        super(GATConv, self).__init__()
        self.hidden_size = hidden_size
        self.num_heads = num_heads
        self.feat_drop = feat_drop
        self.attn_drop = attn_drop
        self.concat = concat

        self.linear = nn.Linear(input_size, num_heads * hidden_size)
        self.weight_src = self.create_parameter(shape=[num_heads, hidden_size])
        self.weight_dst = self.create_parameter(shape=[num_heads, hidden_size])

        self.feat_dropout = nn.Dropout(p=feat_drop)
        self.attn_dropout = nn.Dropout(p=attn_drop)
        self.leaky_relu = nn.LeakyReLU(negative_slope=0.2)

        if isinstance(activation, str):
            activation = getattr(F, activation)
        self.activation = activation

    def _send_attention(self, src_feat, dst_feat, edge_feat):
        alpha = src_feat["src"] + dst_feat["dst"]
        alpha = self.leaky_relu(alpha)
        return {"alpha": alpha, "h": src_feat["h"]}

    def _reduce_attention(self, msg):
        alpha = msg.reduce_softmax(msg["alpha"])
        alpha = paddle.reshape(alpha, [-1, self.num_heads, 1])
        if self.attn_drop > 1e-15:
            alpha = self.attn_dropout(alpha)

        feature = msg["h"]
        feature = paddle.reshape(feature,
                                 [-1, self.num_heads, self.hidden_size])
        feature = feature * alpha
        if self.concat:
            feature = paddle.reshape(feature,
                                     [-1, self.num_heads * self.hidden_size])
        else:
            feature = paddle.mean(feature, axis=1)

        feature = msg.reduce(feature, pool_type="sum")
        return feature

    def forward(self, edge_index, num_nodes, feature):
        if self.feat_drop > 1e-15:
            feature = self.feat_dropout(feature)

        feature = self.linear(feature)
        feature = paddle.reshape(feature,
                                 [-1, self.num_heads, self.hidden_size])

        attn_src = paddle.sum(feature * self.weight_src, axis=-1)
        attn_dst = paddle.sum(feature * self.weight_dst, axis=-1)

        msg = self.send(
            edge_index,
            self._send_attention,
            src_feat={"src": attn_src,
                      "h": feature},
            dst_feat={"dst": attn_dst})

        output = self.recv(
            edge_index, num_nodes, reduce_func=self._reduce_attention, msg=msg)

        if self.activation is not None:
            output = self.activation(output)
        return output