from decimal import Decimal

from . import exchanges
from .user import User
from .utils import log

# TODO this logic isn't scientific in any way, mostly a playground

def determine_limit_price(user: User, symbol: str, purchasing_currency: str) -> Decimal:
    # TODO this is binance-specific right now, refactor this out

    trading_pair = symbol + purchasing_currency

    client = user.binance_client()

    # order depth returns the lowest asks and the highest bids
    # increasing limits returns lower bids and higher asks
    # grab a long-ish order book to get some analytics on the order book

    order_book = client.get_order_book(symbol=trading_pair, limit=100)

    # price that binance reports is at the bottom of the order book
    # looks like they use the bottom of the ask stack to clear market orders (makes sense)
    # cannot determine if the orders in the book are market, limit, or other order types.
    # I wonder if other exchanges expose that sort of information?
    lowest_ask = order_book["asks"][0][0]
    highest_bid = order_book["bids"][0][0]

    ask_difference = Decimal(highest_bid) - Decimal(lowest_ask)

    # TODO can we inspect the low price and determine the volume that was traded at that price point?
    last_day_low = low_over_last_day(user, trading_pair)

    log.warn(
        "price analytics",
        symbol=trading_pair,
        ask_bid_difference=ask_difference,
        ask_bid_percentage_difference=ask_difference / Decimal(lowest_ask) * -100,
        last_day_low_difference=100 - (last_day_low / Decimal(lowest_ask) * 100),
        bid=highest_bid,
        ask=lowest_ask,
        last_day_low=last_day_low,
        reported_price=exchanges.binance_price_for_symbol(trading_pair),
    )

    # TODO calculate momentum, or low price over last 24hrs, to determine the ideal drop price
    # TODO pull percentage drop attempt from user model

    limit_price = min(Decimal(highest_bid), Decimal(lowest_ask) * Decimal(0.97))
    limit_price = min(last_day_low, limit_price)

    # TODO can we inspect the order book depth here? Or general liquidity for the market?
    #      what else can we do to improve our purchase strategy?

    # TODO add option to use the midpoint, or some other position, of the order book instead of the lowest ask

    return limit_price

def low_over_last_day(user: User, trading_pair: str) -> Decimal:
    # import datetime

    # TODO coinbase option is below, but ran into some issues with it that I can't remember
    # candles = coinbase_public_client.get_product_historic_rates(
    #   product_id="PAXG-USD",
    #   granularity=60*60,
    #   start=(datetime.datetime.now() - datetime.timedelta(hours=24)).isoformat(),
    #   stop=datetime.datetime.now().isoformat()
    # )
    # min([candle['low'] for candle in candles])
    # https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-data
    # the API just returns an ordered array, which is insane

    """
      [
      1499040000000,      // Open time
      "0.01634790",       // Open
      "0.80000000",       // High
      "0.01575800",       // Low
      "0.01577100",       // Close
      "148976.11427815",  // Volume
      1499644799999,      // Close time
      "2434.19055334",    // Quote asset volume
      308,                // Number of trades
      "1756.87402397",    // Taker buy base asset volume
      "28.46694368",      // Taker buy quote asset volume
      "17928899.62484339" // Ignore.
    ]
    """

    candles = user.binance_client().get_klines(symbol=trading_pair, interval="1h")

    return Decimal(min([candle[3] for candle in candles]))


# Copyright (c) 2017 The Verde Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
#
# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
#
"""
Add license notice to every source file if not present
"""
import sys
from argparse import ArgumentParser
from pathlib import Path

from pathspec import PathSpec

PROJECT = "verde"
YEAR = "2017"
NOTICE = f"""
# Copyright (c) {YEAR} The {PROJECT.title()} Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
#
# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
#
""".strip()
CHECK_HELP = """
Don't write the files, just return the status. Return code 0 means
nothing would change. Return code 1 means some files lacks the license notice.
"""

def get_gitignore(root):
    """
    Return a PathSpec matching gitignore content if present.

    This function is a modified version of the one present in Black
    (https://github.com/psf/black) available under MIT License.
    """
    gitignore = root / ".gitignore"
    lines = []
    if gitignore.is_file():
        with gitignore.open() as gi_file:
            lines = gi_file.readlines()
    return PathSpec.from_lines("gitwildmatch", lines)

