import base64
import threading
import time
import unittest

from unittest import mock
from typing   import Any, List

import requests

from src.common.crypto   import X448
from src.common.db_onion import pub_key_to_onion_address, pub_key_to_short_address
from src.common.statics  import (CONTACT_MGMT_QUEUE, CONTACT_REQ_QUEUE, C_REQ_MGMT_QUEUE, C_REQ_STATE_QUEUE,
                                 DST_MESSAGE_QUEUE, EXIT, GROUP_ID_LENGTH, GROUP_MGMT_QUEUE,
                                 GROUP_MSG_EXIT_GROUP_HEADER, GROUP_MSG_INVITE_HEADER, GROUP_MSG_JOIN_HEADER,
                                 GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER, GROUP_MSG_QUEUE,
                                 MESSAGE_DATAGRAM_HEADER, ONION_SERVICE_PUBLIC_KEY_LENGTH, PUBLIC_KEY_DATAGRAM_HEADER,
                                 RP_ADD_CONTACT_HEADER, RP_REMOVE_CONTACT_HEADER, TFC_PUBLIC_KEY_LENGTH, TOR_DATA_QUEUE,
                                 UNIT_TEST_QUEUE, URL_TOKEN_QUEUE)

from src.relay.client import c_req_manager, client, client_scheduler, g_msg_manager, get_data_loop

from tests.mock_classes import Gateway
from tests.utils        import gen_queue_dict, nick_to_onion_address, nick_to_pub_key, tear_queues

class TestClient(unittest.TestCase):

    url_token_private_key = X448.generate_private_key()
    url_token_public_key  = X448.derive_public_key(url_token_private_key)
    url_token             = X448.shared_key(url_token_private_key, url_token_public_key).hex()

    class MockResponse(object):
        """Mock Response object."""
        def __init__(self, text) -> None:
            """Create new MockResponse object."""
            self.text    = text
            self.content = text

    class MockSession(object):
        """Mock Session object."""

        def __init__(self) -> None:
            """Create new MockSession object."""
            self.proxies = dict()
            self.timeout = None
            self.url     = None
            self.test_no = 0

        def get(self, url, timeout=0, stream=False) -> Any:
            """Mock .get() method."""

            self.timeout = timeout

            # When we reach `get_data_loop` that loads stream, throw exception to close the test.
            if stream:
                (_ for _ in ()).throw(requests.exceptions.RequestException)

            if url.startswith("http://hpcrayuxhrcy2wtpfwgwjibderrvjll6azfr4tqat3eka2m2gbb55bid.onion/"):

                if self.test_no == 0:
                    self.test_no += 1
                    (_ for _ in ()).throw(requests.exceptions.RequestException)

                if self.test_no == 1:
                    self.test_no += 1
                    return TestClient.MockResponse('OK')

                # Test function recovers from RequestException.
                if self.test_no == 2:
                    self.test_no += 1
                    (_ for _ in ()).throw(requests.exceptions.RequestException)

                # Test function recovers from invalid public key.
                if self.test_no == 3:
                    self.test_no += 1
                    return TestClient.MockResponse(((ONION_SERVICE_PUBLIC_KEY_LENGTH-1)*b'a').hex())

                # Test client prints online/offline messages.
                if self.test_no < 10:
                    self.test_no += 1
                    return TestClient.MockResponse('')

                # Test valid public key moves function to `get_data_loop`.
                if self.test_no == 10:
                    self.test_no += 1
                    return TestClient.MockResponse(TestClient.url_token_public_key.hex())

    @staticmethod
    def mock_session() -> MockSession:
        """Return MockSession object."""
        return TestClient.MockSession()

    def setUp(self) -> None:
        """Pre-test actions."""
        self.o_session   = requests.session
        self.queues      = gen_queue_dict()
        requests.session = TestClient.mock_session

    def tearDown(self) -> None:
        """Post-test actions."""
        requests.session = self.o_session
        tear_queues(self.queues)

    @mock.patch('time.sleep', return_value=None)
    def test_client(self, _) -> None:
        onion_pub_key = nick_to_pub_key('Alice')
        onion_address = nick_to_onion_address('Alice')
        tor_port      = '1337'
        settings      = Gateway()
        sk            = TestClient.url_token_private_key
        self.assertIsNone(client(onion_pub_key, self.queues, sk, tor_port, settings, onion_address, unit_test=True))
        self.assertEqual(self.queues[URL_TOKEN_QUEUE].get(), (onion_pub_key, TestClient.url_token))

class TestGetDataLoop(unittest.TestCase):

    url_token_private_key_user   = X448.generate_private_key()
    url_token_public_key_user    = X448.derive_public_key(url_token_private_key_user)
    url_token_public_key_contact = X448.derive_public_key(X448.generate_private_key())
    url_token                    = X448.shared_key(url_token_private_key_user, url_token_public_key_contact).hex()

    class MockResponse(object):
        """Mock Response object."""
        def __init__(self) -> None:
            self.test_no = 0

        def iter_lines(self) -> List[bytes]:
            """Return data depending test number."""
            self.test_no += 1
            message = b''

            # Empty message
            if self.test_no == 1:
                pass

            # Invalid message
            elif self.test_no == 2:
                message = MESSAGE_DATAGRAM_HEADER + b'\x1f'

            # Valid message
            elif self.test_no == 3:
                message = MESSAGE_DATAGRAM_HEADER    + base64.b85encode(b'test') + b'\n'

            # Invalid public key
            elif self.test_no == 4:
                message = PUBLIC_KEY_DATAGRAM_HEADER + base64.b85encode((TFC_PUBLIC_KEY_LENGTH-1) * b'\x01')

            # Valid public key
            elif self.test_no == 5:
                message = PUBLIC_KEY_DATAGRAM_HEADER + base64.b85encode(TFC_PUBLIC_KEY_LENGTH * b'\x01')

            # Group management headers
            elif self.test_no == 6:
                message = GROUP_MSG_INVITE_HEADER

            elif self.test_no == 7:
                message = GROUP_MSG_JOIN_HEADER

            elif self.test_no == 8:
                message = GROUP_MSG_MEMBER_ADD_HEADER

            elif self.test_no == 9:
                message = GROUP_MSG_MEMBER_REM_HEADER

            elif self.test_no == 10:
                message = GROUP_MSG_EXIT_GROUP_HEADER

            # Invalid header
            elif self.test_no == 11:
                message = b'\x1f'

            # RequestException (no remaining data)
            elif self.test_no == 12:
                (_ for _ in ()).throw(requests.exceptions.RequestException)

            return message.split(b'\n')

    class MockFileResponse(object):
        """MockFileResponse object."""

        def __init__(self, content) -> None:
            self.content = content

    class Session(object):
        """Mock session object."""

        def __init__(self) -> None:
            """Create new Session object."""
            self.proxies   = dict()
            self.timeout   = None
            self.url       = None
            self.stream    = False
            self.test_no   = 0
            self.response  = TestGetDataLoop.MockResponse()
            self.url_token = TestGetDataLoop.url_token
            self.onion_url = 'http://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaam2dqd.onion'

        def get(self, url: str, timeout: int = 0, stream: bool = False) -> Any:
            """Return data depending on what test is in question."""

            self.stream  = stream
            self.timeout = timeout

            if url == f"{self.onion_url}/{self.url_token}/messages":

                # Test function recovers from RequestException.
                if self.test_no == 1:
                    self.test_no += 1
                    (_ for _ in ()).throw(requests.exceptions.RequestException)

                if self.test_no >= 2:
                    self.test_no += 1
                    return self.response

            elif url == f"{self.onion_url}/{self.url_token}/files":

                # Test file data is received
                if self.test_no == 0:
                    self.test_no += 1
                    return TestGetDataLoop.MockFileResponse(b'test')

                # Test function recovers from RequestException.
                if self.test_no > 1:
                    (_ for _ in ()).throw(requests.exceptions.RequestException)

    @staticmethod
    def mock_session() -> Session:
        """Return mock Session object."""
        return TestGetDataLoop.Session()

    def setUp(self) -> None:
        """Pre-test actions."""
        self.o_session   = requests.session
        self.queues      = gen_queue_dict()
        requests.session = TestGetDataLoop.mock_session

    def tearDown(self) -> None:
        """Post-test actions."""
        requests.session = self.o_session
        tear_queues(self.queues)

    def test_get_data_loop(self) -> None:
        # Setup
        onion_pub_key = bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH)
        settings      = Gateway()
        onion_addr    = pub_key_to_onion_address(bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH))
        short_addr    = pub_key_to_short_address(bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH))
        url_token     = TestGetDataLoop.url_token
        session       = TestGetDataLoop.mock_session()

        # Test
        self.assertIsNone(get_data_loop(onion_addr, url_token, short_addr,
                                        onion_pub_key, self.queues, session, settings))

        self.assertIsNone(get_data_loop(onion_addr, url_token, short_addr,
                                        onion_pub_key, self.queues, session, settings))

        self.assertEqual(self.queues[DST_MESSAGE_QUEUE].qsize(), 2)  # Message and file
        self.assertEqual(self.queues[GROUP_MSG_QUEUE].qsize(),   5)  # 5 group management messages

class TestGroupManager(unittest.TestCase):

    def test_group_manager(self) -> None:

        queues = gen_queue_dict()

        def queue_delayer() -> None:
            """Place messages to queue one at a time."""
            time.sleep(0.1)

            # Test function recovers from incorrect group ID size
            queues[GROUP_MSG_QUEUE].put((
                GROUP_MSG_EXIT_GROUP_HEADER,
                bytes((GROUP_ID_LENGTH - 1)),
                pub_key_to_short_address(bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH))
                ))

            # Test group invite for added and removed contacts
            queues[GROUP_MGMT_QUEUE].put((RP_ADD_CONTACT_HEADER, nick_to_pub_key('Alice') + nick_to_pub_key('Bob')))
            queues[GROUP_MGMT_QUEUE].put((RP_REMOVE_CONTACT_HEADER, nick_to_pub_key('Alice')))

            for header in [GROUP_MSG_INVITE_HEADER,     GROUP_MSG_JOIN_HEADER,
                           GROUP_MSG_MEMBER_ADD_HEADER, GROUP_MSG_MEMBER_REM_HEADER]:
                queues[GROUP_MSG_QUEUE].put(
                    (header,
                     bytes(GROUP_ID_LENGTH) + nick_to_pub_key('Bob') + nick_to_pub_key('Charlie'),
                     pub_key_to_short_address(bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH))
                     ))

            queues[GROUP_MSG_QUEUE].put(
                (GROUP_MSG_EXIT_GROUP_HEADER,
                 bytes(GROUP_ID_LENGTH),
                 pub_key_to_short_address(bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH))
                 ))

            # Exit test
            time.sleep(0.2)
            queues[UNIT_TEST_QUEUE].put(EXIT)
            queues[GROUP_MSG_QUEUE].put(
                (GROUP_MSG_EXIT_GROUP_HEADER,
                 bytes(GROUP_ID_LENGTH),
                 pub_key_to_short_address(bytes(ONION_SERVICE_PUBLIC_KEY_LENGTH))
                 ))

        # Test
        threading.Thread(target=queue_delayer).start()
        self.assertIsNone(g_msg_manager(queues, unit_test=True))
        tear_queues(queues)

