'''

This module exports one function, makenetlist, which returns a netlist
representation, when given a list of partial nets from the graphic submodule.

Internally, the NetInfo class is used to group all the functions of generating
the netlist together.  It uses makenetgroups.makegroups to do the initial
grouping of the partial nets, and then handles globals, similar names,
invisible pins, etc.

'''

import re
from collections import defaultdict
from .makenetgroups import makegroups
from ..utility import IndexedString

class NetInfo(object):

    def warn(self, *s):
        self.warnings.append('Warning: %s\n' % '\n          '.join(s))

    @staticmethod
    def sortnames(names):
        ''' Sort names higher in the hierarchy before lower names.
        '''
        return sorted(names, key= lambda x:(x.count('/'), x))

    @staticmethod
    def getshortname(net_id):
        return net_id.rsplit('/',1)[-1]

    def canonicalize(self,
                id_pattern = re.compile('([a-zA-Z]+|[0-9]+|\+| \/ )')):
        ''' Convert all net names into canonical versions:
                1) uppercase all letters
                2) remove leading zeros from numerical portions
                3) convert all non-alphanumeric characters except '+'
                    into underscores
                4) convert all separators ' / ' into '/'
                5) Warn about any changes that result in two
                    previously different names being made the same.
        '''

        def canonical(s):
            def id_parts(s):
                for s in id_pattern.split(s):
                    if not s:
                        pass
                    elif s.isalpha():
                        yield s.upper()
                    elif s.isdigit():
                        yield str(int(s, 10))
                    elif s == ' / ':
                        yield '/'
                    elif s == '+':
                        yield '+'
                    else:
                        yield '_'
            return IndexedString(''.join(id_parts(s)))

        def rename(new, old):
            new = self.by_net_id[new]
            if new is old:
                return
            oldname = old.original or old
            newname = new.original or new
            if new.group is not None:
                self.warn('Similarly named signals merged:', '"%s" into "%s"' % (oldname, newname))
            new.merge(old)
            new.items.update(old.items)
            new.original = new.original or oldname
            old.group.remove(old)
            old.group = None
            old.items.clear()
            del self.by_net_id[old]

        def getbyname():
            # Dictionary indexed by canonicalized signal name of
            #     Dictionary indexed by non-canonicalized signal name of
            #        Set of new net IDs, old net IDs
            #           Set of net ids
            byname = defaultdict(dict)
            for oldid in self.by_net_id:
                newid = canonical(oldid)
                oldname = oldid.rsplit(' / ', 1)[1]
                newname = newid.rsplit('/', 1)[1]
                byname[newname].setdefault(oldname, []).append((newid, oldid))
            return byname.itervalues()

        def convertdict(namedict):
            namelist = list(namedict.iteritems())
            namelist.sort(key=lambda x: (len(x[1]), x[0], x[1]))
            if len(namelist) > 1:
                self.warn('The following net names are considered to be the same name:',
                        ', '.join(x[0] for x in namelist))
            for oldname, id_tuples in namelist:
                for newid, oldid in sorted(id_tuples):
                    rename(newid, oldid)

        for namedict in getbyname():
            convertdict(namedict)

    def find_nettypes(self):
        ''' Determine the type of net for every NetId
        '''
        def _assert(what, message):
            if not what:
                    self.warn(message, net_id)

        implicitglobals = set()
        explicitglobals = set()

        for net_id in self.by_net_id:
            pins = net_id.find_pins
            slabels = net_id.find_slabels
            hlabels = net_id.find_hlabels
            glabels = net_id.find_glabels
            labels = net_id.find_labels

            if pins:
                implicitglobals.add(net_id)
                _assert(not (labels or slabels or hlabels), 'Local sheet signal connected automatically to global')
                _assert(not glabels, 'Global signal used both implicitly and explicitly')
            elif slabels:
                _assert(not glabels, 'Global label misused as hierarchical')
                _assert(len(slabels) <= 1, 'Multiple connections to label on instantiating sheet')
                _assert(len(hlabels) + len(glabels) <= 1, 'Multiple entry points onto sheet for hierarchical label')
            elif hlabels:
                _assert(0, 'Hierarchical label not connected in instantiating sheet')
            elif glabels:
                explicitglobals.add(net_id)
                _assert(len(glabels) <= 1, 'Multiple connections to global signal on sheet')

        return implicitglobals, explicitglobals

    def connect_globals(self):
        ''' Connect the globals together across sheets
        '''
        globaldict = {}

        def merged(srcset):
            result = defaultdict(set)
            for item in srcset:
                globalnet = self.by_net_id[self.getshortname(item)]
                result[globalnet].add(item)
                globalnet.merge(item)
            return result

        implicit, explicit = self.find_nettypes()
        implicit = merged(implicit)
        explicit = merged(explicit)

        for net_id, old_ids in explicit.iteritems():
            if len(old_ids) == 1:
                net_id, = old_ids
                if len(net_id.group.find_real_pins) > 1:
                    self.warn('Global net only used on one page:', net_id)

        for net_id in (set(implicit) & set(explicit)):
            self.warn('Global net used both implicitly and explicitly:',
                    'Explicit: %s' % sorted(explicit[net_id])[0],
                    'Implicit: %s' % sorted(implicit[net_id])[0])

    def shorten_names(self):
        ''' Strip the hierarchy off the front of names where possible
        '''
        getshortname = self.getshortname
        by_net_id = self.by_net_id
        shortnames = defaultdict(set)
        for net_ids in self.allgroups:
            for net_id in net_ids:
                shortnames[getshortname(net_id)].add(net_id)

        for shortname, nameset in shortnames.iteritems():
            groups = [(x.group, x) for x in nameset]
            groups = dict(((id(x), (x, y)) for (x, y) in groups))
            groups = list(groups.itervalues())
            if len(groups) == 1:
                shortname = self.by_net_id[shortname]
                if shortname.group is None:
                    shortname.merge(groups[0][0])
                else:
                    assert shortname.group is groups[0][0]
            else:
                assert groups
                self.warn('Unconnected local nets of same name:',
                    '; '.join(sorted((x[1] for x in groups))))

    def namegroups(self):
        ''' Store the names associated with each group inside it.
        '''
        getshortname = self.getshortname

        def selectshortest(names):
            return self.sortnames(names)[0]

        for group in self.allgroups:
            if group:
                names = defaultdict(set)
                for net_id in group:
                    names[getshortname(net_id)].add(net_id)
                names.pop('?', None)  # Use "?" to mean "no net"
                names = [selectshortest(x) for x in names.itervalues()]
                names = self.sortnames(names)
                if len(names) > 1:
                    self.warn('Aliased net names:', str(names))
                group.names = tuple(names)
            else:
                group.names = ()

    def find_pins(self):
        ''' Rummage through the group items and pull the pins out
            into a separate set.
        '''
        goodgroups = []
        self.allgroups, oldgroups = goodgroups, self.allgroups
        for group in oldgroups:
            if not group and not group.items:
                continue
            goodgroups.append(group)
            group.pins = pins = set()
            for pin in group.find_real_pins:
                pin.group = group
                pins.add(pin)
                net_ids = pin.net_ids
                if not net_ids:
                    continue
                others = pin.partial.net_ids - set(net_ids)
                if not others:
                    continue
                self.warn('Visible net connected to invisible pin',
                            '%s connects %s and %s' %
                            (pin.full_info, ', '.join(net_ids), ', '.join(others)))

            if len(pins) > 1:
                continue

            name = ', '.join(group.names)
            for pin in pins:
                if name:
                    self.warn('Net connects no other pins -- net name ignored',
                            'Net %s, %s' % (name, pin.full_info))
                else:
                    if not pin.partial.single_pin:
                        self.warn('Pin not connected in final netlist', pin.full_info)
                break
            else:
                goodgroups.pop()
                if name:
                    self.warn('Named net ignored (contains no pins)', name)
                else:
                    bigspace = '\n' + 20 * ' '
                    self.warn('Unconnected net',
                        'Elements:%s%s' % (bigspace,
                            bigspace.join(sorted(x.full_info for x in group.items))))

    def kill_aliased_pins(self, pinlist):
        groups = set((id(pin.group) for pin in pinlist))
        if len(groups) > 1:
            bigspace = '\n' + 20 * ' '
            self.warn('Aliased pins connected to different nets',
                    'Elements:%s%s' % (bigspace,
                        bigspace.join(sorted('%s %s' % (x.full_info, x.group.names) for x in pinlist))))
        pinlist.pop()
        for pin in pinlist:
            pin.group.pins.remove(pin)

    def check_aliased_pins(self):
        ''' For parts with multiple sub-parts and invisible shared pins,
            consolidate the shared pins.
        '''
        partdict = defaultdict(dict)
        for group in self.allgroups:
            for pin in group.pins:
                partdict[pin.component.refdes].setdefault(pin.pinnum, []).append(pin)
        aliasedpins = []
        for pindict in partdict.itervalues():
            for pinnum, info in pindict.iteritems():
                if len(info)> 1:
                    self.kill_aliased_pins(info)

    def getinfo(self):
        return[ (group.names, group.pins) for group in self.allgroups]

    def __init__(self, allpartials, warnings):
        self.warnings = warnings
        self.allpartials = allpartials
        self.by_net_id, self.allgroups = makegroups(allpartials)
        self.canonicalize()
        self.connect_globals()
        self.shorten_names()
        self.namegroups()
        self.find_pins()
        self.check_aliased_pins()

class OneNet(object):
    def __init__(self, names, pins):
        self.__dict__.update(locals())

    def __str__(self):
        return 'Net %s\n     %s' % (
            ', '.join(self.names),
            '\n     '.join(sorted((x.full_info for x in self.pins))))

def makenetlist(allpartials, warnings):
    mylist = NetInfo(allpartials, warnings).getinfo()
    mylist = [OneNet(*x) for x in mylist]
    return mylist


"""
Item 84: Write Docstrings for Every Function, Class, and Module

Write documentation for every module, class, method, and function using docstrings.
Keep them up-to_date.
For modules introduce the content and any important classes or functions.
For classes document behavior, important attributes, and subclass behavior.
For functions and methods document every weakness, returned value, raised exceptions,
and other behaviors.
For type annotations, omit providing information in the docstring that is already in the annotations.

"""

#!/usr/bin/env PYTHONHASHSEED=1234 python3

# Reproduce book environment
import random
random.seed(1234)

import logging
from pprint import pprint
from sys import stdout as STDOUT

# Write all output to a temporary directory
import atexit
import gc
import io
import os
import tempfile

TEST_DIR = tempfile.TemporaryDirectory()
atexit.register(TEST_DIR.cleanup)

# Make sure Windows processes exit cleanly
OLD_CWD = os.getcwd()
atexit.register(lambda: os.chdir(OLD_CWD))
os.chdir(TEST_DIR.name)

def close_open_files():
    everything = gc.get_objects()
    for obj in everything:
        if isinstance(obj, io.IOBase):
            obj.close()

atexit.register(close_open_files)

# Example 1
def palindrome(word):
    """Return True if the given word is a palindrome."""
    return word == word[::-1]

assert palindrome('tacocat')
assert not palindrome('banana')

# Example 2
print(repr(palindrome.__doc__))

# Example 3
"""Library for finding linguistic patterns in words.
Testing how words relate to each other can be tricky sometimes!
This module provides easy ways to determine when words you've
found have special properties.
Available functions:
- palindrome: Determine if a word is a palindrome.
- check_anagram: Determine if two words are anagrams.
...
"""