def main():
    """
    Add license notice to every source file if not present or just check
    """
    # Create option parser
    parser = ArgumentParser(
        description=" Add license notice to every source file if not present."
    )
    parser.add_argument(
        "--check", action="store_true", dest="check", default=False, help=CHECK_HELP
    )
    args = parser.parse_args()

    gitignore = get_gitignore(Path("."))

    python_files = [
        path
        for path in Path(".").glob("**/*.py")
        if not str(path).startswith(".")
        if not gitignore.match_file(path)
    ]

    missing_notice_files = []
    for pyfile in python_files:
        code = pyfile.read_text()
        if not code.startswith(NOTICE):
            missing_notice_files.append(pyfile)

    if args.check:
        if missing_notice_files:
            print("License notice is missing in some source files! 💔")
            for pyfile in missing_notice_files:
                print(f"  {pyfile}")
            sys.exit(1)
        else:
            print("All source files have the license notice! 🎉")
            sys.exit(0)
    else:
        print("Successfully added license notice to:")
        for pyfile in missing_notice_files:
            code = pyfile.read_text()
            pyfile.write_text("\n".join([NOTICE, code]))
            print(f"  {pyfile}")
        sys.exit(0)

if __name__ == "__main__":
    main()


# Copyright 2019, The TensorFlow Federated Authors.  #
# 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 collections

import tensorflow as tf
import tensorflow_federated as tff

from tensorflow_federated.python.research.utils import aggregate_fns

def create_weights_delta(input_size=2, hidden_size=5, constant=0):
  """Returns deterministic weights delta for a linear model."""
  kernel = constant + tf.reshape(
      tf.range(input_size * hidden_size, dtype=tf.float32),
      [input_size, hidden_size])
  bias = constant + tf.range(hidden_size, dtype=tf.float32)
  return collections.OrderedDict([('dense/kernel', kernel),
                                  ('dense/bias', bias)])

class ClipNormAggregateFnTest(tf.test.TestCase):

  def global_norm(self, value):
    return tf.linalg.global_norm(tf.nest.flatten(value))

  def test_clip_by_global_norm(self):
    clip_norm = 20.0
    aggregate_fn = aggregate_fns.build_clip_norm_aggregate_fn(clip_norm)
    # Global l2 norms [17.74824, 53.99074].
    deltas = [create_weights_delta(), create_weights_delta(constant=10)]
    deltas_type = tff.framework.type_from_tensors(deltas[0])
    weights = [1., 1.]

    @tff.federated_computation(
        tff.FederatedType(deltas_type, tff.CLIENTS),
        tff.FederatedType(tf.float32, tff.CLIENTS))
    def federated_aggregate_test(deltas, weights):
      state = tff.federated_value(aggregate_fn.initialize(), tff.SERVER)
      return aggregate_fn(state, deltas, weights)

    federated_aggregate_test.type_signature.result.check_equivalent_to(
        tff.StructType((
            tff.FederatedType(
                aggregate_fns.ClipNormAggregateState(
                    clip_norm=tf.float32, max_norm=tf.float32), tff.SERVER),
            tff.FederatedType(deltas_type, tff.SERVER),
        )))

    state, mean = federated_aggregate_test(deltas, weights)

    expected_clipped = []
    for delta in deltas:
      flat = tf.nest.flatten(delta)
      clipped, _ = tf.clip_by_global_norm(flat, clip_norm)
      expected_clipped.append(tf.nest.pack_sequence_as(delta, clipped))
    expected_mean = tf.nest.map_structure(lambda a, b: (a + b) / 2,
                                          *expected_clipped)
    self.assertEqual(state.clip_norm, tf.constant(20.0, tf.float32))
    self.assertEqual(state.max_norm, tf.constant(53.99074, tf.float32))
    tf.nest.map_structure(self.assertAllEqual, expected_mean, mean)