class TestClientScheduler(unittest.TestCase):

    def test_client_scheduler(self) -> None:
        queues             = gen_queue_dict()
        gateway            = Gateway()
        server_private_key = X448.generate_private_key()

        def queue_delayer() -> None:
            """Place messages to queue one at a time."""
            time.sleep(0.1)
            queues[TOR_DATA_QUEUE].put(
                ('1234', nick_to_onion_address('Alice')))
            queues[CONTACT_MGMT_QUEUE].put(
                (RP_ADD_CONTACT_HEADER, b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')]), True))
            time.sleep(0.1)
            queues[CONTACT_MGMT_QUEUE].put(
                (RP_REMOVE_CONTACT_HEADER, b''.join([nick_to_pub_key('Alice'), nick_to_pub_key('Bob')]), True))
            time.sleep(0.1)
            queues[UNIT_TEST_QUEUE].put(EXIT)
            time.sleep(0.1)
            queues[CONTACT_MGMT_QUEUE].put((EXIT, EXIT, EXIT))

        threading.Thread(target=queue_delayer).start()

        self.assertIsNone(client_scheduler(queues, gateway, server_private_key, unit_test=True))
        tear_queues(queues)

class TestContactRequestManager(unittest.TestCase):

    def test_contact_request_manager(self) -> None:

        queues = gen_queue_dict()

        def queue_delayer() -> None:
            """Place messages to queue one at a time."""
            time.sleep(0.1)
            queues[C_REQ_MGMT_QUEUE].put(
                (RP_ADD_CONTACT_HEADER, b''.join(list(map(nick_to_pub_key, ['Alice', 'Bob'])))))
            time.sleep(0.1)

            # Test that request from Alice does not appear
            queues[CONTACT_REQ_QUEUE].put((nick_to_onion_address('Alice')))
            time.sleep(0.1)

            # Test that request from Charlie appears
            queues[CONTACT_REQ_QUEUE].put((nick_to_onion_address('Charlie')))
            time.sleep(0.1)

            # Test that another request from Charlie does not appear
            queues[CONTACT_REQ_QUEUE].put((nick_to_onion_address('Charlie')))
            time.sleep(0.1)

            # Remove Alice
            queues[C_REQ_MGMT_QUEUE].put((RP_REMOVE_CONTACT_HEADER, nick_to_pub_key('Alice')))
            time.sleep(0.1)

            # Load settings from queue
            queues[C_REQ_STATE_QUEUE].put(False)
            queues[C_REQ_STATE_QUEUE].put(True)

            # Test that request from Alice is accepted
            queues[CONTACT_REQ_QUEUE].put((nick_to_onion_address('Alice')))
            time.sleep(0.1)

            # Exit test
            queues[UNIT_TEST_QUEUE].put(EXIT)
            queues[CONTACT_REQ_QUEUE].put(nick_to_pub_key('Charlie'))

        threading.Thread(target=queue_delayer).start()
        self.assertIsNone(c_req_manager(queues, unit_test=True))
        tear_queues(queues)

if __name__ == '__main__':
    unittest.main(exit=False)


from __future__ import annotations

from alpaca.utils import Visitor
from alpaca.concepts import Type, TypeFactory
from eisen.common import binary_ops, boolean_return_ops, implemented_primitive_types
from eisen.state.basestate import BaseState
from eisen.state.state_posttypecheck import State_PostTypeCheck
from eisen.state.typecheckerstate import TypeCheckerState
from eisen.common.restriction import PrimitiveRestriction, ImmutableRestriction
import eisen.adapters as adapters
from eisen.validation.validate import Validate
from eisen.validation.callunwrapper import CallUnwrapper
from eisen.validation.restructure_is_statement import RestructureIsStatement
from eisen.validation.typeparser import TypeParser

from eisen.validation.builtin_print import Builtins

State = TypeCheckerState

class TypeChecker(Visitor):
    """this evaluates the flow of types throughout the ast, and records which
    type flows up through each ast.
    """

    def __init__(self, debug: bool = False):
        super().__init__(debug=debug)
        self.typeparser = TypeParser()

    def run(self, state: BaseState):
        self.apply(TypeCheckerState.create_from_basestate(state))
        return State_PostTypeCheck.create_from_basestate(state)

    def apply(self, state: State) -> Type:
        # this guards the function such that if there is a critical exception thrown
        # downstream, the method will skip execution.
        if state.critical_exception:
            return state.get_void_type()

        result = self._route(state.get_ast(), state)
        if result is None: result = state.get_void_type()
        TypeChecker.set_returned_type(state, result)
        return result

    @classmethod
    def get_curried_type(cls, state: State, fn_type: Type, n_curried_args: int) -> Type:
        argument_type = fn_type.get_argument_type()
        if not argument_type.is_tuple():
            if n_curried_args == 1:
                return TypeFactory.produce_function_type(
                    arg=state.get_void_type(),
                    ret=fn_type.get_return_type(),
                    mod=fn_type.mod).with_restriction(ImmutableRestriction())
            raise Exception(f"tried to curry more arguments than function allows: {n_curried_args} {fn_type}")

        if len(argument_type.components) - n_curried_args == 1:
            # unpack tuple to just a single type
            curried_fn_args = argument_type.components[-1]
        elif len(argument_type.components) - n_curried_args == 0:
            curried_fn_args = state.get_void_type()
        else:
            curried_fn_args = TypeFactory.produce_tuple_type(argument_type.components[n_curried_args:])

        return TypeFactory.produce_function_type(
            arg=curried_fn_args,
            ret=fn_type.get_return_type(),
            mod=fn_type.mod).with_restriction(ImmutableRestriction())

    @classmethod
    def set_returned_type(cls, state: State, type: Type):
        state.get_node_data().returned_type = type

    @classmethod
    def add_reference_type(cls, state: State, name: str, type: Type):
        state.get_context().add_reference_type(name, type)

    def apply_to_first_child_of(self, state: State) -> Type:
        return self.apply(state.but_with_first_child())

    def apply_to_second_child_of(self, state: State) -> Type:
        return self.apply(state.but_with_second_child())

    def returns_void_type(f):
        """this signifies that the void type should be returned. abstracted so if
        the void_type is changed, we can easily configure it here.
        """
        def decorator(fn, state: State):
            f(fn, state)
            return state.get_void_type()
        return decorator

    @Visitor.for_tokens
    def token_(fn, state: State) -> Type:
        if state.get_ast().type in implemented_primitive_types:
            return (TypeFactory
                .produce_novel_type(name=state.get_ast().type)
                .with_restriction(PrimitiveRestriction()))
        elif state.get_ast().type == "nil":
            return TypeFactory.produce_nil_type()

        # debug only
        raise Exception(f"unexpected token type of {state.get_ast().type}, {state.get_ast().value}")

    @Visitor.for_ast_types("start", "return", "cond", "seq")
    def start_(fn, state: State):
        state.apply_fn_to_all_children(fn)

    @Visitor.for_ast_types("mod")
    def mod_(fn, state: State):
        adapters.Mod(state).enter_module_and_apply(fn)

    @Visitor.for_ast_types("!")
    def not_(fn, state: State) -> Type:
        return fn.apply_to_first_child_of(state)

    @Visitor.for_ast_types(".")
    def dot_(fn, state: State) -> Type:
        node = adapters.Scope(state)
        parent_type = fn.apply(state.but_with(ast=node.get_object_ast()))
        attr_name = node.get_attribute_name()
        if Validate.has_member_attribute(state, parent_type, attr_name).failed():
            return state.get_abort_signal()
        return parent_type.get_member_attribute_by_name(attr_name)

    @Visitor.for_ast_types("::")
    def scope_(fn, state: State) -> Type:
        node = adapters.ModuleScope(state)
        instance = node.get_end_instance()
        return instance.type

    @Visitor.for_ast_types("tuple", "params", "prod_type", "lvals", "curried")
    def tuple_(fn, state: State) -> Type:
        if len(state.get_ast()) == 0:
            return state.get_void_type()
        elif len(state.get_ast()) == 1:
            # if there is only one child, then we simply pass the type back, not as a tuple
            return fn.apply_to_first_child_of(state)
        return TypeFactory.produce_tuple_type(
            components=[fn.apply(state.but_with(ast=child)) for child in state.get_ast()])

    @Visitor.for_ast_types("if")
    def if_(fn, state: State) -> Type:
        adapters.If(state).enter_context_and_apply(fn)

    @Visitor.for_ast_types("while")
    def while_(fn, state: State) -> Type:
        adapters.While(state).enter_context_and_apply(fn)

    @classmethod
    def _shared_call_checks(cls, state: State, params_type: Type):
        node = adapters.RefLike(state.but_with_first_child())
        fn_type = node.resolve_reference_type(params_type)
        if Validate.instance_exists(state, node.get_name(), fn_type).failed():
            return state.get_abort_signal()

        if Validate.correct_argument_types(state,
            name=node.get_name(),
            arg_type=fn_type.get_argument_type(),
            given_type=params_type).failed():
                return state.get_abort_signal()
        return fn_type.get_return_type()

    @Visitor.for_ast_types("raw_call")
    def raw_call(fn, state: State) -> Type:
        guessed_params_type = fn.apply_to_second_child_of(state)

        # this will actually change the ast inplace, converting (raw_call ...)
        # into (call (ref ...) (params ...))
        if guessed_params_type == state.get_abort_signal():
            return state.get_abort_signal()
        params_type = CallUnwrapper.process(
            state=state,
            guessed_params_type=guessed_params_type,
            fn=fn)

        fn.apply(state.but_with(ast=state.first_child(), arg_type=params_type))
        if adapters.Call(state).is_print():
            return Builtins.get_type_of_print(state).get_return_type()

        return TypeChecker._shared_call_checks(state, params_type)

    @Visitor.for_ast_types("is")
    def is_(fn, state: State) -> Type:
        node = adapters.Is(state)
        # if the check is not against nil, treat this as a call to
        # some "is" function of a variant
        if node.get_type_name() != "nil":
            params_type = node.get_considered_type().parent_type
            RestructureIsStatement.run(state)
            fn.apply(state.but_with(ast=state.first_child(), arg_type=params_type))
            fn.apply(state.but_with_second_child())
            return TypeChecker._shared_call_checks(state, params_type)
        return state.get_bool_type()

    @Visitor.for_ast_types("curry_call")
    def curry_call_(fn, state: State) -> Type:
        fn_type = fn.apply_to_first_child_of(state)
        curried_args_type = fn.apply_to_second_child_of(state)
        n_curried_args = 1
        if curried_args_type.is_tuple():
            n_curried_args = len(curried_args_type.components)

        return TypeChecker.get_curried_type(state, fn_type, n_curried_args)

    @Visitor.for_ast_types("struct")
    def struct(fn, state: State) -> Type:
        node = adapters.Struct(state)
        if node.has_create_ast():
            fn.apply(state.but_with(ast=node.get_create_ast()))

    @Visitor.for_ast_types("variant")
    def variant_(fn, state: State) -> Type:
        fn.apply(state.but_with(ast=adapters.Variant(state).get_is_ast()))

    @Visitor.for_ast_types("interface", "impls")
    def interface_(fn, state: State) -> Type:
        # no action required
        return

    @Visitor.for_ast_types("cast")
    def cast(fn, state: State) -> Type:
        # (cast (ref name) (type into))
        left_type = fn.apply_to_first_child_of(state)
        right_type = fn.apply_to_second_child_of(state)

        if Validate.castable_types(
            state=state,
            type=left_type,
            cast_into_type=right_type).failed(): return state.get_abort_signal()
        return right_type

    @Visitor.for_ast_types("def", "create", ":=", "is_fn")
    def fn(fn, state: State) -> Type:
        adapters.CommonFunction(state).enter_context_and_apply(fn)

    @classmethod
    def _create_references(cls, state: State, names: list[str], type: Type):
        types = type.components if type.is_tuple() else [type] * len(names)
        if any(type is state.get_abort_signal() for type in types):
            state.critical_exception.set(True)
            return
        if Validate.all_names_are_unbound(state, names).failed():
            return state.get_abort_signal()
        for name, t in zip(names, types):
            TypeChecker.add_reference_type(state, name, t)
        return type

    @Visitor.for_ast_types(*adapters.InferenceAssign.ast_types)
    def idecls_(fn, state: State):
        node = adapters.InferenceAssign(state)
        names = node.get_names()
        type = fn.apply_to_second_child_of(state)
        type = type.with_restriction(node.get_restriction(hint=type))
        return TypeChecker._create_references(state, names, type)

    @Visitor.for_ast_types(*adapters.Typing.ast_types)
    def decls_(fn, state: State):
        node = adapters.Typing(state)
        names = node.get_names()
        type = fn.apply(state.but_with(ast=node.get_type_ast())).with_restriction(node.get_restriction())
        return TypeChecker._create_references(state, names, type)

    @Visitor.for_ast_types("fn_type", "para_type", *adapters.TypeLike.ast_types)
    def _type1(fn, state: State) -> Type:
        return fn.typeparser.apply(state)

    @Visitor.for_ast_types("=", "<-", *binary_ops)
    def binary_ops(fn, state: State) -> Type:
        left_type = fn.apply_to_first_child_of(state)
        right_type = fn.apply_to_second_child_of(state)

        if Validate.can_assign(state, left_type, right_type).failed():
            return state.get_abort_signal()
        return left_type

    @Visitor.for_ast_types(*boolean_return_ops)
    def boolean_return_ops_(fn, state: State) -> Type:
        left_type = fn.apply_to_first_child_of(state)
        right_type = fn.apply_to_second_child_of(state)

        if Validate.equivalent_types(state, left_type, right_type).failed():
            return state.get_abort_signal()
        return state.get_bool_type()

    @Visitor.for_ast_types("fn")
    def fn_(fn, state: State) -> Type:
        node = adapters.Fn(state)
        instance = node.resolve_function_instance(state.get_arg_type())
        if Validate.instance_exists(state, node.get_name(), instance).failed():
            return state.get_abort_signal()
        return instance.type

    @Visitor.for_ast_types("ref")
    def ref_(fn, state: State) -> Type:
        node = adapters.Ref(state)
        if node.is_print():
            return Builtins.get_type_of_print(state)

        type = node.resolve_reference_type()
        if Validate.instance_exists(state, node.get_name(), type).failed():
            return state.get_abort_signal()
        return type

    @Visitor.for_ast_types(*adapters.ArgsRets.ast_types)
    def args_(fn, state: State) -> Type:
        if not state.get_ast():
            return state.get_void_type()
        return fn.apply(state.but_with(ast=state.first_child()))

    @Visitor.for_ast_types("new_vec")
    def new_vec_(fn, state: State) -> Type:
        node = adapters.NewVec(state)
        return node.get_type()

    @Visitor.for_ast_types("index")
    def index_(fn, state: State) -> Type:
        return fn.apply_to_first_child_of(state).parametrics[0]

    @Visitor.for_ast_types("annotation")
    def annotation_(fn, state: State) -> Type:
        # Not implemented
        return


#!/usr/bin/python
# -*- coding: UTF-8 -*-

"""This is the entry module of this application. This module collects the input form user, gets it splitted by stringsplitter module and then passes
the control to executor module for further processing which prints the list of searched tracks

"""

import stringsplitter
import executor
import time

def findtracks():
    """this method takes input from user and breaks that input in different parts using stringsplitter module and sends that list of dicts to executor module.

    """
    trackstr = raw_input('input your poem: ')
    trackstr = trackstr.strip()
    inputdict = stringsplitter.break_string(trackstr)
    executor.processrequest(inputdict)

if __name__ == '__main__':
     findtracks()


'''
Created on 10 jan. 2016

@author: Henrik
Things to think about
ha alla vikter som en matris men vad händer med bias?
Oavsätt antal lager så är den altid linear

'''

import copy
import random
import math
import time

import NeuralNetwork.brain
from NeuralNetwork.brain import BrainFullyCon

class EvoTest(object):
    '''
    classdocs
    '''

    def __init__(self, numberOfSpiders):
        '''
        Constructor
        '''

        millis = int(round(time.time() * 1000))
        random.seed(millis)
        self.startTime = time.time()

        self.inputSize = 3
        self.outputSize = 1
        self.spiders = []
        self.topScoreSpiders = []
        self.spiderChildren = []
        self.numberOfSpiders = numberOfSpiders
        self.brainStruct = [self.inputSize,30,30,self.outputSize]
        self.spiderIDCounter = 0
        self.spidersSurvivied = 0
        self.spidersPure = 0
        self.staticInput = []
        self.staticAnswer = []

        for i in range(0,self.numberOfSpiders):
            self.spiders.append(Spider(self.brainStruct,self.spiderIDCounter,[self.spiderIDCounter,self.spiderIDCounter]))
            self.spiderIDCounter = self.spiderIDCounter+1

        for spider in self.spiders:
            spider.brain.randomizeAllWeights()

        self.generateStaticInput(3, 30)
        for generation in range(0,100):
            nrOfSims = 10000
            self.simpleSimulate()
            #self.simulate(nrOfSims)
            #self.categorizeSpecies()
            print("---------------------------------------------")
            print("Gen=",generation, "SurvPop=",self.spidersSurvivied, "PurePop=", self.spidersPure)
            for i in range(0,10):
                print(i,": Fitness:",round(self.spiders[i].fitness,4),"\tCorrectGuesses=",round(self.spiders[i].correctGuesses/30*100,2),"%", "\tID=",self.spiders[i].ID, "Parents ID=",self.spiders[i].parentsID, "NrOfGenPure=",self.spiders[i].nrOfGenerationsPure)
            self.killOff(0.5)  #Keep 10% alive, kill the rest by Gaussian dist
            print("Killoff: complete")
            self.safeKeepTopScoreSpiders(0.10)
            print("Elistism: complete")
            self.crossOver1()
            print("CrossOver: complete")
            self.mutate(0.05)  #Don't mutate the top 5% of the survivors, at least one will not mutate if non zero
            print("Mutate: complete")

    def generateStaticInput(self,length,number):
        for i in range(0,number):
            self.staticInput.append([])
            for ii in range(0,length):
                self.staticInput[i].append(random.random())
            if random.random()>0.5:
                self.staticInput[i].sort()
                self.staticAnswer.append(1)
            else:
                self.staticAnswer.append(0)
                while(sorted(self.staticInput[i])==self.staticInput[i]):
                        random.shuffle(self.staticInput[i])

    def simpleSimulate(self):
        for spider in self.spiders:
            spider.correctGuesses = 0
            spider.output = 0

            for i in range(0,self.staticAnswer.__len__()):
                outputTemp = spider.brain.crunch(self.staticInput[i])
                #print(outputTemp)
                if self.staticAnswer[i] == 1:
                    spider.output = spider.output + (1-outputTemp[0])
                    if outputTemp[0]>0.5:
                        spider.correctGuesses =spider.correctGuesses+1
                elif self.staticAnswer[i] == 0:
                    spider.output = spider.output + (outputTemp[0])
                    if outputTemp[0]<=0.5:
                        spider.correctGuesses =spider.correctGuesses+1
                else:
                    print("something went wrong")

            spider.fitness = 0.5/(spider.output/self.staticAnswer.__len__())

            self.spiders=sorted(self.spiders, key=lambda spider: spider.correctGuesses)
            self.spiders.reverse()

    def simulate(self,NrOfTimes):
        for spider in self.spiders:
            spider.correctGuesses = 0
            spider.output = 0

            for times in range(0,NrOfTimes):
                sortOrNotSorted = random.randrange(0,2,1)
                inputVector = []
                for i in range(0,self.inputSize):
                    inputVector.append(random.random())

                if sortOrNotSorted==1:
                    answer = "sorted"
                    inputVector.sort()
                else:
                    answer = "random"
                    while(sorted(inputVector)==inputVector):
                        random.shuffle(inputVector)

                outputTemp = spider.brain.crunch(inputVector)
                if answer == "sorted":
                    spider.output = spider.output + (1-outputTemp[0])
                    if outputTemp[0]>0.5:
                        spider.correctGuesses =spider.correctGuesses+1
                elif answer == "random":
                    spider.output = spider.output + (outputTemp[0])
                    if outputTemp[0]<=0.5:
                        spider.correctGuesses =spider.correctGuesses+1
                else:
                    print("something went wrong")

            spider.fitness = 0.5/(spider.output/NrOfTimes)

        self.spiders=sorted(self.spiders, key=lambda spider: spider.output)   # lowest has the least error out of NrOfTimes tries

    def killOff(self,procentToSafeKeep):
        i = 0
        spidersToSafeKeep = round(procentToSafeKeep*self.numberOfSpiders)
        newSpiders = []
        for spider in self.spiders:
            if i<=spidersToSafeKeep:
                newSpiders.append(spider)
            else:
                if i < (abs(round(random.gauss(0, round((self.numberOfSpiders-spidersToSafeKeep)/3))))+spidersToSafeKeep):
                    newSpiders.append(spider)
            i = i +1
        self.spiders = newSpiders
        self.spidersSurvivied = self.spiders.__len__()

    def categorizeSpecies(self):
        '''Will categorize all spiders into species and give each species a letter code
        If the checked spider's weights are too different from the all the existing species
        then a new species is created with a letter code following simple rules:
        if mutated from spider species which letter code was
        -CAAAAA then use CAAAAB
        if completely new random spider then use
        -CUSAA then use CUTAA
        -etc

        '''
        b = 0

    def safeKeepTopScoreSpiders(self,procentToSafeKeep):
        self.topScoreSpiders = []
        spidersToSafeKeep = math.ceil(self.spiders.__len__()*procentToSafeKeep)
        i = 0
        for spider in self.spiders:
            if i<spidersToSafeKeep:
                spiderCopy = self.copySpider(spider,self.brainStruct,spider.ID)
                spider.ID = self.spiderIDCounter
                self.spiderIDCounter = self.spiderIDCounter+1
                self.topScoreSpiders.append(spiderCopy)
                spiderCopy.nrOfGenerationsPure = spider.nrOfGenerationsPure+1

                #self.spiders.remove(spider)
            else:
                break
            i = i +1

    def copySpider(self, spiderOriginal,brainStruct,spiderIDCounter):
        spiderCopy = Spider(brainStruct,spiderIDCounter,spiderOriginal.parentsID)
        spiderCopy.brain.copyBrainWeights(spiderOriginal.brain.allLayers)
        return spiderCopy

    def crossOver1(self):
    #Finds two parents by random with the fitness as weight. Then takes the genes from
    #both parents and combines them into a new child
        self.spiderChildren = []

        for k in range(0,self.numberOfSpiders-(self.spiders.__len__()+self.topScoreSpiders.__len__())):
            spiderParents = []
            runAgain = 1
            while runAgain == 1:
                roulette = []
                intervalInc = 0
                for spider in self.spiders:
                    intervalInc = intervalInc + spider.fitness*100
                    roulette.append([spider,intervalInc])
                if intervalInc<self.spiders.__len__():
                    print("WARNING BAD ROULETT RESOLUTION")
                index = random.randrange(0,math.ceil(intervalInc+1),1)

                n = 0
                while index > math.ceil(roulette[n][1]):
                    n = n+1
                if spiderParents.__len__()==0:
                    spiderParents.append(roulette[n][0])
                elif spiderParents[0]!=roulette[n][0]:
                        spiderParents.append(roulette[n][0])
                        runAgain = 0
            if spiderParents.__len__()!=2:
                print("WARNING: NOT 2 PARENTS")
            spiderChild = Spider(self.brainStruct,self.spiderIDCounter,[spiderParents[0].ID,spiderParents[1].ID])
            self.spiderIDCounter = self.spiderIDCounter+1

            for layerIndex in range(0,self.brainStruct.__len__()):
                for neuronIndex in range(0,self.brainStruct[layerIndex]):
                    spiderChild.brain.allLayers[layerIndex][neuronIndex].b = spiderParents[0].brain.allLayers[layerIndex][neuronIndex].b
                    spiderChild.brain.allLayers[layerIndex][neuronIndex].k = copy.deepcopy(spiderParents[0].brain.allLayers[layerIndex][neuronIndex].k)
                else:  #WTF DOES THIS ELSE ?!?!?
                    spiderChild.brain.allLayers[layerIndex][neuronIndex].b = spiderParents[1].brain.allLayers[layerIndex][neuronIndex].b
                    spiderChild.brain.allLayers[layerIndex][neuronIndex].k = copy.deepcopy(spiderParents[1].brain.allLayers[layerIndex][neuronIndex].k)
            self.spiderChildren.append(spiderChild)

        self.spiders.extend(self.spiderChildren)

    def mutate(self,procentHowMuch):
        for spider in self.spiders:
            spider.nrOfGenerationsPure = 0
            spider.brain.randomizeSomeWeights(procentHowMuch)

        self.spiders.extend(self.topScoreSpiders)
        self.spidersPure = self.topScoreSpiders.__len__()

        if self.spiders.__len__() != self.numberOfSpiders:
            print("SPIDER POPULATION IS CHANGING SIZE")

class Spider(object):

    def __init__(self,struct,ID,parentsID):
        self.brain = BrainFullyCon(struct)
        self.output = 0
        self.nrOfGenerationsPure = 0
        self.correctGuesses = 0
        self.ID = ID
        self.fitness = 0
        self.parentsID = parentsID

evoTest = EvoTest(30)

print("elapsed time: ",(time.time()-evoTest.startTime),"[s]")


import logging
import sys

def setLogger(logger, fname=None, logLevel='info'):
    """ Function to set up the handle error logging.
    logger (obj) = a logger object

    fname = the name of the log file, if no logfile is given then logging
            information will be pritned to stdout.

    logLevel = level of information to print out, options are {info, debug} [Default: info]
    """

    # Determine log level
    if logLevel == 'info':
        _level = logging.INFO
    elif logLevel == 'debug':
        _level = logging.DEBUG

    # Set the level in logger
    logger.setLevel(_level)

    # Set the log format
    logfmt = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

    # Output to file or STDOUT
    if fname:
        fh = logging.FileHandler(filename=fname,mode='w')
        fh.setLevel(_level)
        fh.setFormatter(logfmt)
        logger.addHandler(fh)
    else:
        # Since no file was provide output to STDOUT and STDERR
        loghandler = logging.StreamHandler(stream=sys.stdout)
        errhandler = logging.StreamHandler(stream=sys.stderr)

        # Set logging level for the different output handlers.
        # ERRORs to STDERR, and everything else to STDOUT
        loghandler.setLevel(_level)
        errhandler.setLevel(logging.ERROR)

        # Format the log handlers
        loghandler.setFormatter(logfmt)
        errhandler.setFormatter(logfmt)

        # Add handler to the main logger
        logger.addHandler(loghandler)
        logger.addHandler(errhandler)


# pylint: disable=R0903,C0111,R0902
"""The :class:`geoutils.StagerDaemon` supports the daemonisation
facility for the geostager.

"""
__all__ = ["StagerDaemon"]

import signal
import daemoniser
import os
import time
import random
import string

from geosutils.log import log
from geosutils.files import (copy_file,
                             get_directory_files_list)

class StagerDaemon(daemoniser.Daemon):
    """:class:`StagerDaemon`

    """
    dry = False
    batch = False
    conf = None
    fixture_dir = None
    files_to_process = []
    regen_count = 10
    regen_sleep = 1.0

    def __init__(self,
                 pidfile,
                 dry=False,
                 conf=None,
                 fixture_dir=None,
                 regen_count=10,
                 regen_sleep=1):
        """:class:`StagerDaemon` initialisation.

        """
        super(StagerDaemon, self).__init__(pidfile=pidfile)

        self.dry = dry
        self.conf = conf
        self.fixture_dir = fixture_dir
        self.regen_count = regen_count
        self.regen_sleep = regen_sleep

    def _start(self, event):
        """Override the :meth:daemoniser.Daemon._start` method.

        """
        signal.signal(signal.SIGTERM, self._exit_handler)

        self.source_files()

        self.process(event)

    def process(self, event):
        """Main processing thread for the file stager process.

        Will cycle through the
        :attr:`geoutils.StagerDaemon.files_to_process`
        :attr:`geoutils.StagerDaemon.regen_count` times and recreate
        a new file for ingestion.

        **Args:**
            *event*: a :mod:`threading.Event` based internal semaphore
            that can be set via the :mod:`signal.signal.SIGTERM` signal
            event to perform a function within the running proess.

        """
        fixture_file_count = len(self.files_to_process)
        for regen_count in range(self.regen_count):
            if fixture_file_count <= 0:
                break

            file_index = regen_count % fixture_file_count
            regen_file = self.files_to_process[file_index]

            filename, extension = os.path.splitext(regen_file)
            regen_file_rand = ('%s_%s%s' % (filename,
                                            self.random_string(),
                                            extension))

            target_file =  os.path.join(self.conf.inbound_dir,
                                        os.path.basename(regen_file_rand))
            log.info('Regen file %d:%s' % ((regen_count + 1), target_file))

            if not self.dry:
                copy_file(regen_file, target_file)

            if event.isSet():
                break

            time.sleep(self.regen_sleep)

    def source_files(self):
        """Checks inbound directory (defined by the
        :attr:`geoutils.StagerDaemon.fixture_dir` attribute) for valid
        NITF files to be regenerated with a different filename.

        **Returns:**
            list of absolute path names to the files found

        """
        if (self.fixture_dir is None or
            not os.path.isdir(self.fixture_dir)):
            log.error('Fixture directory "%s" undefined or missing' %
                      self.fixture_dir)
        else:
            log.debug('Sourcing files at: %s' % self.fixture_dir)
            self.files_to_process = get_directory_files_list(self.fixture_dir)

        return self.files_to_process

    @staticmethod
    def random_string(length=10):
        rand = ''.join(random.choice(string.lowercase) for i in range(length))

        return rand


import tinyproto as tp
from threading import Lock, Thread

class ChatClientConnection(tp.TinyProtoConnection):
    __slots__ = (
        '__msg_inbox', '__msg_inbox_lock',
        '__msg_outbox', '__msg_outbox_lock',
        '__transaction_lock',
    )

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

        self.__msg_inbox = []
        self.__msg_outbox = []
        self.__msg_inbox_lock = Lock()
        self.__msg_outbox_lock = Lock()
        self.__transaction_lock = Lock()

    @property
    def Inbox(self):
        with self.__msg_inbox_lock:
            res = self.__msg_inbox
            self.__msg_inbox = []
            return res
    @Inbox.setter
    def Inbox(self, newval):
        with self.__msg_inbox_lock:
            self.__msg_inbox.append(newval)

    @property
    def Outbox(self):
        with self.__msg_outbox_lock:
            res = self.__msg_outbox
            self.__msg_outbox = []
            return res
    @Outbox.setter
    def Outbox(self, newval):
        with self.__msg_outbox_lock:
            self.__msg_outbox.append(newval)

    def transmission_received(self, msg):
        self.Inbox = msg.decode()

    def loop_pass(self):
        with self.__transaction_lock:
            pending_msgs = self.Outbox
            for msg in pending_msgs:
                self.transmit(msg.encode())

class ChatClient(tp.TinyProtoClient):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        t = Thread(target = self.start)
        t.start()

    def loop_pass(self):
        for conn_id, conn_o in self.active_connections.items():
            pending_msgs = conn_o.Inbox
            for m in pending_msgs:
                print(m)

if __name__ == '__main__':
    c = ChatClient(
        connection_handler = ChatClientConnection,
        timeout = 3,
    )

    print('Connecting ... ')
    conn_details = tp.TinyProtoConnectionDetails('localhost', 8088)
    cuid = c.connect_to(conn_details)
    conn = c.active_connections[cuid]
    print('Connection established!')

    buff = "abc"
    while buff != 'quit' and conn.is_alive():
        buff = input('')
        conn.Outbox = buff
    conn.shutdown = True
    c.shutdown = True


from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render
from django.db.models import F
# from django.template import loader
from django.views.generic import View
from communities.models import *
from news_feed.models import *
from notifications import models as notifications
from django.contrib.auth.models import User
# from django.urls import reverse
from .forms import *
import os
from datetime import datetime
import re
import os.path
import json
from itertools import chain
from notifications.models import Notification, Notificationlist

def home(request):
    groups = Group.objects.annotate(members=models.Count('subscribers') + models.Count('editors')).order_by('-members')
    context = {'groups': groups}
    return render(request, 'communities/base.html', context)

def community(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)

        if not group.can_see_group(request.user):
            context = {
                'group': group,
            }
            return render(request, 'communities/closedgroup.html', context)

        personal_in_community_articles = PersonalInCommunityArticle.objects.filter(allowed=True, group=group)
        community_articles = CommunityArticle.objects.filter(allowed=True, group=group)
        articles = sorted(chain(personal_in_community_articles, community_articles),
                          key=lambda instance: instance.pubdate, reverse=True)

        personal_in_community_request_articles = PersonalInCommunityArticle.objects.filter(allowed=False)
        community_request_articles = CommunityArticle.objects.filter(allowed=False)
        request_articles = sorted(chain(personal_in_community_request_articles, community_request_articles),
                                  key=lambda instance: instance.pubdate)

        articles_count = len(articles)
        if request.user.is_authenticated and group.personal_in_community_articles.filter(allowed=False,
                                                                                author=request.user):
            author_request_articles = group.personal_in_community_articles.filter(allowed=False,
                                                                                author=request.user).order_by(
                '-pubdate')
        else:
            author_request_articles = []
        context = {
            'group': group,
            'articles': articles,
            'request_articles': request_articles,
            'articles_count': articles_count,
            'author_request_articles': author_request_articles
        }
        return render(request, 'communities/community.html', context)
    else:
        return render(request, 'communities/nogroup.html', status=404)

def information(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)

        if not group.can_see_group(request.user):
            context = {
                'group': group,
            }
            return render(request, 'communities/closedgroup.html', context)

        articles_count = group.community_articles.filter(allowed=True).count()
        context = {'group': group, 'articles_count': articles_count}
        return render(request, 'communities/information.html', context)
    else:
        return render(request, 'communities/nogroup.html')

def collection(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)

        if not group.can_see_group(request.user):
            context = {
                'group': group,
            }
            return render(request, 'communities/closedgroup.html', context)

        articles_count = group.community_articles.filter(allowed=True).count()
        context = {'group': group, 'articles_count': articles_count}
        return render(request, 'communities/collection.html', context)
    else:
        return render(request, 'communities/nogroup.html')

def unsubscribe(request, groupid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        group.unsubscribe(request.user)
    return HttpResponse()

def subscribe(request, groupid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        group.subscribe(request.user)
    return HttpResponse()

def like(request, groupid, articleid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        article = group.articles.get(id=articleid)
        article.like(request.user)
    return HttpResponse()

def removelike(request, groupid, articleid):
    if request.user.is_authenticated:
        group = Group.objects.get(id=groupid)
        article = group.articles.get(id=articleid)
        article.removelike(request.user)
    return HttpResponse()

def createarticle(request, groupid):
    group = Group.objects.get(id=groupid)
    if request.user in group.editors.all() or request.user == group.admin or group.allowarticles == 1 or group.allowarticles == 2:
        if request.POST.get("text", ''):
            if request.POST.get("personal"):
                new_article = PersonalInCommunityArticle(group=group, author=request.user,
                                                         text=request.POST.get("text"))
            else:
                new_article = CommunityArticle(group=group, text=request.POST.get("text"))

            if group.allowarticles == 2 and not (request.user in group.editors.all() or request.user == group.admin):
                new_article.allowed = False
            new_article.save()

            for file in request.FILES.getlist('files'):
                if file.content_type in ['image/png', 'image/jpeg', 'application/pdf', 'text/plain',
                                         'application/msword'] and file.size <= 5000000:
                    new_file = BasicArticleFile(name=file.name, file=file)
                    new_file.save()
                    new_article.files.files.add(new_file)
                    new_article.files.save()

            request.user.profile.last_online_update()
    slug = group.slug
    return HttpResponseRedirect('/groups/' + str(slug) + '/')

class AddToCollection(View):
    def get(self, request, groupid):
        group = Group.objects.get(id=groupid)
        slug = group.slug
        return HttpResponseRedirect('/groups/' + str(slug) + '/collection/')

    def post(self, request, groupid):
        group = Group.objects.get(id=groupid)
        if request.user.is_authenticated and (request.user in group.editors.all() or request.user == group.admin):
            for file in request.FILES.getlist('files'):
                if file.content_type in ['image/png', 'image/jpeg', 'application/pdf', 'text/plain',
                                         'application/msword'] and file.size <= 5000000:
                    new_file = GroupFile(group=group, name=file.name, file=file)
                    new_file.save()

            request.user.profile.last_online_update()
        slug = group.slug
        return HttpResponseRedirect('/groups/' + str(slug) + '/collection/')

class EditArticle(View):
    def get(self, request, groupid, articleid):
        group = Group.objects.get(id=groupid)
        slug = group.slug
        return HttpResponseRedirect('/groups/' + str(slug) + '/')

    def post(self, request, groupid, articleid):
        bound_form = ArticleForm(request.POST)
        group = Group.objects.get(id=groupid)
        slug = group.slug
        if request.user in group.editors.all() or request.user == group.admin:
            article = BasicArticle.objects.get(id=articleid)
            if bound_form.is_valid():
                article.text = request.POST.get("text")
                article.save()

                for fileid in request.POST.get("removedfiles").split():
                    try:
                        file = article.files.get(id=int(fileid))
                        file.delete()
                    except:
                        pass

                for file in request.FILES.getlist('files'):
                    if file.content_type in ['image/png', 'image/jpeg', 'application/pdf', 'text/plain',
                                             'application/msword'] and file.size <= 5000000:
                        new_file = ArticleFile(article=article, name=file.name, file=file)
                        new_file.save()

            request.user.profile.last_online_update()

        return HttpResponseRedirect('/groups/' + str(slug) + '/')

def creategroup(request):
    if request.user.is_authenticated:
        group = Group(groupname=request.POST.get("groupname"), admin=request.user, pubdate=datetime.now())
        group.save()
        group.slug = str(group.id)
        group.save()

        request.user.profile.last_online_update()
        return HttpResponse(group.slug)
    else:
        return HttpResponse(-1)

def edit(request, groupid):
    group = Group.objects.get(id=groupid)
    if request.user.is_authenticated:
        request.user.profile.last_online_update()
        if request.user in group.editors.all() or request.user == group.admin:
            if request.POST.get('type') == 'allowarticle':
                article = BasicArticle.objects.get(id=request.POST.get('data')).get_child()
                if article.__class__.__name__ != "PersonalArticle" and article.group == group:
                    article.allowed = True
                    article.save()

                    if article.__class__.__name__ == "PersonalInCommunityArticle":
                        if article.author.profile.notificationsettings.post_published_notifications:
                            notifications.create_notification_post_published(group, article.author, 'post_accepted')

                return HttpResponse('Ok')
            elif request.POST.get('type') == 'deletearticle':
                article = BasicArticle.objects.get(id=request.POST.get('data')).get_child()
                if article.__class__.__name__ != "PersonalArticle" and article.group == group:
                    if article.__class__.__name__ == "PersonalInCommunityArticle":
                        if article.author.profile.notificationsettings.post_published_notifications:
                            notifications.create_notification_post_published(group, article.author, 'post_accepted')
                    article.delete()

                return HttpResponse('Ok')
            elif request.POST.get('type') == 'getpost':
                article = BasicArticle.objects.get(id=request.POST.get('data'))
                return HttpResponse(article.text)
            elif request.POST.get('type') == 'deletepost':
                article = BasicArticle.objects.get(id=request.POST.get('data'))

                if request.user != article.author and not article.author is None:
                    try:
                        notification_text = "Ваш пост в " + group.groupname + " удален"
                        notification_name = "Пост удален"
                        notification_href = '/groups/' + group.slug + '/'
                        notification = Notification(not_text=notification_text, not_name=notification_name,
                                                    not_link=notification_href, not_date=datetime.now())
                        notification.save()
                        notificationlist = Notificationlist.objects.get(user=article.author).notifications
                        notificationlist.add(notification)
                    except:
                        print('Notification Error')

                article.delete()
                return HttpResponse('Ok')
            if request.POST.get('type') == 'delete_from_collection':
                # print(request.POST.get('file'))
                try:
                    groupfile = group.files.get(id=request.POST.get('file'))
                    groupfile.delete()
                except:
                    pass

                return HttpResponse('Ok')

        if request.POST.get('type') == 'delete_request_article':
            article = BasicArticle.objects.get(id=request.POST.get('data'))
            article.delete()
            return HttpResponse('Ok')
        elif request.POST.get('type') == 'plus' or request.POST.get('type') == 'plusplus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.plus(request.user)
        elif request.POST.get('type') == 'remove_plus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.remove_plus(request.user)
        elif request.POST.get('type') == 'minus' or request.POST.get('type') == 'minusminus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.minus(request.user)
        elif request.POST.get('type') == 'remove_minus':
            basic_article = BasicArticle.objects.get(id=request.POST.get('articleid'))
            if basic_article:
                basic_article.remove_minus(request.user)

    if request.POST.get('type') == 'delete_from_collection' and not os.path.isfile(request.POST.get('file')):
        # print(request.POST.get('file'))
        try:
            groupfile = group.files.get(id=request.POST.get('file'))
            groupfile.delete()
        except:
            pass
        return HttpResponse('Ok')

    return HttpResponse('error0')

def moreedit(request, groupid):
    if request.user.is_authenticated:
        request.user.profile.last_online_update()
        group = Group.objects.get(id=groupid)
        slug = group.slug
        if request.user in group.editors.all() or request.user == group.admin:
            if request.POST.get('type') == 'public':
                group.public = not group.public
                if group.public:
                    for user in group.subrequests.all():
                        group.subscribers.add(user)
                    group.subrequests.clear()
                group.save()
                return HttpResponse('opened / closed')
            elif request.POST.get('type') == 'delete':
                group.delete()
                return HttpResponse('deleted')
            elif request.POST.get('type') == 'allowsub':
                user = User.objects.filter(id=request.POST.get('userid'))[0]

                group.subrequests.remove(user)
                group.subscribers.add(user)

                if user.profile.notificationsettings.accepted_to_group_notifications:
                    notifications.create_notification_accepted_to_group(group, user, 'request_accepted')

                return HttpResponse('allowed')
            elif request.POST.get('type') == 'rejectsub':
                user = User.objects.filter(id=request.POST.get('userid'))[0]
                group.subrequests.remove(user)

                if user.profile.notificationsettings.accepted_to_group_notifications:
                    notifications.create_notification_accepted_to_group(group, user, 'request_rejected')

                return HttpResponse('rejected')
            elif request.POST.get('type') == 'allowarticles':
                group.allowarticles = request.POST.get('data')
                group.save()
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'deletegroupimage':
                try:
                    os.remove('media/' + group.image.name)
                except:
                    pass
                group.image = False
                group.save()
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'uploadimage':
                file = request.FILES['groupimage']
                if file.content_type != 'image/png' and file.content_type != 'image/jpeg' and file.size <= 5000000:
                    return HttpResponseRedirect('/groups/' + str(slug) + '/edit/')
                try:
                    os.remove('media/' + group.image.name)
                except:
                    pass
                group.image = request.FILES['groupimage']

                new_article = CommunityArticle(group=group, text='В сообществе обновлена фотография')
                new_article.save()

                new_file = BasicArticleFile(files_list=new_article.files, name=request.FILES['groupimage'].name,
                                            file=request.FILES['groupimage'])
                new_file.save()

                group.save()
                return HttpResponseRedirect('/groups/' + str(slug) + '/edit/')
            elif request.POST.get('type') == 'checkslug':
                newslug = request.POST.get('slug').lower()

                if newslug.replace(' ', '') == '':
                    group.slug = str(group.id)
                    group.save()
                    return HttpResponse('/groups/' + group.slug + '/edit/')

                if newslug.replace(' ', '') != newslug:
                    return HttpResponse('spaces')

                if newslug == '':
                    return HttpResponse('empty')

                pattern = re.compile("^[a-zA-Z]+$")
                if not pattern.match(newslug):
                    return HttpResponse('wrong')

                if newslug == group.slug:
                    HttpResponse('/groups/' + group.slug + '/edit/')
                elif newslug == 'create' or Group.objects.filter(slug=newslug).exists():
                    return HttpResponse('hasAlready')

                if len(newslug) > 20:
                    return HttpResponse('error')

                try:
                    group.slug = newslug
                    group.save()
                except:
                    return HttpResponse('error')

                return HttpResponse('/groups/' + group.slug + '/edit/')
            elif request.POST.get('type') == 'banuser':
                user = User.objects.get(id=request.POST.get('userid'))
                try:
                    group.subscribers.remove(user)
                except:
                    pass
                try:
                    group.editors.remove(user)
                except:
                    pass
                group.banned.add(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'banned')
                return HttpResponse(user.username)
            elif request.POST.get('type') == 'cancelban':
                user = User.objects.filter(id=request.POST.get('userid'))[0]
                group.banned.remove(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'restored')
                return HttpResponse('restored')
            elif request.POST.get('type') == 'toeditor':
                user = User.objects.filter(id=request.POST.get('data'))[0]
                group.subscribers.remove(user)
                group.editors.add(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'promoted')
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'touser':
                user = User.objects.filter(id=request.POST.get('data'))[0]
                group.editors.remove(user)
                group.subscribers.add(user)
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'demoted')
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'toadmin':
                user = User.objects.filter(id=request.POST.get('data'))[0]
                group.editors.remove(user)
                group.admin = user
                group.save()
                # if user.profile.notificationsettings.accepted_to_group_notifications:
                #	notifications.create_notification_accepted_to_group(group, user, 'to_admin')
                return HttpResponse('Ok')
            elif request.POST.get('type') == 'editname':
                if request.POST.get('data').strip() != '' and len(request.POST.get('data')) <= 48:
                    group.groupname = request.POST.get('data')
                group.save()
                return HttpResponse('/groups/' + group.slug + '/edit/')
            elif request.POST.get('type') == 'editdescription':
                if len(request.POST.get('data')) <= 200:
                    group.description = request.POST.get('data')
                group.save()
                return HttpResponse('/groups/' + group.slug + '/edit/')
        elif request.POST.get('type') == 'sendsubrequest':
            group.send_subrequest(request.user)
            return HttpResponse('sent')
        elif request.POST.get('type') == 'cancelsubrequest':
            group.cancel_subrequest(request.user)
            return HttpResponse('cancelled')
            '''
			if request.user in group.subrequests.all():
				group.subrequests.remove(request.user)
				return HttpResponse('cancelled')
			else:
				group.subrequests.add(request.user)
				return HttpResponse('sent')
			'''
    return HttpResponse('error')

def editgroup(request, groupslug):
    if Group.objects.filter(slug=groupslug).exists():
        group = Group.objects.get(slug=groupslug)
        if group.admin != request.user:
            return HttpResponseRedirect('/groups/' + group.slug + '/')
        else:
            group = {'group': group}
            return render(request, 'communities/edit.html', group)
    else:
        return render(request, 'communities/nogroup.html')

'''
class EditGroup(View):
	def get(self, request, groupslug):
		group = Group.objects.filter(slug=groupslug)[0]
		if group.admin != request.user:
			HttpResponseRedirect('../')
		form = GroupEditForm()
		context = {'form': form, 'group': group}
		return render(request, 'communities/edit.html', context)

	def post(self, request, groupslug):
		# return HttpResponse(request.POST.get('slug'))
		href = '../../' + request.POST.get('slug') + '/edit'

		group = Group.objects.filter(slug=groupslug)[0]
		if group.admin != request.user:
			HttpResponseRedirect('../')
		bound_form = GroupEditForm(request.POST)
		form = GroupEditForm()

		if bound_form.is_valid():
			# return HttpResponse(request.POST.get('slug'))
			group = bound_form.save(group)
			context = {'form': form, 'group': group}
			href = '../../' + request.POST.get('slug') + '/edit'
			# return HttpResponseRedirect(href)
			# return render(request, 'communities/edit.html', context)
		context = {'form': bound_form, 'group': group}
		# return HttpResponse(request.POST.get('slug'))
		# return render(request, 'communities/edit.html', context)
		return HttpResponseRedirect(href)
'''


import bpy

material_name = "Ice Shelf Sharp"

for o in bpy.data.objects:
    if o.name.startswith("MetaBallObj"):
      print(o.name)
      o.select_set(True)
      if material_name in o.data.materials:
        continue

      material = bpy.data.materials.get(material_name)

      o.data.materials.append(material)


# -*- coding: utf-8 -*-
# time: 2023-7-21 14:12
# file: run_demo.py
# author: 罗永贵
import sys
import test
from PyQt5.QtWidgets import QApplication,QMainWindow

if __name__ == '__main__':
    # 实例化，传参
    app = QApplication(sys.argv)
    # 创建对象
    mainWindow = QMainWindow()
    # 创建ui，引用demo文件中的Ui_MainWindow类
    ui = test.Ui_MainWindow()
    # 调用Ui_MainWindow类的setupUi，创建初始组件
    ui.setupUi(mainWindow)
    # 创建窗口
    mainWindow.show()
    # 进入程序的主循环，并通过exit函数确保主循环安全结束(该释放资源的一定要释放)
    sys.exit(app.exec_())


#!/usr/bin/env python3
import time

import rospy
from scipy.spatial.transform import Rotation
import numpy as np
from math import pi

from RobotController import gait_generator
from InverseKinematics import leg_IK
from RoboticsUtilities import trajectory, Transformations
from std_msgs.msg import Float64
from gazebo_msgs.msg import ModelStates

def get_robot_states(msg):
    global robot_state
    global robot_velocity_control_x
    global robot_velocity_control_y
    robot_state = msg.pose[msg.name.index("notspot_gazebo")]
    robot_velocity_control_x.update_state(robot_state.position.x)
    robot_velocity_control_y.update_state(robot_state.position.y)

USE_IMU = True
RATE = 240

rospy.init_node("Robot_Controller")

# Robot geometry
body = [0.1908, 0.080]
legs = [0.0, 0.04, 0.100, 0.094333]

# global variable updated by get_robot_states
robot_state = None

# way points, keeping staring and ending velocity 0
waypoints = [[0, 0], [1, -1]]
velocity = [[0, 0], [0, 0]]
yaw = [0, 0]
trajectory_time = 20 # in secs

# initialize trajectory
robot_trajectory_x_axis = trajectory.Trajectory(waypoints[0][0], waypoints[1][0], velocity[0][0], velocity[1][0], 0, trajectory_time)
robot_trajectory_y_axis = trajectory.Trajectory(waypoints[0][1], waypoints[1][1], velocity[0][1], velocity[1][1], 0, trajectory_time)
robot_trajectory_yaw = trajectory.Trajectory(yaw[0], yaw[1], 0, 0, 0, trajectory_time)

#initialize controller
kp = 0.1
robot_velocity_control_x = trajectory.trajectory_controller(kp*3)
# kp needs to be negative for Y
# cause body frame rotated 180 about x-axis
robot_velocity_control_y = trajectory.trajectory_controller(-kp*3)
robot_yaw_control = trajectory.trajectory_controller(0.00)

# initialize robot gait generator
beta = 0.75
t_cycle = 0.4
robot_height = 0.15
robot_leg = leg_IK.robot_leg(legs)
robot_gait = gait_generator.gait_generator(beta, t_cycle, robot_height, body, robot_leg)

command_topics = ["/notspot_controller/FR1_joint/command",
                  "/notspot_controller/FR2_joint/command",
                  "/notspot_controller/FR3_joint/command",
                  "/notspot_controller/FL1_joint/command",
                  "/notspot_controller/FL2_joint/command",
                  "/notspot_controller/FL3_joint/command",
                  "/notspot_controller/RR1_joint/command",
                  "/notspot_controller/RR2_joint/command",
                  "/notspot_controller/RR3_joint/command",
                  "/notspot_controller/RL1_joint/command",
                  "/notspot_controller/RL2_joint/command",
                  "/notspot_controller/RL3_joint/command"]

publishers = []
for i in range(len(command_topics)):
    publishers.append(rospy.Publisher(command_topics[i], Float64, queue_size = 0))

rospy.Subscriber("/gazebo/model_states", ModelStates, get_robot_states)

rate = rospy.Rate(RATE)

del body
del legs
del command_topics
del USE_IMU
del RATE

# current simulation time as trajectory start time
start_time = rospy.get_time()
# time allowed to get gazebo setup
settling_time = 3

while not rospy.is_shutdown():

    # get current_trajectory time
    current_trajectory_time = rospy.get_time() - start_time - settling_time
    print("current time", current_trajectory_time)
    if (not robot_velocity_control_x.current_pose == None and
            not robot_velocity_control_y.current_pose == None )\
            and current_trajectory_time > 0:

        print("current_body_pos", robot_state.position.x, robot_state.position.y)
        robot_orientation = robot_state.orientation
        rot = Rotation.from_quat([robot_orientation.x, robot_orientation.y, robot_orientation.z, robot_orientation.w])
        robot_yaw_control.update_state(rot.as_euler("xyz", degrees=False)[2])

        if current_trajectory_time <= trajectory_time:
            # update target
            robot_velocity_control_x.update_target(robot_trajectory_x_axis.at_time(current_trajectory_time)[0])
            robot_velocity_control_y.update_target(robot_trajectory_y_axis.at_time(current_trajectory_time)[0])
            robot_yaw_control.update_target(robot_trajectory_yaw.at_time(current_trajectory_time)[0])

        # calculate velocity command
        vel_command_x = robot_velocity_control_x.get_velocity_command()
        vel_command_y = robot_velocity_control_y.get_velocity_command()
        yaw_rate_command = robot_yaw_control.get_velocity_command()

        velocity_command = np.asarray(np.matmul(Transformations.rotz(rot.as_euler("xyz", degrees=False)[2]), np.asarray([[vel_command_x], [vel_command_y], [0]])))
        velocity_command = velocity_command.astype(float)
        print(rot.as_euler("xyz", degrees=False))
        velocity_command = np.asarray([[vel_command_x], [vel_command_y], [0]])

        if current_trajectory_time > trajectory_time and \
                robot_velocity_control_x.current_error() < 0.05 and \
                robot_velocity_control_y.current_error() < 0.05 and \
                robot_yaw_control.current_error() < 0.05:
            velocity_command = [0, 0, 0]
            yaw_rate_command = 0
        print("velocity command", velocity_command)
        # velocity_command = np.asarray([[0.3], [0], [0]])
        yaw_rate_command = 0.0
        joint_angles = robot_gait.run(current_trajectory_time, velocity_command, yaw_rate_command)
    else:
        print("stance")
        joint_angles = robot_gait.run(-1, np.asarray([0, 0]), 0)

    print("angles", joint_angles)

    for i in range(len(joint_angles)):
        publishers[i].publish(joint_angles[i])

    rate.sleep()


import PySimpleGUI as sg
import os
import copy
import random
from pattern.es import tag

PATH = '.'

BLANK = 0
A = 1
B = 2
C = 3
D = 4
E = 5
F = 6
G = 7
H = 8
I = 9
J = 10
K = 11
L = 12
LL = 13
M = 14
N = 15
Ñ = 16
O = 17
P = 18
Q = 19
R = 20
RR = 21
S = 22
T = 23
U = 24
V = 25
W = 26
X = 27
Y = 28
Z = 29

# Orientacion de las palabras
ORIENTATION_RIGHT = 1
ORIENTATION_DOWN = 2
ORIENTATION_ERROR = -1
ORIENTATION_NONE = 0

# Tablero en blanco de 10x10
initial_tablero = [[BLANK, ] * 10, [BLANK, ] * 10, [BLANK, ] * 10, [BLANK, ] * 10, [BLANK, ] * 10,
                 [BLANK, ] * 10, [BLANK, ] * 10, [BLANK, ] * 10, [BLANK, ] * 10, [BLANK, ] * 10]

# Metadata de las letras
blank = {'letra': '', 'imagen': os.path.join(PATH, 'blank.png')}
a = {'letra': 'A', 'imagen': os.path.join(PATH, 'a.png')}
b = {'letra': 'B', 'imagen': os.path.join(PATH, 'b.png')}
c = {'letra': 'C', 'imagen': os.path.join(PATH, 'c.png')}
d = {'letra': 'D', 'imagen': os.path.join(PATH, 'd.png')}
e = {'letra': 'E', 'imagen': os.path.join(PATH, 'e.png')}
f = {'letra': 'F', 'imagen': os.path.join(PATH, 'f.png')}
g = {'letra': 'G', 'imagen': os.path.join(PATH, 'g.png')}
h = {'letra': 'H', 'imagen': os.path.join(PATH, 'h.png')}
i = {'letra': 'I', 'imagen': os.path.join(PATH, 'i.png')}
j = {'letra': 'J', 'imagen': os.path.join(PATH, 'j.png')}
k = {'letra': 'K', 'imagen': os.path.join(PATH, 'k.png')}
l = {'letra': 'L', 'imagen': os.path.join(PATH, 'l.png')}
ll = {'letra': 'LL', 'imagen': os.path.join(PATH, 'll.png')}
m = {'letra': 'M', 'imagen': os.path.join(PATH, 'm.png')}
n = {'letra': 'N', 'imagen': os.path.join(PATH, 'n.png')}
ñ = {'letra': 'Ñ', 'imagen': os.path.join(PATH, 'ñ.png')}
o = {'letra': 'O', 'imagen': os.path.join(PATH, 'o.png')}
p = {'letra': 'P', 'imagen': os.path.join(PATH, 'p.png')}
q = {'letra': 'Q', 'imagen': os.path.join(PATH, 'q.png')}
r = {'letra': 'R', 'imagen': os.path.join(PATH, 'r.png')}
rr = {'letra': 'RR', 'imagen': os.path.join(PATH, 'rr.png')}
s = {'letra': 'S', 'imagen': os.path.join(PATH, 's.png')}
t = {'letra': 'T', 'imagen': os.path.join(PATH, 't.png')}
u = {'letra': 'U', 'imagen': os.path.join(PATH, 'u.png')}
v = {'letra': 'V', 'imagen': os.path.join(PATH, 'v.png')}
w = {'letra': 'W', 'imagen': os.path.join(PATH, 'w.png')}
x = {'letra': 'X', 'imagen': os.path.join(PATH, 'x.png')}
y = {'letra': 'Y', 'imagen': os.path.join(PATH, 'y.png')}
z = {'letra': 'Z', 'imagen': os.path.join(PATH, 'z.png')}

images = {BLANK: blank, A: a, B: b, C: c, D: d, E: e, F: f,
          G: g, H: h, I: i, J: j, K: k, L: l, LL: ll, M: m,
          N: n, Ñ: ñ, O: o, P: p, Q: q, R: r, RR: rr, S: s,
          T: t, U: u, V: v, W: w, X: x, Y: y, Z: z}

initial_atril = []

images_keys = list(images.keys())
images_keys.remove(0)
for i in range(0,7):
    initial_atril.append(random.choice(images_keys))

def render_square(image, key):
    return sg.RButton('', image_filename=image, size=(1, 1), pad=(0, 0), key=key)

'''
Refresca el atril en base a un movimiento. Esto se realiza haciendo un update en
los botones dependiento los nuevos valores del "board"
En este caso, si encuentra valores en 0,
actualizaría la imagen del boton con un "blank"
'''
def redraw_atril(window, board):
    for i in range(7):
        piece_image = images[board[i]]['imagen']
        elem = window.FindElement(key=i)
        elem.Update(image_filename=piece_image)

'''
Refresca el tablero en base a un movimiento. Esto se realiza haciendo un update en
los botones dependiento los nuevos valores del "board"
'''
def redraw_tablero(window, board):
    for i in range(10):
        for j in range(10):
            piece_image = images[board[i][j]]['imagen']
            elem = window.FindElement(key=(i, j))
            elem.Update(image_filename=piece_image)

# Define qué tipo de movimiento se debera seguir en la agregacion de letras al tablero
def get_orientation(movimiento_actual, movimiento_anterior):
    orientation = ORIENTATION_ERROR
    # eje X sumo 1 y la Y quedo igual, se va pa la derecha
    if movimiento_anterior[0] == movimiento_actual[0] and movimiento_anterior[1]+1 == movimiento_actual[1]:
        orientation = ORIENTATION_RIGHT
    if movimiento_anterior[0]+1 == movimiento_actual[0] and movimiento_anterior[1] == movimiento_actual[1]:
        orientation = ORIENTATION_DOWN
    return orientation

'''
Define si el movimiento es el correcto dependiendo de qué sentido se haya
fijado al principio de agregada la segunda letra
'''
def correct_movement(movimiento_actual, movimiento_anterior, orientation):
    if get_orientation(movimiento_actual, movimiento_anterior) == orientation:
        return True
    else:
        return False

def Play():
    board_tablero = copy.deepcopy(initial_tablero)
    board_atril = copy.deepcopy(initial_atril)

    # Genero una matriz de 10x10 de tipo RButton con las imagenes en blanco
    tablero = []
    for i in range(10):
        row = []
        for j in range(10):
            piece_image = images[BLANK]
            row.append(render_square(piece_image['imagen'], key=(i, j)))
        tablero.append(row)

    # Genero un array de 7 elementos de tipo RButton con las imágenes de las letras aleatorias
    atril = []
    for i in range(7):
        row = []
        piece_image = images[board_atril[i]]
        row.append(render_square(piece_image['imagen'], key=i))
        atril.append(row)

    board_tab = [[sg.Button('CHECK')], [sg.Column(atril), sg.Column(tablero)]]
    window = sg.Window('ScrabbleAr', default_button_element_size=(12, 1), auto_size_buttons=False).Layout(board_tab)

    word = ''
    move_from = move_to = -1
    first_movement = True
    orientation = ORIENTATION_NONE
    # Temporal para no permitir el click en el atril de los "blancos"
    keys_chosen = []

    while True:
        while True:
            button, value = window.Read()
            if button == 'CHECK':
                if len(word) >= 2 and len(word) <=7:
                    wordType = tag(word)[0][1]
                    if wordType == 'VB':
                        sg.Popup('La palabra existe y es un verbo: ', word)
                    else:
                        sg.Popup('La palabra no es un verbo: ', word)
                    # Si esta bien, calcular puntos y luego cambia el turno
                else:
                    sg.Popup('Atención: ', 'La palabra formada no cumple con los mínimos ni máximos')
            if button in (None, 'Exit'):
                exit()
            # Click origen
            if type(button) is int:
                if button in keys_chosen:
                    sg.Popup('Atención: ', 'Click incorrecto, este elemento esta vacio')
                    break
                if move_from != -1:
                    sg.Popup('Atención: ', 'Click incorrecto, debe insistir en el tablero')
                    break
                move_from = button
                # Busco que numero de letra esta en la posicion clickeada
                piece = board_atril[move_from]
                letter_choosen = images[board_atril[move_from]]['letra']
                keys_chosen.append(button)
            # click destino
            if type(button) is tuple:
                if move_from == -1:
                    sg.Popup('Atención: ', 'Click incorrecto, debe insistir en el atril')
                    break
                move_to = button
                row, col = move_to

                if first_movement == False:
                    if orientation == ORIENTATION_NONE:
                        orientation = get_orientation(move_to, move_to_anterior)
                    if orientation == ORIENTATION_ERROR:
                        sg.Popup('Atención: ', 'No se pudo calcular el sentido')
                        orientation = ORIENTATION_NONE
                        break
                    if not correct_movement(move_to, move_to_anterior, orientation):
                        sg.Popup('Atención: ', 'Movimiento incorrecto')
                        break

                # La posicion de la letra que se fue queda en 0
                board_atril[move_from] = BLANK
                # El tablero queda con el numero "nuevo"
                board_tablero[row][col] = piece
                # Luego tengo que "redibujar" ambos tableros
                redraw_atril(window, board_atril)
                redraw_tablero(window, board_tablero)
                word = word + letter_choosen

                move_to_anterior = move_to
                move_from = move_to = -1
                first_movement = False
                break

Play()


import csv
import glob
import math
from pathlib import Path
import argparse

import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
from graph_tool.all import *
from extract_largest_component import write_largest_component

class ParameterExtraction:
    def __init__(self, graphs_path: str, export_file: str) -> None:
        super().__init__()
        self.graphs_path = Path(graphs_path)
        self.export_file = export_file

    def _iterate_edge_lists(self):
        if self.graphs_path.is_dir():
            for filename in glob.iglob(str(self.graphs_path) + '/**/*.edges', recursive=True):
                yield filename

    def _get_num_edges(self, graph: Graph):
        return graph.num_edges()

    def _get_num_vertices(self, graph: Graph):
        return graph.num_vertices()

    def _get_global_clustering_coefficient(self, graph: Graph):
        return global_clustering(graph)[0]

    def _get_pseudo_diameter(self, graph: Graph):
        return pseudo_diameter(graph)[0]

    def _get_assortativity(self, graph: Graph):
        return assortativity(graph, 'total')[0]

    def _get_scalar_assortativity(self, graph: Graph):
        return scalar_assortativity(graph, 'total')[0]

    def _get_density(self, graph: Graph):
        return (2 * graph.num_edges()) / (graph.num_vertices() * (graph.num_vertices() - 1))

    def _get_average_shortest_path(self, graph: nx.Graph):
        return nx.average_shortest_path_length(graph)

    # Degree distribution log normal?
    def get_degree_sequence(self, graph: nx.Graph):
        degree_sequence = sorted(nx.degree(G).values(), reverse=True)

    """
    DEGREE
    """

    def _get_avg_degree(self, graph: Graph):
        return 2 * (graph.num_edges() / graph.num_vertices())

    def _get_min_deg(self, graph: Graph):
        min_degree = math.inf
        for v in graph.vertices():
            degree = v.out_degree()
            if (degree < min_degree):
                min_degree = degree
        return min_degree

    def _get_max_deg(self, graph: Graph):
        max_degree = -1
        for v in graph.vertices():
            degree = v.out_degree()
            if (degree > max_degree):
                max_degree = degree
        return max_degree

    """
    VERTEX BETWEENNESS
    """

    def _get_max_vertex_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return max(vertex_betweenness.get_array())

    def _get_min_vertex_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return min(vertex_betweenness.get_array())

    def _get_mean_vertex_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return np.mean(vertex_betweenness.get_array())

    def _get_std_vertex_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return np.std(vertex_betweenness.get_array())

    """
    EDGE BETWEENNESS
    """

    def _get_max_edge_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return max(edge_betweenness.get_array())

    def _get_min_edge_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return min(edge_betweenness.get_array())

    def _get_mean_edge_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return np.mean(edge_betweenness.get_array())

    def _get_std_edge_betweenness(self, graph: Graph):
        vertex_betweenness, edge_betweenness = betweenness(graph)
        return np.std(edge_betweenness.get_array())

    """
    CLIQUE
    """

    def _get_clique_number(self, graph: nx.Graph):
        return nx.graph_clique_number(graph)

    def _get_number_of_cliques(self, graph: nx.Graph):
        return nx.graph_number_of_cliques(graph)

    """
    PLOT
    """

    def plot_data(self, csv_file: str):
        p = Path(csv_file)
        if p.exists():
            with open(str(p)) as csv_file:
                reader = csv.reader(csv_file, delimiter=',')
                next(reader)
                ccd = list()
                avd = list()
                for row in reader:
                    ccd.append(row[2])
                    avd.append(row[3])
            plt.scatter(ccd, avd)
            plt.show()

    def run(self, do_non_np=True):
        columns = [self._get_num_edges,
                   self._get_num_vertices,
                   self._get_global_clustering_coefficient,
                   self._get_avg_degree,
                   self._get_pseudo_diameter,
                   self._get_assortativity,
                   self._get_scalar_assortativity,
                   self._get_density,
                   self._get_min_deg,
                   self._get_max_deg,
                   self._get_max_vertex_betweenness,
                   self._get_min_vertex_betweenness,
                   self._get_mean_vertex_betweenness,
                   self._get_std_vertex_betweenness,
                   self._get_max_edge_betweenness,
                   self._get_min_edge_betweenness,
                   self._get_mean_edge_betweenness,
                   self._get_std_edge_betweenness]

        columns_nx = [
            self._get_average_shortest_path
        ]

        columns_nx_non_np = [
            self._get_clique_number,
            self._get_number_of_cliques,
        ]

        f = csv.writer(open(self.export_file, 'w'))
        f.writerow(map(lambda x: x.__name__, columns + columns_nx))
        for file in self._iterate_edge_lists():
            # Get data_real properties
            with open(file, 'r') as c:
                dialect = csv.Sniffer().sniff(c.read(1024))
                csv_options = {'delimiter': dialect.delimiter, 'quotechar': dialect.quotechar}

            # Create graph and write specified columns to new csv file
            g = load_graph_from_csv(file, directed=False, csv_options=csv_options)
            gx = nx.read_edgelist(file, delimiter=csv_options['delimiter'], create_using=nx.Graph(), nodetype=int)
            if nx.is_connected(gx):
                print('Did graph {}'.format(file))
                column_values = [c(g) for c in columns]
                column_values += [c(gx) for c in columns_nx]
                if do_non_np:
                    column_values += [c(gx) for c in columns_nx_non_np]
                f.writerow(column_values)
            else:
                print('Graph was not connected, skipping ... {}'.format(file))

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Extraction of diversity of graph properties')
    parser.add_argument('data_input', metavar='I', type=str,
                        help='Input data_real directory')
    parser.add_argument('data_output', metavar='O', type=str,
                        help='Output CSV file')
    parser.add_argument('-np',
                        help='Do graph properties algorithm that are not in NP', action='store_true')
    parser.add_argument('-fix',
                        help='If not connected, extract largest component', action='store_true')
    args = parser.parse_args()

    if args.fix:
        print('Fixing graphs...')
        write_largest_component(args.data_input, args.data_output + '_fixed')
        p = ParameterExtraction(args.data_output + '_fixed', args.data_output)
    else:
        print('Do not fix graphs')
        p = ParameterExtraction(args.data_input, args.data_output)

    if args.np:
        print('NP true')
        p.run(do_non_np=True)
    else:
        print('NP false')
        p.run(do_non_np=False)