# Example 4
class Player:
    """Represents a player of the game.
    Subclasses may override the 'tick' method to provide
    custom animations for the player's movement depending
    on their power level, etc.
    Public attributes:
    - power: Unused power-ups (float between 0 and 1).
    - coins: Coins found during the level (integer).
    """

# Example 5
import itertools
def find_anagrams(word, dictionary):
    """Find all anagrams for a word.
    This function only runs as fast as the test for
    membership in the 'dictionary' container.
    Args:
        word: String of the target word.
        dictionary: collections.abc.Container with all
            strings that are known to be actual words.
    Returns:
        List of anagrams that were found. Empty if
        none were found.
    """
    permutations = itertools.permutations(word, len(word))
    possible = (''.join(x) for x in permutations)
    found = {word for word in possible if word in dictionary}
    return list(found)

assert find_anagrams('pancakes', ['scanpeak']) == ['scanpeak']

# Example 6
# Check types in this file with: python -m mypy <path>
"""
from typing import Container, List

def find_anagrams(word: str,
                  dictionary: Container[str]) -> List[str]:
    pass
"""
# Example 7
# Check types in this file with: python -m mypy <path>

#from typing import Container, List

#def find_anagrams(word: str,
#                  dictionary: Container[str]) -> List[str]:
#    """Find all anagrams for a word.
#    This function only runs as fast as the test for
#    membership in the 'dictionary' container.
#    Args:
#        word: Target word.
#        dictionary: All known actual words.
#    Returns:
#        Anagrams that were found.
#    """
#    pass


import ast
import operator as op
from typing import Sequence

from .exceptions import *
from .types import *

__all__ = ("DraconicConfig", "OperatorMixin", "zip_star")

# ===== config =====
DISALLOW_PREFIXES = ["_", "func_"]
DISALLOW_METHODS = ["format", "format_map", "mro"]

class DraconicConfig:
    """A configuration object to pass into the Draconic interpreter."""

    def __init__(
        self,
        max_const_len=200_000,
        max_loops=10_000,
        max_statements=100_000,
        max_power_base=1_000_000,
        max_power=1_000,
        disallow_prefixes=None,
        disallow_methods=None,
        default_names=None,
        builtins_extend_default=True,
        max_int_size=64,
        max_recursion_depth=50,
    ):
        """
        Configuration object for the Draconic interpreter.

        :param int max_const_len: The maximum length literal that should be allowed to be constructed.
        :param int max_loops: The maximum total number of loops allowed per execution.
        :param int max_statements: The maximum total number of statements allowed per execution.
        :param int max_power_base: The maximum power base (x in x ** y)
        :param int max_power: The maximum power (y in x ** y)
        :param list disallow_prefixes: A list of str - attributes starting with any of these will be inaccessible
        :param list disallow_methods: A list of str - methods named these will not be callable
        :param dict default_names: A dict of str: Any - default names in the runtime
        :param bool builtins_extend_default: If False, ``builtins`` to the interpreter overrides default names
        :param int max_int_size: The maximum allowed size of integers (-2^(pow-1) to 2^(pow-1)-1). Default 64.
                                 Integers can technically reach up to double this size before size check.
                                 *Not* the max value!
        :param int max_recursion_depth: The maximum allowed recursion depth.
        """
        if disallow_prefixes is None:
            disallow_prefixes = DISALLOW_PREFIXES
        if disallow_methods is None:
            disallow_methods = DISALLOW_METHODS

        self.max_const_len = max_const_len
        self.max_loops = max_loops
        self.max_statements = max_statements
        self.max_power_base = max_power_base
        self.max_power = max_power
        self.max_int_size = max_int_size
        self.min_int = -(2 ** (max_int_size - 1))
        self.max_int = (2 ** (max_int_size - 1)) - 1
        self.disallow_prefixes = disallow_prefixes
        self.disallow_methods = disallow_methods
        self.builtins_extend_default = builtins_extend_default
        self.max_recursion_depth = max_recursion_depth

        # types
        self._list = safe_list(self)
        self._dict = safe_dict(self)
        self._set = safe_set(self)
        self._str = safe_str(self)

        # default names
        if default_names is None:
            default_names = self._default_names()
        self.default_names = default_names

    @property
    def list(self):
        return self._list

    @property
    def dict(self):
        return self._dict

    @property
    def set(self):
        return self._set

    @property
    def str(self):
        return self._str

    def _default_names(self):
        return {
            "True": True,
            "False": False,
            "None": None,
            # functions
            "bool": bool,
            "int": int,
            "float": float,
            "str": self.str,
            "tuple": tuple,
            "dict": self.dict,
            "list": self.list,
            "set": self.set,
        }

# ===== operators =====
class OperatorMixin:
    """A mixin class to provide the operators."""

    def __init__(self, config):
        """
        :type config: draconic.helpers.DraconicConfig
        """
        self._config = config

        self.operators = {
            # binary
            ast.Add: self._safe_add,
            ast.Sub: self._safe_sub,
            ast.Mult: self._safe_mult,
            ast.Div: op.truediv,
            ast.FloorDiv: op.floordiv,
            ast.Pow: self._safe_power,
            ast.Mod: op.mod,
            ast.LShift: self._safe_lshift,
            ast.RShift: op.rshift,
            ast.BitOr: op.or_,
            ast.BitXor: op.xor,
            ast.BitAnd: op.and_,
            ast.Invert: op.invert,
            # unary
            ast.Not: op.not_,
            ast.USub: op.neg,
            ast.UAdd: op.pos,
            # comparison
            ast.Eq: op.eq,
            ast.NotEq: op.ne,
            ast.Gt: op.gt,
            ast.Lt: op.lt,
            ast.GtE: op.ge,
            ast.LtE: op.le,
            ast.In: lambda x, y: op.contains(y, x),
            ast.NotIn: lambda x, y: not op.contains(y, x),
            ast.Is: lambda x, y: x is y,
            ast.IsNot: lambda x, y: x is not y,
        }

    def _safe_power(self, a, b):
        """Exponent: limit power base and power to prevent CPU-locking computation"""
        if abs(a) > self._config.max_power_base or abs(b) > self._config.max_power:
            _raise_in_context(NumberTooHigh, f"{a} ** {b} is too large of an exponent")
        result = a**b
        if isinstance(result, int) and (result < self._config.min_int or result > self._config.max_int):
            _raise_in_context(NumberTooHigh, "This exponent would create a number too large")
        return result

    def _safe_mult(self, a, b):
        """Multiplication: limit the size of iterables that can be created, and the max size of ints"""
        # sequences can only be multiplied by ints, so this is safe
        self._check_binop_operands(a, b)
        if isinstance(b, int) and b * approx_len_of(a) > self._config.max_const_len:
            _raise_in_context(IterableTooLong, "Multiplying these two would create something too long")
        if isinstance(a, int) and a * approx_len_of(b) > self._config.max_const_len:
            _raise_in_context(IterableTooLong, "Multiplying these two would create something too long")
        result = a * b
        if isinstance(result, int) and (result < self._config.min_int or result > self._config.max_int):
            _raise_in_context(NumberTooHigh, "Multiplying these two would create a number too large")
        return result

    def _safe_add(self, a, b):
        """Addition: limit the size of iterables that can be created, and the max size of ints"""
        self._check_binop_operands(a, b)
        if approx_len_of(a) + approx_len_of(b) > self._config.max_const_len:
            _raise_in_context(IterableTooLong, "Adding these two would create something too long")
        result = a + b
        if isinstance(result, int) and (result < self._config.min_int or result > self._config.max_int):
            _raise_in_context(NumberTooHigh, "Adding these two would create a number too large")
        return result

    def _safe_sub(self, a, b):
        """Addition: limit the max size of ints"""
        self._check_binop_operands(a, b)
        result = a - b
        if isinstance(result, int) and (result < self._config.min_int or result > self._config.max_int):
            _raise_in_context(NumberTooHigh, "Subtracting these two would create a number too large")
        return result

    def _safe_lshift(self, a, b):
        """Left Bit-Shift: limit the size of integers/floats to prevent CPU-locking computation"""
        self._check_binop_operands(a, b)

        if isinstance(b, int) and b > self._config.max_int_size - 2:
            _raise_in_context(NumberTooHigh, f"{a} << {b} is too large of a shift")

        result = a << b
        if isinstance(result, int) and (result < self._config.min_int or result > self._config.max_int):
            _raise_in_context(NumberTooHigh, "Shifting these two would create a number too large")

        return a << b

    def _check_binop_operands(self, a, b):
        """Ensures both operands of a binary operation are safe (int limit)."""
        if isinstance(a, int) and (a < self._config.min_int or a > self._config.max_int):
            _raise_in_context(NumberTooHigh, "This number is too large")
        if isinstance(b, int) and (b < self._config.min_int or b > self._config.max_int):
            _raise_in_context(NumberTooHigh, "This number is too large")

# ==== other utils ====
def zip_star(a: Sequence, b: Sequence, star_index: int):
    """
    Like zip(a, b), but zips the element at ``a[star_index]`` with a list of 0..len(b) elements such that every other
    element of ``a`` maps to exactly one element of ``b``.

    >>> zip_star(['a', 'b', 'c'], [1, 2, 3, 4], star_index=1)  # like a, *b, c = [1, 2, 3, 4]
    [('a', 1), ('b', [2, 3]), ('c', 4)]
    >>> zip_star(['a', 'b', 'c'], [1, 2], star_index=1)  # like a, *b, c = [1, 2]
    [('a', 1), ('b', []), ('c', 2)]

    Requires ``len(b) >= len(a) - 1`` and ``star_index < len(a)``.
    """
    if not 0 <= star_index < len(a):
        raise IndexError("'star_index' must be a valid index of 'a'")
    if not len(b) >= len(a) - 1:
        raise ValueError("'b' must be no more than 1 shorter than 'a'")

    length_difference = len(b) - (len(a) - 1)

    yield from zip(a[:star_index], b[:star_index])
    yield a[star_index], b[star_index : star_index + length_difference]
    yield from zip(a[star_index + 1 :], b[star_index + length_difference :])


# -*- coding: utf-8 -*-

import json

from jwt.jwt import JWT

from oidc.tests import (
    mock,
    TestBase,
)

class AccessTokenRequestTest(TestBase):

    @property
    def target(self):
        from oidc.authorizationcodeflow import AccessTokenRequest
        return AccessTokenRequest

    def setUp(self):
        super().setUp()

        self.code = self.store.issue_authorization_code(self.client,
                                                        self.owner,
                                                        {'openid', 'scope'})

    def test_answer(self):
        inst = self.target()
        inst.update({
            'client_id': self.client.id,
            'grant_type': 'authorization_code',
            'code': self.code.get_code(),
        })
        inst.validate()

        with mock.patch.object(self.provider, 'authorize_client',
                               return_value=True):
            resp = inst.answer(self.provider, self.owner)

        jwt = JWT(self.jwkset.copy())

        self.assertTrue(jwt.verify(resp.id_token))

        id_token = json.loads(jwt.decode(resp.id_token).decode('utf8'))
        self.assertEqual(
            id_token['at_hash'],
            self.provider.left_hash(self.client.get_jws_alg(),
                                    resp.access_token))