class FixedClipNormProcessTest(tf.test.TestCase):

  def test_clip_by_global_norm(self):
    clip_norm = 20.0
    test_deltas = [create_weights_delta(), create_weights_delta(constant=10)]
    update_type = tff.framework.type_from_tensors(test_deltas[0])
    aggregate_fn = aggregate_fns.build_fixed_clip_norm_mean_process(
        clip_norm=clip_norm, model_update_type=update_type)

    self.assertEqual(
        aggregate_fn.next.type_signature,
        tff.FunctionType(
            parameter=(
                tff.FederatedType((), tff.SERVER),
                tff.FederatedType(update_type, tff.CLIENTS),
                tff.FederatedType(tf.float32, tff.CLIENTS),
            ),
            result=collections.OrderedDict(
                state=tff.FederatedType((), tff.SERVER),
                result=tff.FederatedType(update_type, tff.SERVER),
                measurements=tff.FederatedType(
                    aggregate_fns.NormClippedAggregationMetrics(
                        max_global_norm=tf.float32, num_clipped=tf.int32),
                    tff.SERVER)),
        ))

    state = aggregate_fn.initialize()
    weights = [1., 1.]
    output = aggregate_fn.next(state, test_deltas, weights)

    expected_clipped = []
    for delta in test_deltas:
      clipped, _ = tf.clip_by_global_norm(tf.nest.flatten(delta), clip_norm)
      expected_clipped.append(tf.nest.pack_sequence_as(delta, clipped))
    expected_mean = tf.nest.map_structure(lambda a, b: (a + b) / 2,
                                          *expected_clipped)
    self.assertAllClose(expected_mean, output['result'])

    # Global l2 norms [17.74824, 53.99074].
    metrics = output['measurements']
    self.assertAlmostEqual(metrics.max_global_norm, 53.99074, places=5)
    self.assertEqual(metrics.num_clipped, 1)

if __name__ == '__main__':
  tf.test.main()


#!/usr/bin/env python3
# Copyright (c) 2018-2021 The Xaya developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test spendability of premine and that P2SH is enforced correctly for it."""

from test_framework.test_framework import BitcoinTestFramework
from test_framework.messages import *
from test_framework.util import *

import codecs

PREMINE_VALUE = Decimal ('222222222')
PREMINE_ADDRESS = 'dHNvNaqcD7XPDnoRjAoyfcMpHRi5upJD7p'
PREMINE_PRIVKEYS = ['b69iyynFSWcU54LqXisbbqZ8uTJ7Dawk3V3yhht6ykxgttqMQFjb',
                    'b3fgAKVQpMj24gbuh6DiXVwCCjCbo1cWiZC2fXgWEU9nXy6sdxD5']
PREMINE_PUBKEYS = [
  '03c278d06b977e67b8ea45ef24e3c96a9258c47bc4cce3d0b497b690d672497b6e',
  '0221ac9dc97fe12a98374344d08b458a9c2c1df9afb29dd6089b94a3b4dc9ad570',
]