from flask import Flask, redirect, url_for, session, request,send_file,render_template
from flask_oauthlib.client import OAuth, OAuthException
from PIL import Image
from io import BytesIO
import facebook as fb
import sys
import logging
import os
import requests
import base64

FACEBOOK_APP_ID = os.environ['FACEBOOK_APP_ID']
FACEBOOK_APP_SECRET = os.environ['FACEBOOK_APP_SECRET']

app = Flask(__name__)
app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.ERROR)
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)

facebook = oauth.remote_app(
    'facebook',
    consumer_key=FACEBOOK_APP_ID,
    consumer_secret=FACEBOOK_APP_SECRET,
    request_token_params={'scope': 'email'},
    base_url='https://graph.facebook.com',
    request_token_url=None,
    access_token_url='/oauth/access_token',
    access_token_method='GET',
    authorize_url='https://www.facebook.com/dialog/oauth'
)

def manipulate(url):
    i = Image.open(BytesIO(url.content))
    nn = Image.open('netneutrality.png')
    ii = i.copy()
    width,height = ii.size
    ii.paste(nn,(width-200,height-200),nn)
    byte_io = BytesIO()
    ii.save(byte_io, 'PNG')
    return send_file(byte_io, mimetype='image/png',attachment_filename="nnprofilepic.png",as_attachment=True),ii

@app.route('/')
def index():
    if 'oauth_token' in session:
        graph = fb.GraphAPI(access_token=session['oauth_token'][0], version='2.5')
        print(session['oauth_token'][0])
        profile_img = graph.get_object('me/picture?width=9999&redirect=false')['data']['url']
        name = graph.get_object('me')['name']
        r = requests.get(profile_img)
        ii = manipulate(r)[1]
        imgserve = BytesIO()
        ii.save(imgserve, 'PNG')
        imgserve.seek(0)
        img_tag = base64.b64encode(imgserve.getvalue()).decode()
        return render_template('result.html',imgsrc=img_tag,name = name ,url = r.url[8:])

    else:
        return render_template('index.html')

@app.route('/connect')
def login():
    callback = url_for(
        'facebook_authorized',
        next=request.args.get('next') or request.referrer or None,
        _external=True
    )
    return facebook.authorize(callback=callback)

@app.route('/login/authorized')
def facebook_authorized():
    resp = facebook.authorized_response()
    if resp is None:
        return 'Access denied: reason=%s error=%s' % (
            request.args['error_reason'],
            request.args['error_description']
        )
    if isinstance(resp, OAuthException):
        return 'Access denied: %s' % resp.message

    session['oauth_token'] = (resp['access_token'],'')
    return redirect('/')

@app.errorhandler(404)
def page_not_found(error):
    return render_template('404.html'), 404

@facebook.tokengetter
def get_facebook_oauth_token():
    return session.get('oauth_token')

if __name__ == '__main__':
    app.run()


"""
AI driven entity.
"""
from enum import Enum

import pygame

from map import Map
from config import CONFIG
from consts import Screens, IMAGE_FOLDER, BASE_SPEED, GRAVITY_CONST
from animation import Animation, BotAnimationType

class BotMoveType(Enum):
    UP = 0
    RIGHT = 1
    LEFT= 2
    STAND = 3

class Bot:

    def __init__(self, x_pos=0, y_pos=0, x_speed=0, y_speed=0):
        self.bot_move = BotMoveType.RIGHT
        self.x_pos = x_pos
        self.y_pos = y_pos
        self.x_speed = x_speed
        self.y_speed = y_speed
        self.max_speed = 2
        self.jump_count = 0
        self.jump_limit = 1
        self.jump_power = 7
        self.camera_x = 0
        self.camera_y = 0
        self.width = 64
        self.height = 64
        self.animation = Animation(IMAGE_FOLDER + 'player.png', self.width, self.height)

    def collision_detection(self, map):

        # Move on keypress
        if self.bot_move == BotMoveType.RIGHT:
            self.x_speed += BASE_SPEED
        elif self.bot_move == BotMoveType.LEFT:
            self.x_speed -= BASE_SPEED
        else:
            self.x_speed = 0

        # Check if the bottom of the player is masked
        on_ground = Map.is_masked_h_line(map, 4, self.x_pos, self.y_pos + self.height + self.y_speed, self.width / 2)
        if on_ground:
            self.y_speed = 0
            self.jump_count = 0
        elif self.y_speed < 25:  # Gravity
            self.y_speed += GRAVITY_CONST

        # Jumping
        if on_ground and self.jump_count < self.jump_limit and self.bot_move == BotMoveType.UP:
            self.y_speed -= self.jump_power
            self.jump_count += 1
            self.animation.animation_type = BotAnimationType.FRONT

        # Check the sides of the bot
        if Map.masked_top_v_line(map, 4, self.x_pos - self.width, self.y_pos, self.height) == 0:
            self.y_speed = 0
            if self.x_speed < 0:
                self.x_speed = 0
        elif Map.masked_top_v_line(map, 4, self.x_pos + self.width, self.y_pos, self.height) == 0:
            self.y_speed = 0
            if self.x_speed > 0:
                self.x_speed = 0

        # Check max speed
        if self.x_speed > self.max_speed:
            self.x_speed = self.max_speed
        elif self.x_speed < -self.max_speed:
            self.x_speed = -self.max_speed

        # Apply changes
        self.x_pos += self.x_speed
        self.y_pos += self.y_speed

        # Change animation on direction change
        if self.x_speed < 0:
            self.animation.animation_type = BotAnimationType.LEFT
        elif self.x_speed > 0:
            self.animation.animation_type = BotAnimationType.RIGHT
        elif self.x_speed == 0 and self.y_speed == 0:
            self.animation.animation_type = BotAnimationType.STAND

    def update_bot_state(self):
        self.bot_move = BotMoveType.UP  # Just jump for now

    def display(self, screen, map, player):
        self.camera_x = player.camera_x
        self.camera_y = player.camera_y

        self.collision_detection(map)
        self.update_bot_state()
        self.animation.play(screen, self)


import random
from itertools import zip_longest
from typing import List, Union, Optional, Tuple

import matplotlib as mpl
import numpy as np
import pandas as pd
from PySide6.QtCore import Qt, QSettings, QSize, QAbstractTableModel, QModelIndex, QObject, QEvent, QRect, QRectF
from PySide6.QtGui import (QColor, QStandardItemModel, QIcon, QDropEvent, QKeyEvent, QBrush, QImage, QPixmap,
                           QPainter, QTransform, QPen, QPolygonF, QGradient, QPalette)
from PySide6.QtWidgets import (QDialog, QColorDialog, QListWidgetItem, QFileDialog, QMessageBox, QInputDialog,
                             QAbstractItemView, QDialogButtonBox, QFormLayout, QDoubleSpinBox, QAbstractSpinBox, QLabel,
                             QStyledItemDelegate, QStyleOptionViewItem, QWidget, QHBoxLayout, QButtonGroup,
                             QToolButton)

from PySide6MolecularNetwork.node import NODE_POLYGON_MAP, NodePolygon
from metgem.models.metadata import ColorMarkRole, ColumnDataRole
from metgem.utils import pairwise
from metgem.ui.color_mapping_dialog_ui import Ui_ColorMappingDialog

ColumnRole = Qt.UserRole + 1
ValueRole = Qt.UserRole + 2

MOVE_COLUMNS_SELECT = 0
MOVE_COLUMNS_UNSELECT = 1

NODE_BRUSH_STYLES = {
    Qt.NoBrush,
    Qt.Dense1Pattern,
    Qt.Dense2Pattern,
    Qt.Dense3Pattern,
    Qt.Dense4Pattern,
    Qt.Dense5Pattern,
    Qt.Dense6Pattern,
    Qt.Dense7Pattern,
    Qt.HorPattern,
    Qt.VerPattern,
    Qt.CrossPattern,
    Qt.BDiagPattern,
    Qt.FDiagPattern,
    Qt.DiagCrossPattern,
}

# https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
# noinspection PyCallByClass,PyTypeChecker
def generate_colors(n, seed=None):
    """Generate list of `n` colors"""

    colors = []
    if seed is None:
        h = random.random()
    else:
        h = seed

    golden_ratio = 0.618033988749895  # 1/phi
    for i in range(n):
        colors.append(QColor.fromHsv(h * 360, 150, 254, 255))
        h += golden_ratio
        h %= 1
    return colors

def get_colors(n, cmap='auto'):
    if cmap == 'auto':
        return generate_colors(n)
    else:
        try:
            cm = mpl.colormaps.get_cmap(cmap)
        except ValueError:
            return []
        if (isinstance(cm, (mpl.colors.ListedColormap, mpl.colors.LinearSegmentedColormap)) and cm.N < 256) \
                or n == 1:
            return [QColor(*cm(i, bytes=True)) for i in range(cm.N)]
        else:
            step = 256 // (n - 1)
            return [QColor(*cm(i * step, bytes=True)) for i in range(n)]

# https://gist.github.com/ChrisBeaumont/4025831
# noinspection PyArgumentList
def cmap2pixmap(cmap, steps=50):
    """Convert a maplotlib colormap into a QPixmap
    :param cmap: The colormap to use
    :type cmap: Matplotlib colormap instance (e.g. matplotlib.cm.gray)
    :param steps: The number of color steps in the output. Default=50
    :type steps: int
    :rtype: QPixmap
    """
    if cmap == 'auto':
        colors = generate_colors(steps)
    else:
        try:
            norm = mpl.colors.Normalize(vmin=0., vmax=1.)
            sm = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
            inds = np.linspace(0, 1, steps)
            colors = [QColor(*c) for c in sm.to_rgba(inds, bytes=True)]
        except ValueError:
            return

    im = QImage(steps, 1, QImage.Format_Indexed8)
    im.setColorTable([c.rgba() for c in colors])
    for i in range(steps):
        im.setPixel(i, 0, i)
    im = im.scaled(72, 18)
    return QPixmap.fromImage(im)

def nodePolygonToPixmap(polygon: NodePolygon, size: QSize = QSize(48, 48), zoom: int = 1.2) -> QPixmap:
    w, h = size.width(), size.height()
    image = QImage(w*zoom, h*zoom, QImage.Format_ARGB32)
    image.fill(Qt.transparent)
    painter = QPainter(image)
    painter.setPen(QPen(QPalette().color(QPalette.Text), 5))
    painter.setTransform(QTransform().translate(w/2*zoom, h/2*zoom).scale(w/100., h/100.))
    if polygon == NodePolygon.Circle:
        painter.drawEllipse(QRectF(-50*zoom, -50*zoom, 100., 100.))
    else:
        polygon = NODE_POLYGON_MAP.get(polygon)
        painter.drawPolygon(polygon)
    painter.end()
    return QPixmap(image)

def nodeBrushStyleToPixmap(style: Qt.BrushStyle, size: QSize = QSize(48, 48), zoom: int = 1.2) -> QPixmap:
    w, h = size.width(), size.height()
    image = QImage(w*zoom, h*zoom, QImage.Format_ARGB32)
    image.fill(Qt.transparent)
    painter = QPainter(image)
    painter.setBrush(QBrush(style))
    painter.drawRect(QRectF(zoom, zoom, w, h))
    painter.end()
    return QPixmap(image)

class ColorDialog(QColorDialog):

    @staticmethod
    def getColorPolygonAndBrushStyle(
            initial_color: Union[QColor, Qt.GlobalColor, QGradient] = QColor(),
            initial_polygon: NodePolygon = NodePolygon.Circle,
            initial_brushstyle: Qt.BrushStyle = Qt.NoBrush,
            parent: Optional[QWidget] = None, title: str = '',

            options: Union[QColorDialog.ColorDialogOptions,
                           QColorDialog.ColorDialogOption] = QColorDialog.ColorDialogOptions()) \
            -> (QColor, NodePolygon):

        dlg = QColorDialog(parent)

        # Add buttons to select polygon
        polygons_group = QButtonGroup(parent)
        polygons_layout = QHBoxLayout(parent)
        for p in NodePolygon:
            if p == NodePolygon.Custom:
                continue

            button = QToolButton(parent)
            button.setCheckable(True)
            button.setText(p.name)
            button.setIcon(QIcon(nodePolygonToPixmap(p, button.iconSize())))

            if p == initial_polygon:
                button.setChecked(True)
            polygons_group.addButton(button)
            polygons_group.setId(button, p.value)
            polygons_layout.addWidget(button)
        dlg.layout().insertLayout(dlg.layout().count()-1, polygons_layout)

        # Add buttons to select brush style
        brushstyle_group = QButtonGroup(parent)
        brushstyle_layout = QHBoxLayout(parent)
        for p in NODE_BRUSH_STYLES:
            button = QToolButton(parent)
            button.setCheckable(True)
            button.setIcon(QIcon(nodeBrushStyleToPixmap(p, button.iconSize())))

            if p == initial_brushstyle:
                button.setChecked(True)
            brushstyle_group.addButton(button)
            brushstyle_group.setId(button, p.value)
            brushstyle_layout.addWidget(button)
        dlg.layout().insertLayout(dlg.layout().count()-1, brushstyle_layout)

        if title:
            dlg.setWindowTitle(title)
        dlg.setOptions(options)
        dlg.setCurrentColor(initial_color)
        dlg.exec_()
        return (dlg.selectedColor(),
                NodePolygon(polygons_group.checkedId()),
                brushstyle_group.checkedId(),
                )

class RangeInputDialog(QDialog):
    def __init__(self, parent=None, low=None, high=None):
        super().__init__(parent)

        self.sbLowValue = QDoubleSpinBox(self)
        self.sbLowValue.setMinimum(0)
        self.sbLowValue.setStepType(QAbstractSpinBox.StepType.AdaptiveDecimalStepType)
        if low is not None:
            self.sbLowValue.setValue(low)

        self.sbHighValue = QDoubleSpinBox(self)
        self.sbHighValue.setMinimum(0)
        self.sbLowValue.setStepType(QAbstractSpinBox.StepType.AdaptiveDecimalStepType)
        if high is not None:
            self.sbHighValue.setValue(high)

        button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, self)

        layout = QFormLayout(self)
        layout.addRow("Low value", self.sbLowValue)
        layout.addRow("High value", self.sbHighValue)
        layout.addWidget(button_box)

        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)

    def getRange(self):
        return self.sbLowValue.value(), self.sbHighValue.value()

class WidgetItem(QListWidgetItem):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setSizeHint(QSize(self.sizeHint().width(), 32))
        self.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)