class PremineTest(BitcoinTestFramework):
  def set_test_params(self):
    self.setup_clean_chain = True
    self.num_nodes = 1

  def skip_test_if_missing_module (self):
    self.skip_if_no_wallet ()

  def run_test(self):
    node = self.nodes[0]
    node.importaddress (PREMINE_ADDRESS)

    # Find basic data about the genesis coinbase tx.
    genesis = node.getblock (node.getblockhash (0), 2)
    assert_equal (len (genesis['tx']), 1)
    tx = genesis['tx'][0]
    txid = tx['hash']
    assert_equal (len (tx['vout']), 1)
    out = tx['vout'][0]
    assert_equal (out['value'], PREMINE_VALUE)
    assert_equal (out['scriptPubKey']['address'], PREMINE_ADDRESS)

    # Accessing it should work normally (upstream Bitcoin/Namecoin have a
    # special check that disallows the genesis coinbase with getrawtransaction,
    # as it is not spendable).
    node.gettransaction (txid)
    assert_equal (node.getrawtransaction (txid, False, genesis['hash']),
                  tx['hex'])

    # The coinbase txout should be in the UTXO set.
    utxo = node.gettxout (txid, 0)
    assert utxo is not None

    # Check balance of node and then import the keys for the premine
    # and check again.  It should be available as spendable.
    assert_equal (node.getbalance (), 0)
    for key in PREMINE_PRIVKEYS:
      node.importprivkey (key, 'premine')
    pubkeys = []
    for addr in node.getaddressesbylabel ('premine'):
      data = node.getaddressinfo (addr)
      if (not data['isscript']) and (not data['iswitness']):
        pubkeys.append (data['pubkey'])
    assert_equal (set (pubkeys), set (PREMINE_PUBKEYS))
    p2sh = node.addmultisigaddress (1, PREMINE_PUBKEYS)
    assert_equal (p2sh['address'], PREMINE_ADDRESS)
    node.rescanblockchain ()
    assert_equal (node.getbalance (), PREMINE_VALUE)

    # Construct a raw tx spending the premine.
    addr = node.getnewaddress ()
    inputs = [{"txid": txid, "vout": 0}]
    outputs = {addr: Decimal ('123456')}
    rawTx = node.createrawtransaction (inputs, outputs)

    # Try to "sign" it by just adding the redeem script, which would have been
    # valid before the P2SH softfork.  Doing so should fail, which verifies that
    # P2SH is enforced right from the start and thus that the premine is safe.
    data = node.getaddressinfo (PREMINE_ADDRESS)
    redeemScript = data['hex']
    # Prepend script size, so that it will correctly push the script hash
    # to the stack.
    redeemScript = ("%02x" % (len (redeemScript) // 2)) + redeemScript
    forgedTx = tx_from_hex (rawTx)
    forgedTx.vin[0].scriptSig = codecs.decode (redeemScript, 'hex_codec')
    forgedTx = forgedTx.serialize ().hex ()
    assert_raises_rpc_error (-26, "not valid",
                             node.sendrawtransaction, forgedTx, 0)

    # Sign and send the raw tx, should succeed.
    signed = node.signrawtransactionwithwallet (rawTx)
    assert signed['complete']
    signedTx = signed['hex']
    sendId = node.sendrawtransaction (signedTx, 0)
    node.generate (1)
    assert_equal (node.gettransaction (sendId)['confirmations'], 1)

if __name__ == '__main__':
  PremineTest().main()


from re import search
from setuptools import setup, find_packages

with open("graphql/__init__.py") as init_file:
    version = search('__version__ = "(.*)"', init_file.read()).group(1)

with open("README.md") as readme_file:
    readme = readme_file.read()

setup(
    name="GraphQL-core-next",
    version=version,
    description="GraphQL-core-next is a Python port of GraphQL.js,"
    " the JavaScript reference implementation for GraphQL.",
    long_description=readme,
    long_description_content_type="text/markdown",
    keywords="graphql",
    url="https://github.com/graphql-python/graphql-core-next",
    author="Christoph Zwerschke",
    author_email="cito@online.de",
    license="MIT license",
    # PEP-561: https://www.python.org/dev/peps/pep-0561/
    package_data={"graphql": ["py.typed"]},
    classifiers=[
        "Development Status :: 5 - Production/Stable",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.6",
        "Programming Language :: Python :: 3.7",
    ],
    install_requires=[],
    python_requires=">=3.6",
    test_suite="tests",
    tests_require=[
        "pytest",
        "pytest-asyncio",
        "pytest-cov",
        "pytest-describe",
        "black",
        "flake8",
        "mypy",
        "tox",
        "python-coveralls",
    ],
    packages=find_packages(include=["graphql"]),
    include_package_data=True,
    zip_safe=False,
)


'''
Flask-Admin has issues when used with Application Factory pattern.
Since I had issues when using it with pytest, I had to move the admin
instance to "register_admin" method.

https://github.com/flask-admin/flask-admin/issues/910
'''
from flask import redirect, url_for, request
from flask_admin import Admin
from flask_admin.menu import MenuLink
from flask_admin.contrib.sqla import ModelView
from flask_login import current_user

from app.extensions import db
from app.auth.models import User, Role, PreAllowedUser
from app.main.models import (
    Product, Specification, StockProduct, Stock, Order, OrderItem, Transaction)

class ProtectedModelView(ModelView):
    column_display_pk = True
    def is_accessible(self):
        return (current_user.is_authenticated and
                current_user.is_administrator())

    def inaccessible_callback(self, name, **kwargs):
        return redirect(url_for('auth.login', next=request.url))

def register_admin(app):
    admin = Admin(app=app, template_mode='bootstrap3')
    admin.add_link(MenuLink(name='Voltar', url=('/')))
    admin.add_views(
        ProtectedModelView(User, db.session),
        ProtectedModelView(Role, db.session),
        ProtectedModelView(PreAllowedUser, db.session),
        ProtectedModelView(Product, db.session),
        ProtectedModelView(Specification, db.session),
        ProtectedModelView(StockProduct, db.session),
        ProtectedModelView(Stock, db.session),
        ProtectedModelView(Order, db.session),
        ProtectedModelView(OrderItem, db.session),
        ProtectedModelView(Transaction, db.session),
    )