# noinspection PyUnresolvedReferences
class ColorMixin:
    __metaclass__ = WidgetItem

    def setBackground(self, background: Union[QBrush, QColor]):
        if background is not None:
            if not isinstance(background, QBrush):
                background = QBrush(background)
            color = background.color()
            luma = (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255
            text_color = QColor(Qt.black) if luma > 0.5 else QColor(Qt.white)

            super().setBackground(color)
            self.setForeground(text_color)
        else:
            self.setData(Qt.BackgroundRole, None)
            self.setData(Qt.ForegroundRole, None)

class GroupListWidgetItem(ColorMixin, WidgetItem):
    def __init__(self, data, *args, **kwargs):
        super().__init__(data, *args, **kwargs)
        self.setData(ValueRole, data)

class BoolListWidgetItem(ColorMixin, WidgetItem):
    def __init__(self, data, *args, **kwargs):
        super().__init__(str(data), *args, **kwargs)
        self.setData(ValueRole, data)

class RangeListWidgetItem(ColorMixin, WidgetItem):
    def __init__(self, low, high, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setData(ValueRole, (low, high))

    def setData(self, role, value):
        super().setData(role, value)
        if role == ValueRole:
            self.setText('[{0:.2f} - {1:.2f}]'.format(*value))

    def __lt__(self, other):
        sval = self.data(ValueRole)
        oval = other.data(ValueRole)
        if sval is not None and oval is not None:
            return sval < oval
        return super().__lt__(other)

class ColumnListWidgetItem(ColorMixin, WidgetItem):
    def __init__(self, *args, column: int = None, **kwargs):
        super().__init__(*args, **kwargs)

        self.setData(ColumnRole, column)

    def __lt__(self, other):
        scol = self.data(ColumnRole)
        ocol = other.data(ColumnRole)
        if scol is not None and ocol is not None:
            return scol < ocol
        return super().__lt__(other)

class ColorListWidgetItem(ColorMixin, WidgetItem):
    def setBackground(self, background: QBrush):
        if not isinstance(background, QBrush):
            background = QBrush(background)

        color = background.color()
        self.setData(Qt.DisplayRole,
                     f"RGB{color.red(), color.green(), color.blue()} [{color.name()}]")
        super().setBackground(background)

class ItemDelegate(QStyledItemDelegate):
    def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex) -> None:
        super().paint(painter, option, index)
        polygon_id = index.data(BaseColorMappingDialog.PolygonRole)
        brushstyle = index.data(BaseColorMappingDialog.BrushStyleRole)
        h = option.rect.height()

        painter.save()
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setClipRect(option.rect)
        painter.setPen(QPen(Qt.lightGray, h/5.))
        if brushstyle is not None:
            painter.setBrush(QBrush(Qt.lightGray, Qt.BrushStyle(brushstyle)))
        zoom = .9
        painter.setTransform(QTransform()
                             .translate(option.rect.x()+h/2+10, option.rect.y()+h/2)
                             .rotate(-5.)
                             .scale(h/100.*zoom, h/100.*zoom))
        if polygon_id is None or polygon_id == NodePolygon.Circle:
            painter.drawEllipse(QRect(-50, -50, 100, 100))
        else:
            polygon = NODE_POLYGON_MAP.get(polygon_id, QPolygonF())
            painter.drawPolygon(polygon)
        painter.restore()

class BaseColorMappingDialog(QDialog, Ui_ColorMappingDialog):

    PolygonRole = Qt.UserRole + 3
    BrushStyleRole = Qt.UserRole + 4

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setupUi(self)
        self.setWindowFlags(Qt.Tool | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint)

        current_cmap = QSettings().value('ColorMap', 'auto')
        cmaps = ['auto'] + sorted([k for k in mpl.colormaps.keys() if not k.endswith('_r')], key=lambda s: s.casefold())
        for i, cmap in enumerate(cmaps):
            pixmap = cmap2pixmap(cmap)
            self.cbColorMap.addItem(QIcon(pixmap), cmap)
            if cmap == current_cmap:
                self.cbColorMap.setCurrentIndex(i)

        self.lstColumns.viewport().installEventFilter(self)
        self.lstUsedColumns.viewport().installEventFilter(self)
        self.lstUsedColumns.installEventFilter(self)

        self.lstUsedColumns.itemDoubleClicked.connect(self.get_color)
        self.lstColors.itemDoubleClicked.connect(self.get_color)
        self.btEditColor.clicked.connect(lambda: self.get_color(self.lstColors.currentItem()))
        self.btAddColor.clicked.connect(self.add_color)
        self.btRemoveColors.clicked.connect(self.remove_selected_colors)
        self.btGenerateColors.clicked.connect(lambda: self.generate_new_colors(self.cbColorMap.currentText()))
        self.btAutoAssignColors.clicked.connect(self.auto_assign_colors)
        self.cbColorMap.currentIndexChanged.connect(lambda x: self.generate_new_colors(self.cbColorMap.itemText(x)))
        self.btRemoveSelectedColumnsColors.clicked.connect(self.remove_selected_columns_colors)
        self.btLoadColorList.clicked.connect(self.load_color_list)
        self.btSaveColorList.clicked.connect(self.save_color_list)

    def get_color(self, item: ColorListWidgetItem = None):
        current_color = item.data(Qt.BackgroundRole).color() \
            if item is not None and item.data(Qt.BackgroundRole) is not None else QColor()
        color = QColorDialog.getColor(initial=current_color, parent=self,
                                      options=QColorDialog.ColorDialogOption.ShowAlphaChannel)
        if color.isValid():
            item.setBackground(color)

    def add_color(self):
        color = QColorDialog.getColor(parent=self, options=QColorDialog.ColorDialogOption.ShowAlphaChannel)
        if color.isValid():
            item = ColorListWidgetItem()
            item.setBackground(color)
            self.lstColors.addItem(item)

    def remove_selected_colors(self):
        for item in self.lstColors.selectedItems():
            self.lstColors.takeItem(self.lstColors.row(item))

    def set_colors(self, colors: List[Union[str, QColor]],
                   polygons: List[Union[int, str, NodePolygon]] = [],
                   brush_styles: List[Union[str, Qt.BrushStyle]] = []):
        self.lstColors.clear()
        for color, poly, brush_style in zip_longest(colors, polygons, brush_styles):
            if isinstance(color, str):
                color = QColor(color)

            if isinstance(poly, str):
                try:
                    poly = NodePolygon[poly]
                except KeyError:
                    poly = NodePolygon.Circle
            if isinstance(poly, int):
                poly = NodePolygon(poly)

            if isinstance(brush_style, str):
                brush_style = Qt.BrushStyle(int(brush_style))

            if color.isValid():
                item = ColorListWidgetItem()
                item.setBackground(color)
                if poly is not None:
                    item.setData(BaseColorMappingDialog.PolygonRole, poly)
                if brush_style is not None:
                    item.setData(BaseColorMappingDialog.BrushStyleRole, Qt.BrushStyle(brush_style))
                self.lstColors.addItem(item)

    def generate_new_colors(self, cmap: str = 'auto'):
        self.set_colors(get_colors(self.lstUsedColumns.count(), cmap=cmap))

    def auto_assign_colors(self):
        column_items = self.lstUsedColumns.selectedItems()
        if not column_items:
            column_items = [self.lstUsedColumns.item(row) for row in range(self.lstUsedColumns.count())]
        color_items = self.lstColors.selectedItems()
        if not color_items:
            color_items = [self.lstColors.item(row) for row in range(self.lstColors.count())]

        for i, column_item in enumerate(column_items):
            try:
                color_item = color_items[i]
            except IndexError:
                color_item = color_items[-1]

            if color_item:
                column_item.setBackground(color_item.data(Qt.BackgroundRole).color())
                column_item.setData(BaseColorMappingDialog.PolygonRole,
                                    color_item.data(BaseColorMappingDialog.PolygonRole))
                column_item.setData(BaseColorMappingDialog.BrushStyleRole,
                                    color_item.data(BaseColorMappingDialog.BrushStyleRole))

    def remove_selected_columns_colors(self):
        for item in self.lstUsedColumns.selectedItems():
            item.setBackground(None)

    def import_color_item(self, row: List[str]) -> Tuple:
        color = QColor(row[1][0])
        if color.isValid():
            return color,

    def load_color_list(self):
        filter_ = ["Color list (*.txt)"]
        filename, filter_ = QFileDialog.getOpenFileName(self, "Load color list",
                                                        filter=";;".join(filter_))
        if filename:
            data = []
            try:
                df = pd.read_csv(filename, header=None)

                for row in df.iterrows():
                    d = self.import_color_item(row)
                    if d is not None:
                        data.append(d)

                self.set_colors(*zip(*data))
            except FileNotFoundError:
                QMessageBox.warning(self, None, "Selected file does not exists.")
            except IOError:
                QMessageBox.warning(self, None, "Load failed (I/O Error).")

    def export_color_item(self, item: QListWidgetItem) -> List[str]:
        color = item.data(Qt.BackgroundRole).color()
        if color is not None and color.isValid():
            return color.name(),

    def save_color_list(self):
        filter_ = ["Color list (*.txt)"]
        filename, filter_ = QFileDialog.getSaveFileName(self, "Save color list",
                                                        filter=";;".join(filter_))
        if filename:
            try:
                with open(filename, 'w') as f:
                    for row in range(self.lstColors.count()):
                        f.write(",".join([str(_) for _ in self.export_color_item(self.lstColors.item(row))]) + '\n')
            except FileNotFoundError:
                QMessageBox.warning(self, None, "Selected file does not exists.")
            except IOError:
                QMessageBox.warning(self, None, "Save failed (I/O Error).")

    def eventFilter(self, obj: QObject, event: Union[QDropEvent, QKeyEvent, QEvent]):
        if event.type() == QEvent.KeyPress and obj == self.lstUsedColumns:
            if event.key() == Qt.Key_Delete or event.key() == Qt.Key_Backspace:
                self.remove_selected_columns_colors()
                event.accept()
        elif event.type() == QEvent.DragEnter and obj == event.source().viewport():
            event.setDropAction(Qt.IgnoreAction)  # Do not allow internal drag'n'drop
        elif event.type() == QEvent.Drop and obj == self.lstUsedColumns.viewport():
            if event.source() == self.lstColors:
                data = event.mimeData()
                if data.hasFormat('application/x-qabstractitemmodeldatalist'):
                    first_item = self.lstUsedColumns.itemAt(event.pos())

                    if first_item is not None:
                        model = QStandardItemModel()
                        model.dropMimeData(data, Qt.CopyAction, 0, 0, QModelIndex())
                        for i in range(model.rowCount()):
                            color = model.item(i, 0).data(Qt.BackgroundRole).color()
                            polygon_id = model.item(i, 0).data(BaseColorMappingDialog.PolygonRole)
                            brushstyle = model.item(i, 0).data(BaseColorMappingDialog.BrushStyleRole)
                            item = self.lstUsedColumns.item(self.lstUsedColumns.row(first_item) + i)
                            if item:
                                item.setBackground(color)
                                item.setData(BaseColorMappingDialog.PolygonRole, polygon_id)
                                item.setData(BaseColorMappingDialog.BrushStyleRole, brushstyle)
                    event.accept()
            elif event.source() == self.lstColumns:
                data = event.mimeData()
                if data.hasFormat('application/x-qabstractitemmodeldatalist'):
                    event.setDropAction(Qt.MoveAction)
                    model = QStandardItemModel()
                    model.dropMimeData(data, Qt.CopyAction, 0, 0, QModelIndex())

                    for i in range(model.rowCount()):
                        item = model.item(i, 0)
                        new_item = ColumnListWidgetItem(item.data(Qt.DisplayRole),
                                                        column=item.data(ColumnRole))
                        bg = item.data(Qt.BackgroundRole)
                        polygon_id = item.data(BaseColorMappingDialog.PolygonRole)
                        brushstyle = item.data(BaseColorMappingDialog.BrushStyleRole)
                        if bg is not None and bg.color().isValid():
                            new_item.setBackground(bg.color())
                        new_item.setData(BaseColorMappingDialog.PolygonRole, polygon_id)
                        new_item.setData(BaseColorMappingDialog.BrushStyleRole, brushstyle)
                        self.lstUsedColumns.addItem(new_item)
                    self.lstUsedColumns.sortItems()
                    event.accept()

        return super().eventFilter(obj, event)

class PieColorMappingDialog(BaseColorMappingDialog):

    def __init__(self, *args, model: QAbstractTableModel = None, selected_columns: List[int] = None, **kwargs):
        super().__init__(*args, **kwargs)

        for i in range(self.layoutBins.count()):
            w = self.layoutBins.itemAt(i).widget()
            if w is not None and not isinstance(w, QLabel):
                w.hide()

        if model is not None:
            for col in range(model.columnCount()):
                text = model.headerData(col, Qt.Horizontal, Qt.DisplayRole)
                color = model.headerData(col, Qt.Horizontal, ColorMarkRole)
                item = ColumnListWidgetItem(text, column=col)
                if color is not None:
                    item.setBackground(color)

                if selected_columns is not None and col in selected_columns:
                    self.lstUsedColumns.addItem(item)
                else:
                    self.lstColumns.addItem(item)

        colors = QSettings().value('NetworkView/pie_colors',
                                   [c.name() for c in generate_colors(8)])
        self.set_colors(colors)

        self.btUseSelectedColumns.clicked.connect(lambda: self.move_selected_columns(MOVE_COLUMNS_SELECT))
        self.btRemoveSelectedColumns.clicked.connect(lambda: self.move_selected_columns(MOVE_COLUMNS_UNSELECT))

    def move_selected_columns(self, direction=MOVE_COLUMNS_SELECT):
        if direction == MOVE_COLUMNS_SELECT:
            source = self.lstColumns
            dest = self.lstUsedColumns
        else:
            source = self.lstUsedColumns
            dest = self.lstColumns

        for item in source.selectedItems():
            source.takeItem(source.row(item))
            new_item = ColumnListWidgetItem(item.data(Qt.DisplayRole), column=item.data(ColumnRole))
            bg = item.data(Qt.BackgroundRole)
            if bg is not None and bg.color().isValid():
                new_item.setBackground(bg.color())
            dest.addItem(new_item)
            new_item.setSelected(True)

    def done(self, r):
        if r == QDialog.DialogCode.Accepted:
            colors = [self.lstColors.item(row).data(Qt.BackgroundRole).color().name()
                      for row in range(self.lstColors.count())]
            QSettings().setValue('NetworkView/pie_colors', colors)
        super().done(r)

    def getValues(self):
        columns = []
        colors = []
        for row in range(self.lstUsedColumns.count()):
            item = self.lstUsedColumns.item(row)
            column = item.data(ColumnRole)
            bg = item.data(Qt.BackgroundRole)
            columns.append(column)
            if bg is not None and bg.color().isValid():
                colors.append(bg.color())
            else:
                colors.append(QColor(Qt.transparent))
        return columns, colors

class ColorMappingDialog(BaseColorMappingDialog):

    def __init__(self, *args, model: QAbstractTableModel = None, column_id: int = None, mapping: dict = None, **kwargs):
        self._model = model
        self._column_id = column_id

        super().__init__(*args, **kwargs)

        self.lstUsedColumns.setItemDelegate(ItemDelegate())
        self.lstColors.setItemDelegate(ItemDelegate())

        data = self._model.headerData(self._column_id, Qt.Horizontal, role=ColumnDataRole)
        if data is not None and not isinstance(data, pd.Series):
            data = pd.Series(data)
        self._data = data.fillna(0).squeeze()

        if model is not None and column_id is not None:
            for i in range(model.columnCount()):
                index = model.index(0, i)
                text = model.headerData(i, Qt.Horizontal, Qt.DisplayRole)
                color = model.headerData(i, Qt.Horizontal, ColorMarkRole)
                item = ColumnListWidgetItem(text, column=index.column())
                if color is not None:
                    item.setBackground(color)

                if index.column() != column_id:
                    self.lstColumns.addItem(item)

        self.lstUsedColumns.setSelectionMode(QAbstractItemView.SelectionMode.ContiguousSelection)
        self.lstColumns.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
        self.populate_bins(mapping)

        colors = QSettings().value('NetworkView/node_colors',
                                   [c.name() for c in generate_colors(8)])
        self.set_colors(colors)

        self.btUseSelectedColumns.clicked.connect(self.on_use_selected_column)
        self.btRemoveSelectedColumns.clicked.connect(self.on_unuse_selected_column)

    def populate_bins(self, mapping=None):
        try:
            self.btGenerateBins.clicked.disconnect()
            self.btMergeBins.clicked.disconnect()
            self.btSplitBins.clicked.disconnect()
            self.btEditBin.clicked.disconnect()
            self.btAddBin.clicked.disconnect()
            self.btRemoveBins.clicked.disconnect()
        except (TypeError, RuntimeError):
            pass

        data = self._data

        if data is not None:
            if data.dtype == bool:
                self.lblUsedColumns.setText('Groups')
                for val in (True, False):
                    item = BoolListWidgetItem(val)
                    self.lstUsedColumns.addItem(item)
                    if mapping is not None:
                        color = mapping.get(val, None)
                        if color is None:
                            color = mapping.get(str(val).lower(), None)  # True -> 'true', False -> 'false'
                        if color is not None:
                            item.setBackground(QColor(color))
            elif data.dtype == object:
                self.lblUsedColumns.setText('Groups')
                if mapping is not None:
                    for group, color in mapping:
                        item = GroupListWidgetItem(str(group))
                        item.setBackground(QColor(color))
                        self.lstUsedColumns.addItem(item)
                else:
                    groups = data.dropna().unique()
                    for group in groups:
                        item = GroupListWidgetItem(str(group))
                        self.lstUsedColumns.addItem(item)
            else:
                self.lblUsedColumns.setText('Bins')
                if mapping is not None:
                    values, colors = mapping
                    try:
                        for low, high, color in zip(values[:-1], values[1:], colors):
                            item = RangeListWidgetItem(low, high)
                            item.setBackground(QColor(color))
                            self.lstUsedColumns.addItem(item)
                    except ValueError:
                        self.generate_new_bins(8)
                else:
                    self.generate_new_bins(8)

                self.btGenerateBins.clicked.connect(self.on_generate_new_bins)
                self.btMergeBins.clicked.connect(self.on_merge_bins)
                self.btSplitBins.clicked.connect(self.on_split_bins)
                self.btEditBin.clicked.connect(self.on_edit_bin)
                self.btAddBin.clicked.connect(self.on_add_bin)
                self.btRemoveBins.clicked.connect(self.on_remove_bins)

    def on_unuse_selected_column(self):
        self.lstUsedColumns.clear()

        if self._column_id is not None:
            index = self._model.index(0, self._column_id)
            text = self._model.headerData(self._column_id, Qt.Horizontal, Qt.DisplayRole)
            color = self._model.headerData(self._column_id, Qt.Horizontal, ColorMarkRole)
            item = ColumnListWidgetItem(text, column=index.column())
            if color is not None:
                item.setBackground(color)

            self.lstColumns.addItem(item)

    def on_use_selected_column(self):
        self.on_unuse_selected_column()

        selected_items = self.lstColumns.selectedItems()
        if len(selected_items) != 1:
            return

        self.lstUsedColumns.clear()
        item = selected_items[0]
        self.lstColumns.takeItem(self.lstColumns.row(item))
        self._column_id = item.data(ColumnRole)

        self.populate_bins()

    def on_remove_bins(self):
        for item in self.lstUsedColumns.selectedItems():
            self.lstUsedColumns.takeItem(self.lstUsedColumns.row(item))

    def on_add_bin(self):
        dlg = RangeInputDialog(self)
        if dlg.exec():
            item = RangeListWidgetItem(*dlg.getRange())
            self.lstUsedColumns.addItem(item)

    def on_edit_bin(self):
        for item in self.lstUsedColumns.selectedItems():
            low, high = item.data(ValueRole)
            dlg = RangeInputDialog(self, low=low, high=high)
            if dlg.exec():
                item.setData(ValueRole, dlg.getRange())
            break

    def on_split_bins(self):
        for item in self.lstUsedColumns.selectedItems():
            low, high = item.data(ValueRole)
            self.lstUsedColumns.takeItem(self.lstUsedColumns.row(item))
            diff = (low + high) / 2
            item = RangeListWidgetItem(low, low + diff)
            self.lstUsedColumns.addItem(item)
            item = RangeListWidgetItem(low + diff, high)
            self.lstUsedColumns.addItem(item)

    def on_merge_bins(self):
        bins = []
        for item in self.lstUsedColumns.selectedItems():
            bins.extend(item.data(ValueRole))
            self.lstUsedColumns.takeItem(self.lstUsedColumns.row(item))

        if bins:
            item = RangeListWidgetItem(min(bins), max(bins))
            self.lstUsedColumns.addItem(item)

    def on_generate_new_bins(self):
        bins, ok = QInputDialog.getInt(self, None, "Enter a number of bins:", value=8, min=1)

        if ok:
            self.generate_new_bins(bins)

    def generate_new_bins(self, bins=8):
        self.lstUsedColumns.clear()
        for low, high in pairwise(np.histogram_bin_edges(self._data, bins=bins)):
            item = RangeListWidgetItem(low, high)
            self.lstUsedColumns.addItem(item)

    def import_color_item(self, row: List[str]) -> Tuple:
        color = QColor(row[1][0])
        try:
            polygon_id = NodePolygon[row[1][1]] if len(row[1]) > 1 else NodePolygon.Circle
        except KeyError:
            polygon_id = NodePolygon.Circle
        brushstyle = Qt.BrushStyle(row[1][2]) if len(row[1]) > 2 else Qt.NoBrush

        if color.isValid():
            return color, polygon_id, brushstyle

    def export_color_item(self, item: QListWidgetItem) -> Tuple[str, str, Qt.BrushStyle]:
        color = item.data(Qt.BackgroundRole).color()
        polygon_id = item.data(BaseColorMappingDialog.PolygonRole)
        polygon_id = polygon_id if polygon_id is not None else NodePolygon.Circle
        brushstyle: Qt.BrushStyle = item.data(BaseColorMappingDialog.BrushStyleRole)
        brushstyle = brushstyle if brushstyle is not None else Qt.NoBrush

        if color is not None and color.isValid():
            return color.name(), polygon_id.name, brushstyle

    def done(self, r):
        if r == QDialog.DialogCode.Accepted:
            colors = [self.lstColors.item(row).data(Qt.BackgroundRole).color().name()
                      for row in range(self.lstColors.count())]
            QSettings().setValue('NetworkView/node_colors', colors)
        super().done(r)

    def get_color(self, item: ColorListWidgetItem = None):
        current_color = item.data(Qt.BackgroundRole).color() \
            if item is not None and item.data(Qt.BackgroundRole) is not None else QColor()
        current_polygon = item.data(BaseColorMappingDialog.PolygonRole) \
            if item is not None and item.data(BaseColorMappingDialog.PolygonRole) is not None else NodePolygon.Circle
        current_brushstyle = item.data(BaseColorMappingDialog.BrushStyleRole) \
            if item is not None and item.data(BaseColorMappingDialog.BrushStyleRole) is not None else Qt.NoBrush

        color, polygon_id, brushstyle = ColorDialog.getColorPolygonAndBrushStyle(
            initial_color=current_color,
            initial_polygon=current_polygon,
            initial_brushstyle=current_brushstyle,
            parent=self,
            options=QColorDialog.ColorDialogOption.ShowAlphaChannel)

        if color.isValid():
            item.setBackground(color)
        item.setData(BaseColorMappingDialog.PolygonRole, polygon_id)
        item.setData(BaseColorMappingDialog.BrushStyleRole, Qt.BrushStyle(brushstyle))

    def add_color(self):
        color, polygon_id, brushstyle = ColorDialog.getColorPolygonAndBrushStyle(
            parent=self,
            options=QColorDialog.ColorDialogOption.ShowAlphaChannel)

        if color.isValid():
            item = ColorListWidgetItem()
            item.setBackground(color)
            item.setData(BaseColorMappingDialog.PolygonRole, polygon_id)
            item.setData(BaseColorMappingDialog.BrushStyleRole, Qt.BrushStyle(brushstyle))
            self.lstColors.addItem(item)

    def getValues(self):
        item = self.lstUsedColumns.item(0)
        if isinstance(item, BoolListWidgetItem):
            mapping = {}
            for row in range(self.lstUsedColumns.count()):
                item = self.lstUsedColumns.item(row)
                group = item.data(ValueRole)
                bg = item.data(Qt.BackgroundRole)
                polygon_id = item.data(BaseColorMappingDialog.PolygonRole)
                polygon_id = polygon_id if polygon_id is not None else NodePolygon.Circle
                brushstyle = item.data(BaseColorMappingDialog.BrushStyleRole)
                brushstyle = brushstyle if brushstyle is not None else Qt.NoBrush

                if bg is not None and bg.color().isValid():
                    mapping[group] = (bg.color(), polygon_id.value, brushstyle)
                else:
                    mapping[group] = (QColor(Qt.transparent), polygon_id.value, brushstyle)
            return self._column_id, mapping
        elif isinstance(item, GroupListWidgetItem):
            mapping = {}
            for row in range(self.lstUsedColumns.count()):
                item = self.lstUsedColumns.item(row)
                group = str(item.data(ValueRole))
                bg = item.data(Qt.BackgroundRole)
                polygon_id = item.data(BaseColorMappingDialog.PolygonRole)
                polygon_id = polygon_id if polygon_id is not None else NodePolygon.Circle
                brushstyle = item.data(BaseColorMappingDialog.BrushStyleRole)
                brushstyle = Qt.BrushStyle(brushstyle) if brushstyle is not None else Qt.NoBrush

                if bg is not None and bg.color().isValid():
                    mapping[group] = (bg.color(), polygon_id.value, brushstyle)
                else:
                    mapping[group] = (QColor(Qt.transparent), polygon_id.value, brushstyle)
            return self._column_id, mapping
        else:
            bins = []
            colors = []
            polygons = []
            styles = []
            for row in range(self.lstUsedColumns.count()):
                item = self.lstUsedColumns.item(row)
                low, high = item.data(ValueRole)
                bg = item.data(Qt.BackgroundRole)
                polygon_id = item.data(BaseColorMappingDialog.PolygonRole)
                polygon_id = polygon_id if polygon_id is not None else NodePolygon.Circle
                brushstyle = item.data(BaseColorMappingDialog.BrushStyleRole)
                brushstyle = Qt.BrushStyle(brushstyle) if brushstyle is not None else Qt.NoBrush

                bins.append(low)
                polygons.append(polygon_id.value)
                styles.append(brushstyle)
                if bg is not None and bg.color().isValid():
                    colors.append(bg.color())
                else:
                    colors.append(QColor(Qt.transparent))

            try:
                # noinspection PyUnboundLocalVariable
                bins.append(high)
            except UnboundLocalError:
                pass

            return self._column_id, (bins, colors, polygons, styles)


import urllib.request
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta

class FantastyBasketballNerd:
    __scheduleurl = "https://www.fantasybasketballnerd.com/service/schedule/"
    __playersurl = "https://www.fantasybasketballnerd.com/service/players/"
    __teamsurl = "https://www.fantasybasketballnerd.com/service/teams/"
    __injuriesurl = "https://www.fantasybasketballnerd.com/service/injuries/"
    __depthcharturl = "https://www.fantasybasketballnerd.com/service/depth/{TEAM_CODE}"

    class AppURLopener(urllib.request.FancyURLopener):
        version = "Mozilla/5.0"

    teams = dict()
    players = dict()

    class Injury:
        def __init__(self):
            self.injured = False
            self.injurytext = ""

    class Player:
        def __init__(self):
            self.name = ""
            self.team = ""
            self.id = ""
            self.injury = FantastyBasketballNerd.Injury()

    class Team:
        def __init__(self):
            self.code = ""
            self.name = ""
            self.schedule = list()

    def __readURL(self, url):
        opener = self.AppURLopener()
        webpage = opener.open(url)
        myfile = webpage.read()
        return myfile

    def __init__(self):
        self.__initPlayers()
        self.__initTeams()
        self.__initSchedule()

    def __initPlayers(self):
        playerxml = ET.fromstring(self.__readURL(self.__playersurl))
        injuryxml = ET.fromstring(self.__readURL(self.__injuriesurl))
        injuries = dict()
        for t in injuryxml.findall('Team'):
            for i in t.findall('Player'):
                injury = self.Injury()
                injury.injured = True
                injury.injurytext = i.find('injury').text + ", " + i.find('notes').text
                injuries[i.find('name').text] = injury

        for p in playerxml.findall('Player'):
            player = self.Player()
            player.name = p.find('name').text
            player.id = p.find('playerId').text
            player.team = p.find('team').text #team code
            if player.name in injuries:
                player.injury = injuries[player.name]
            else:
                player.injury.injured = False
                player.injury.injurytext = ""
            if player.name == "C.J. McCollum":
                player.name = "CJ McCollum"
            self.players[player.name] = player

    def __initTeams(self):
        xml = ET.fromstring(self.__readURL(self.__teamsurl))
        for t in xml.findall('Team'):
            team = self.Team()
            team.code = t.find('code').text
            team.name = t.find('name').text
            self.teams[team.code] = team

    def __initSchedule(self):
        xml = ET.fromstring(self.__readURL(self.__scheduleurl))
        for g in xml.findall('Game'):
            date = datetime.strptime(g.find('gameDate').text, '%Y-%m-%d %H:%M:%S') #format: 2020-12-22 19:00:00
            away = g.find('away').text #away team code
            home = g.find('home').text  # home team code
            self.teams[away].schedule.append(date)
            team1 = self.teams[away]
            self.teams[home].schedule.append(date)
            team2 = self.teams[home]

    def readTeamSchedule(self, teamCode):
        team = self.teams[teamCode]
        assert isinstance(team, self.Team)
        return team.schedule

    def weekStartEnd(self, week):
        offset = timedelta(days = 7 - datetime.today().weekday())
        oneweek = timedelta(days=7)
        weekstart =  datetime.today() + offset + (week * oneweek)
        weekstart = weekstart.replace(hour = 0)
        weekend = weekstart + oneweek
        return weekstart, weekend

    def numberPlayerGamesWeek(self, name, week=0):
        schedule = self.readTeamSchedule(self.players[name].team)
        weekstart, weekend = self.weekStartEnd(week)
        numgames = 0
        for d in schedule:
            if ((d > weekstart) and (d < weekend)):
                numgames += 1
        return numgames

    def numberPlayerGamesPlayed(self, name):
        schedule = self.readTeamSchedule(self.players[name].team)
        numgames = 0
        for d in schedule:
            if (d < datetime.today()):
                numgames += 1
        return numgames

    def getPlayerInjured(self, name):
        injurytext = ""
        if self.players[name].injury.injured:
            injurytext = "injured, " + self.players[name].injury.injurytext
        return injurytext


from collections import defaultdict

num_samples = len(snakemake.input)-1
dist_threshold = 10000

GeneID_to_location = {}
with open(snakemake.input[-1], "r") as input_coords_file:
	for line in input_coords_file:
		GeneID, transcript, chrom, start, stop = line.strip().split()
		#for flybase_dyak
		if "-" in GeneID:
			GeneID = GeneID.split("-")[0]
		start, stop = int(start), int(stop)
		if GeneID in GeneID_to_location:
			previous_chrom, previous_start, previous_stop = GeneID_to_location[GeneID]
			GeneID_to_location[GeneID] = [chrom, min(previous_start, start), max(previous_stop, stop)]
		else:
			GeneID_to_location[GeneID] = [chrom, start, stop]

geneid_to_diffs = defaultdict(list)
geneid_to_sames = defaultdict(list)
for i in range(num_samples):
	geneid_to_cluster_found = {}
	geneid_to_anchor_range = {}
	geneid_to_best_AS = {}
	with open(snakemake.input[i], "r") as input_AS_file:
		for line in input_AS_file:
			geneid, transcript_range, anchor_range, anchor_AS, strand = line.strip().split()
			if geneid not in geneid_to_cluster_found:
				geneid_to_cluster_found[geneid] = False
			if geneid not in geneid_to_best_AS:
				geneid_to_best_AS[geneid] = 0
			anchor_AS = int(anchor_AS)
			best_AS = geneid_to_best_AS[geneid]
			if anchor_AS > best_AS:
				geneid_to_cluster_found[geneid] = True
				geneid_to_anchor_range[geneid] = anchor_range
				geneid_to_best_AS[geneid] = anchor_AS
	for geneid in geneid_to_best_AS:
		cluster_found = geneid_to_cluster_found[geneid]
		if cluster_found:
			gene_chrom, gene_start, gene_stop = GeneID_to_location[geneid]
			anchor_range = geneid_to_anchor_range[geneid]
			anchor_chrom, anchor_startstop = anchor_range.split(":")
			anchor_start, anchor_stop = anchor_startstop.split("-")
			anchor_start, anchor_stop = int(anchor_start), int(anchor_stop)
			if gene_chrom != anchor_chrom or anchor_start > gene_stop + dist_threshold or gene_start > anchor_stop + dist_threshold:
				diff = True
				same = False
			else:
				same = True
				diff = False
			geneid_to_diffs[geneid].append(diff)
			geneid_to_sames[geneid].append(same)

with open(snakemake.output[0], "w") as output_alldiff_file, open(snakemake.output[1], "w") as output_allsame_file, open(snakemake.output[2], "w") as output_somesome_file:
	for geneid in geneid_to_diffs:
		sames = geneid_to_sames[geneid]
		diffs = geneid_to_diffs[geneid]
		if all(diffs):
			output_alldiff_file.write(f"{geneid}\n")
		elif all(sames):
			output_allsame_file.write(f"{geneid}\n")
		else:
			output_somesome_file.write(f"{geneid}\n")


import json
import string

#create a list of tv shows or movies and add them to a json file
# read json file in if possible to make sure there are no duplicates in show name

class tv_movie:
    def __init__(self, name, isMovie, season):
        self.name = name
        self.isMovie = isMovie
        if(isMovie): #movies do not have seasons
            self.season = 0
        else:
            self.season = season

    def convert(self): #convert the class into a dictionary
        dictionary = {
            "Name" : self.name,
            "Movie" : str(self.isMovie),
            "Seasons Watched" : self.season
        }
        return dictionary

def validate_season():
    while True:
        num = input("How many seasons have you seen: ")
        try:
            #convert length to int
            value = int(num)
            if value > 0:
                break
            #non integer answers
        except ValueError:
            print("You did not enter a number. Please enter a number \n")
    return value

exists = input("Do you already have a file containing the movies and tv shows you have seen?\n")
while exists.lower() != "yes" and exists.lower() != "no":
    exists = input("Do you already have a file containing the movies and tv shows you have seen?\n")

#list of tv shows and movies watched
tvm_list = []

file_path = "tv_movie_list.json"
# if the file exists it should be saved under the name tv_movie_list.json
if exists.lower() == "yes":
    with open(file_path, "r", encoding = 'utf-8') as f:
        tvm_list = json.load(f)

more = "yes"

while more.lower() != "no":
    # start building the list of movies and tv shows
    tvm_name = input("\nEnter the name of a movie or tv show you have seen: ").capitalize()
    tvm_movie = input("Is it a movie?  ")
    while tvm_movie.lower() != "yes" and tvm_movie.lower() != "no":
        tvm_movie = input("Is it a movie?  ")

    if tvm_movie == "no":
        tvm_season = validate_season()
        tvm_bool = False
    else:
        tvm_season = 0
        tvm_bool = True
    #create the new entry
    new_tvm = tv_movie(tvm_name, tvm_bool, tvm_season)
    #add new entry to list of movies
    tvm_list.append(new_tvm.convert())
    more = input("Are there any more TV shows or Movies you would like to add? ")

with open(file_path, "w") as outfile:
    json.dump(tvm_list, outfile)

print("done")


import uuid

import test_v3

class CatalogTestCase(test_v3.RestfulTestCase):
    """Test service & endpoint CRUD"""

    def setUp(self):
        super(CatalogTestCase, self).setUp()

        self.service_id = uuid.uuid4().hex
        self.service = self.new_service_ref()
        self.service['id'] = self.service_id
        self.catalog_api.create_service(
            self.service_id,
            self.service.copy())

        self.endpoint_id = uuid.uuid4().hex
        self.endpoint = self.new_endpoint_ref(service_id=self.service_id)
        self.endpoint['id'] = self.endpoint_id
        self.catalog_api.create_endpoint(
            self.endpoint_id,
            self.endpoint.copy())

    # service crud tests

    def test_create_service(self):
        """POST /services"""
        ref = self.new_service_ref()
        r = self.post(
            '/services',
            body={'service': ref})
        return self.assertValidServiceResponse(r, ref)

    def test_list_services(self):
        """GET /services"""
        r = self.get('/services')
        self.assertValidServiceListResponse(r, ref=self.service)

    def test_list_services_xml(self):
        """GET /services (xml data)"""
        r = self.get('/services', content_type='xml')
        self.assertValidServiceListResponse(r, ref=self.service)

    def test_get_service(self):
        """GET /services/{service_id}"""
        r = self.get('/services/%(service_id)s' % {
            'service_id': self.service_id})
        self.assertValidServiceResponse(r, self.service)

    def test_update_service(self):
        """PATCH /services/{service_id}"""
        service = self.new_service_ref()
        del service['id']
        r = self.patch('/services/%(service_id)s' % {
            'service_id': self.service_id},
            body={'service': service})
        self.assertValidServiceResponse(r, service)

    def test_delete_service(self):
        """DELETE /services/{service_id}"""
        self.delete('/services/%(service_id)s' % {
            'service_id': self.service_id})

    # endpoint crud tests

    def test_list_endpoints(self):
        """GET /endpoints"""
        r = self.get('/endpoints')
        self.assertValidEndpointListResponse(r, ref=self.endpoint)

    def test_list_endpoints_xml(self):
        """GET /endpoints (xml data)"""
        r = self.get('/endpoints', content_type='xml')
        self.assertValidEndpointListResponse(r, ref=self.endpoint)

    def test_create_endpoint(self):
        """POST /endpoints"""
        ref = self.new_endpoint_ref(service_id=self.service_id)
        r = self.post(
            '/endpoints',
            body={'endpoint': ref})
        self.assertValidEndpointResponse(r, ref)

    def assertValidErrorResponse(self, response):
        self.assertTrue(response.status in [400])

    def test_create_endpoint_400(self):
        """POST /endpoints"""
        ref = self.new_endpoint_ref(service_id=self.service_id)
        ref["region"] = "0" * 256
        self.post('/endpoints', body={'endpoint': ref}, expected_status=400)

    def test_get_endpoint(self):
        """GET /endpoints/{endpoint_id}"""
        r = self.get(
            '/endpoints/%(endpoint_id)s' % {
                'endpoint_id': self.endpoint_id})
        self.assertValidEndpointResponse(r, self.endpoint)

    def test_update_endpoint(self):
        """PATCH /endpoints/{endpoint_id}"""
        ref = self.new_endpoint_ref(service_id=self.service_id)
        del ref['id']
        r = self.patch(
            '/endpoints/%(endpoint_id)s' % {
                'endpoint_id': self.endpoint_id},
            body={'endpoint': ref})
        self.assertValidEndpointResponse(r, ref)

    def test_delete_endpoint(self):
        """DELETE /endpoints/{endpoint_id}"""
        self.delete(
            '/endpoints/%(endpoint_id)s' % {
                'endpoint_id': self.endpoint_id})

    def test_create_endpoint_on_v2(self):
        # clear the v3 endpoint so we only have endpoints created on v2
        self.delete(
            '/endpoints/%(endpoint_id)s' % {
                'endpoint_id': self.endpoint_id})

        # create a v3 endpoint ref, and then tweak it back to a v2-style ref
        ref = self.new_endpoint_ref(service_id=self.service['id'])
        del ref['id']
        del ref['interface']
        ref['publicurl'] = ref.pop('url')
        ref['internalurl'] = None
        # don't set adminurl to ensure it's absence is handled like internalurl

        # create the endpoint on v2 (using a v3 token)
        r = self.admin_request(
            method='POST',
            path='/v2.0/endpoints',
            token=self.get_scoped_token(),
            body={'endpoint': ref})
        endpoint_v2 = r.body['endpoint']

        # test the endpoint on v3
        r = self.get('/endpoints')
        endpoints = self.assertValidEndpointListResponse(r)
        self.assertEqual(len(endpoints), 1)
        endpoint_v3 = endpoints.pop()

        # these attributes are identical between both API's
        self.assertEqual(endpoint_v3['region'], ref['region'])
        self.assertEqual(endpoint_v3['service_id'], ref['service_id'])
        self.assertEqual(endpoint_v3['description'], ref['description'])

        # a v2 endpoint is not quite the same concept as a v3 endpoint, so they
        # receive different identifiers
        self.assertNotEqual(endpoint_v2['id'], endpoint_v3['id'])

        # v2 has a publicurl; v3 has a url + interface type
        self.assertEqual(endpoint_v3['url'], ref['publicurl'])
        self.assertEqual(endpoint_v3['interface'], 'public')

        # tests for bug 1152632 -- these attributes were being returned by v3
        self.assertNotIn('publicurl', endpoint_v3)
        self.assertNotIn('adminurl', endpoint_v3)
        self.assertNotIn('internalurl', endpoint_v3)

        # test for bug 1152635 -- this attribute was being returned by v3
        self.assertNotIn('legacy_endpoint_id', endpoint_v3)


import gymnasium as gym
import numpy as np
import pygame

from bas import wrappers
from bas.render.constants import Colors
from bas.render.renderers.renderer import Renderer
from bas.wrappers.observation import components

class ObservationContainerWrapperRenderer(Renderer):
    """Renderer for drawing all components used in the `ObservationContainerWrapper`."""

    WRAPPER = wrappers.ObservationContainerWrapper

    def __init__(self, render_wrapper: "RenderWrapper", instance: gym.Wrapper):
        super().__init__(render_wrapper, instance)

        self._component_renderers = {
            components.SectionObservationComponent: self.render_section_observation_component,
            components.SectionVelocityObservationComponent: self.render_section_velocity_observation_component,
            components.SectionDistanceObservationComponent: self.render_section_distance_observation_component,
            components.SectionVelocityDistanceObservationComponent: self.render_section_velocity_distance_observation_component,
            components.TargetDirectionObservationComponent: self.render_target_direction_observation_component,
            components.TargetDirectionDistanceObservationComponent: self.render_target_direction_observation_component,
            components.AgentVelocityObservationComponent: self.render_agent_velocity_observation_component,
        }

    def render(self, canvas: pygame.Surface):
        """Distribute rendering of observation components to component renderers."""

        for component, observation in zip(
            self._components, self.cached_step.observation
        ):
            component_renderer = self._component_renderers.get(type(component))
            if component_renderer:
                component_renderer(canvas, component, observation)

    def render_section_observation_component(
        self,
        canvas: pygame.Surface,
        component: components.SectionObservationComponent,
        observation: np.ndarray,
    ):
        agent_to_env_rotation = self._shared_computer.agent_to_env_rotation()
        observation = observation @ agent_to_env_rotation.T

        for section_index, section_position in enumerate(observation):
            self.line(
                canvas,
                Colors.OBSERVATION,
                self.agent.position,
                self.agent.position + section_position * component._max_range,
                2,
            )
            self.circle(
                canvas,
                Colors.OBSERVATION,
                self.agent.position + section_position * component._max_range,
                self.swarm.config.radius / 2,
            )

            angle = (
                section_index * 2 * np.pi / component._num_sections + self.agent.angle + component._offset_angle
            )

            self.line(
                canvas,
                Colors.MISC,
                self.agent.position,
                self.agent.position
                + np.array([np.cos(angle), np.sin(angle)]) * component._max_range,
                1,
            )

        self.circle(canvas, Colors.MISC, self.agent.position, component._max_range, 1)

    def render_section_velocity_observation_component(
        self,
        canvas: pygame.Surface,
        component: components.SectionVelocityObservationComponent,
        section_velocities: np.ndarray,
    ):
        agent_to_env_rotation = self._shared_computer.agent_to_env_rotation()
        section_velocities = section_velocities @ agent_to_env_rotation.T

        section_boid_indices = self._shared_computer.closest_boid_indices_per_section(
            component._num_sections,
            component._max_range + component._subtract_radius * self.env.swarm.config.radius,
        )

        for velocity, boid_index in zip(section_velocities, section_boid_indices):
            if boid_index is None:
                continue

            self.line(
                canvas,
                Colors.OBSERVATION,
                self.swarm.positions[boid_index],
                self.swarm.positions[boid_index]
                + velocity * self.agent.max_velocity * 10,
                2,
            )

        self.circle(canvas, Colors.MISC, self.agent.position, component._max_range, 1)

    def render_section_distance_observation_component(
        self,
        canvas: pygame.Surface,
        component: components.SectionDistanceObservationComponent,
        observation: np.ndarray,
    ):
        for section_index, section_distance in enumerate(observation):
            angle = (
                section_index * 2 * np.pi / component._num_sections + self.agent.angle
            )

            self.line(
                canvas,
                Colors.OBSERVATION,
                self.agent.position,
                self.agent.position
                + np.array(
                    [
                        np.cos(angle + np.pi / component._num_sections),
                        np.sin(angle + np.pi / component._num_sections),
                    ]
                )
                * component._max_range
                * section_distance,
                2,
            )

            self.line(
                canvas,
                Colors.MISC,
                self.agent.position,
                self.agent.position
                + np.array([np.cos(angle), np.sin(angle)]) * component._max_range,
                1,
            )

        self.circle(canvas, Colors.MISC, self.agent.position, component._max_range, 1)

    def render_section_velocity_distance_observation_component(
        self,
        canvas: pygame.Surface,
        component: components.SectionVelocityDistanceObservationComponent,
        observation: np.ndarray,
    ):

        agent_to_env_rotation = self._shared_computer.agent_to_env_rotation()
        section_normals = component._section_normals @ agent_to_env_rotation.T
        section_tangents = component._section_tangents @ agent_to_env_rotation.T

        boid_distances = self._shared_computer.agent_to_boid_distances()
        section_boid_indices = self._shared_computer.closest_boid_indices_per_section(
            component._num_sections,
            component._max_range + component._subtract_radius * self.env.swarm.config.radius,
        )
        section_distances = [
            (
                boid_distances[boid_index]
                - self.env.swarm.config.radius * component._subtract_radius
                if boid_index is not None
                else component._max_range
            )
            for boid_index in section_boid_indices
        ]

        for section_index, (
            section_distance,
            (section_normal_velocity, section_tangent_velocity),
        ) in enumerate(zip(section_distances, observation)):
            angle = (
                section_index * 2 * np.pi / component._num_sections + self.agent.angle
            )

            anchor_position = (
                self.agent.position
                + np.array(
                    [
                        np.cos(angle + np.pi / component._num_sections),
                        np.sin(angle + np.pi / component._num_sections),
                    ]
                )
                * section_distance
            )

            self.line(
                canvas,
                Colors.OBSERVATION,
                anchor_position,
                anchor_position
                + section_normals[section_index]
                * section_normal_velocity
                * self.agent.max_velocity
                * 10,
                4,
            )
            self.line(
                canvas,
                Colors.OBSERVATION,
                anchor_position,
                anchor_position
                + section_tangents[section_index]
                * section_tangent_velocity
                * self.agent.max_velocity
                * 10,
                4,
            )

    def render_target_direction_observation_component(
        self,
        canvas: pygame.Surface,
        component: components.TargetDirectionObservationComponent,
        observation: np.ndarray,
    ):

        agent_to_env_rotation = self._shared_computer.agent_to_env_rotation()
        observation = agent_to_env_rotation @ observation

        self.line(
            canvas,
            Colors.OBSERVATION,
            self.agent.position,
            self.agent.position + observation * 10,
            4,
        )

    def render_agent_velocity_observation_component(
        self,
        canvas: pygame.Surface,
        component: components.AgentVelocityObservationComponent,
        observation: np.ndarray,
    ):
        agent_to_env_rotation = self._shared_computer.agent_to_env_rotation()
        observation = agent_to_env_rotation @ observation
        self.line(
            canvas,
            Colors.OBSERVATION,
            self.agent.position,
            self.agent.position + observation * self.agent.max_velocity * 10,
            8,
        )


import pygame

from random import randint

import config as c
import auxiliary as a

# enemy - класс врагов
class enemy(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, group):
        pygame.sprite.Sprite.__init__(self)
        self.image = a.load_image("virus.png").convert_alpha()
        self.rect = self.image.get_rect(center=(x, y))
        self.add(group)
        self.rect.x = x
        self.rect.y = y
        self.width = width - 50
        self.height = height - 50
        self.velocity = 10
        self.counter = 0
        self.points = 0

    def draw(self):
        c.WIN.blit(self.image, (self.rect.x, self.rect.y))

    def move(self):
        if self.rect.x < 0:
            self.points += 1
            self.rect.x = 900 + randint(0, 3000)
        self.rect.x -= self.velocity
        self.counter += 1

        # увеличиваем скорость
        if self.counter == 5 * c.FPS:
            self.velocity += 1
            self.counter = 0

    def get_points(self):
        return self.points

    def start_position(self):
        self.rect.x = 900
        self.velocity = 10
        self.counter = 0


from random import choice
import numpy as np
from numpy import load
from numpy import expand_dims
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import Normalizer
from sklearn.svm import SVC
from matplotlib import pyplot
import pickle

# load faces
data = np.array(pickle.load(open('data/datasets/Xtest.pickle', 'rb')))
testX_faces = data
# load face embeddings
data = load('embeddedFaceData.npz')
trainX, trainy, testX, testy = data['arr_0'], data['arr_1'], data['arr_2'], data['arr_3']
# normalize input vectors
in_encoder = Normalizer(norm='l2')
trainX = in_encoder.transform(trainX)
testX = in_encoder.transform(testX)
# label encode targets
out_encoder = LabelEncoder()
out_encoder.fit(trainy)
trainy = out_encoder.transform(trainy)
testy = out_encoder.transform(testy)
model = SVC(kernel='linear', probability=True)
model.fit(trainX, trainy)

# test model on 5 random examples from the test dataset
for i in range(5):
    selection = choice([j for j in range(testX.shape[0])])
    randomImage = testX_faces[selection]
    face_embed = testX[selection]
    face_class = testy[selection]
    face_name = out_encoder.inverse_transform([face_class])
    # prediction for the face
    samples = expand_dims(face_embed, axis=0)
    yhat_class = model.predict(samples)
    yhat_prob = model.predict_proba(samples)
    # get name
    class_index = yhat_class[0]
    class_probability = yhat_prob[0,class_index] * 100
    predict_names = out_encoder.inverse_transform(yhat_class)
    print(f'Predicted: {predict_names[0]} ({class_probability:.3f})')
    print(f'Expected: {face_name[0]}')
    pyplot.imshow(randomImage)
    title = '%s (%.3f)' % (predict_names[0], class_probability)
    pyplot.title(title)
    pyplot.show()


import cv2
import numpy as np

cap = cv2.VideoCapture(0)

while True:
    _,img = cap.read()
    img = cv2.flip(img,1)

    for i in range(3):
        C = np.zeros(img.shape,dtype = np.uint8)
        C[:,:,i] = img[:,:,i]
        cv2.imshow("frame {}".format(str(i)),C)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()


#convert an image to binary image
import cv2

#open the image in greyscale
img = cv2.imread('lenna.tif',0)

#if the intensity is lesser than equal to 127,make it black else white

prop = img.shape

for i in range(prop[0]):
    for j in range( prop[1]):
        if img[i][j] <= 127:
            img[i][j] = 0
        else:
            img[i][j] = 255

cv2.imwrite('lenna_binary.jpg',img)


import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

catchment_div_containers = []

app.layout = html.Div([
    html.H1('Water Usage Estimator'),
    html.P("This is a small web-application for estimating how much water a particular property produces annually"),
    html.Div(catchment_div_containers, id='catchment-section'),
    html.Button("Add another catchment", id='add-catchment-btn')
])

@app.callback(
    Output(component_id='catchment-section', component_property='children'),
    [Input(component_id='add-catchment-btn', component_property='n_clicks')])
def add_new_catchment_section(clicks):
    if clicks:
        for click in range(clicks-len(catchment_div_containers)):
            catchment_div_containers.append(
                html.Div(id='catchment-'+str(len(catchment_div_containers)), children=[
                    html.H3("This is a new catchment"),
                    html.P("This will contain information about a catchment"),
                    html.Button("Remove", id='remove-catchment-'+str(len(catchment_div_containers)))
                ])
            )

    return catchment_div_containers

@app.callback(
    Output(component_id='catchment-section', component_property='children'),
    [Input(component_id='add-catchment-btn', component_property='n_clicks')])
def add_new_catchment_section(clicks):
    pass

if __name__ == "__main__":
    app.run_server(port=8000, debug=True)