from sqlalchemy import Column, Integer, JSON
from sqlalchemy.orm import declarative_base
from py2neo.ogm import GraphObject, Property, RelatedTo, RelatedFrom
from src.driver_singleton import Driver
from typing import Dict
from substrateinterface.utils import ss58
from py2neo import Subgraph, Node, Relationship
import time
import logging

Base = declarative_base()
"""
Block
"""

treasury_address = "13UVJyLnbVp9RBZYFwFGyDvVd1y27Tt8tkntv6Q7JVPhFsTB"

def decorator_factory(decorator):
    """
    Meta decorator
    Is used to decorate other decorators such that they can have passed an argument
    e.g.
    @decorator(argument)
    would not work if decorator isn't decorated with this decorator
    It is used mostly for the event_error_handling such that we can decorate a function with an exception and
    log it if something bad happened
    """

    def layer(error, *args, **kwargs):
        def repl(f, *args, **kwargs):
            return decorator(f, error, *args, **kwargs)

        return repl

    return layer

@decorator_factory
def profiler(function, cls_name, *args, **kwargs):
    def wrapper(*args, **kwargs):
        logging.debug("Transaction started")
        start = time.perf_counter()
        result = function(*args, **kwargs)
        logging.debug(f"Transaction finished {cls_name} {function.__name__} took {time.perf_counter() - start} s")
        return result

    return wrapper

class RawData(Base):
    __tablename__ = "raw_data"
    block_number = Column(Integer, primary_key=True)
    data = Column(JSON)

    def __repr__(self):
        return f"Block {self.block_number}"

class Utils:
    schema = dict()
    @staticmethod
    def convert_public_key_to_polkadot_address(address):
        try:
            if address[0] == '1':
                return address
            return ss58.ss58_encode(ss58_format=0, address=address)
        except ValueError:
            address = address.replace("0x", "")
            if address[0] == '1':
                return address

    @staticmethod
    def extract_event_attributes(event, treasury):
        if treasury:
            try:
                if event['event_id'] == 'Transfer':
                    return 0
                result = event['attributes']
                if isinstance(result, int):
                    return result
                else:
                    result1 = result[0]['value']
                    if isinstance(result1, int):
                        return result1
                    else:
                        return result[1]['value']
            except KeyError:
                return 0
        else:
            result = event['attributes'][1]
            if isinstance(result, Dict):
                return result['value']
            else:
                return result

    @staticmethod
    def merge_subgraph(subgraph, *other):
        for element in other:
            subgraph = subgraph | element
        return subgraph

    @staticmethod
    def merge(res_list):
        new_list = []
        for i in range(int(len(res_list))):
            if len(res_list) == 1:
                new_list.append(res_list[0])
                return new_list
            elif len(res_list) == 0:
                return new_list
            else:
                try:
                    x = res_list.pop()
                except IndexError:
                    x = Subgraph()
                try:
                    y = res_list.pop()
                except IndexError:
                    y = Subgraph()
            new_list.append(Utils.merge_subgraph(x, y))
        return new_list

    def set_schema(self, schema):
        self.schema = schema

    def restructure_block(self, data, schema):
        self.set_schema(schema)

        return

class Block(GraphObject):
    __primarykey__ = "block_number"

    block_number = Property("block_number")
    hash = Property("hash")
    timestamp = Property("timestamp")

    has_author = RelatedTo("Validator")
    has_transaction = RelatedTo("Transaction")
    previous_block = RelatedTo("Block")
    has_aggregator = RelatedTo("Aggregator")

    @staticmethod
    def create(data, timestamp):
        block = Node("Block",
                     block_number=data["number"],
                     hash=data["hash"],
                     timestamp=timestamp
                     )
        return block

    @staticmethod
    def save(block: "Block"):
        Driver().get_driver().save(block)

class Transaction(GraphObject):
    extrinsic_hash = Property()
    amount_transferred = Property()
    is_successful = Property()

    has_extrinsic_function = RelatedTo("ExtrinsicFunction")
    has_event_function = RelatedTo("EventFunction")
    reward_validator = RelatedTo("Balance")
    reward_treasury = RelatedTo("Balance")
    sender_account = RelatedTo("Account")
    is_proxy = RelatedTo("Transaction")
    is_batch = RelatedTo("Transaction")

    @staticmethod
    def finish_transaction(block, transaction):
        block_transaction_relationship = Relationship(block, "HAS_TRANSACTION", transaction)
        return block_transaction_relationship

    @staticmethod
    def save(transaction: "Transaction"):
        Driver().get_driver().save(transaction)

    @staticmethod
    def create(block,
               transaction_data,
               event_data,
               proxy_transaction=None,
               batch_from_account=None,
               batch_transaction=None,
               subgraph=None
               ):

        call_function = transaction_data["call"]["call_function"]
        call_module = transaction_data["call"]["call_module"]
        print(call_module, call_function)
        transaction = Node("Transaction",
                           extrinsic_hash=transaction_data["extrinsic_hash"])
        subgraph = Utils.merge_subgraph(subgraph, transaction)

        extrinsic_function = ExtrinsicFunction.get(call_function, subgraph)
        if extrinsic_function is None:
            extrinsic_function = ExtrinsicFunction.create(call_function)
        extrinsic_module = ExtrinsicModule.get(call_module, subgraph)
        if extrinsic_module is None:
            extrinsic_module = ExtrinsicModule.create(call_module)
        subgraph = Utils.merge_subgraph(subgraph, extrinsic_function, extrinsic_module)
        extrinsicfunction_extrinsicmodule_relationship = Relationship(extrinsic_function,
                                                                      "PART_OF_MODULE",
                                                                      extrinsic_module)
        transaction_extrinsicfunction_relationship = Relationship(transaction,
                                                                  "HAS_EXTRINSICFUNCTION",
                                                                  extrinsic_function)
        subgraph = Utils.merge_subgraph(subgraph, extrinsicfunction_extrinsicmodule_relationship,
                                        transaction_extrinsicfunction_relationship)

        #Transaction.handle_extrinsics_without_sender(call_module, call_function, subgraph, block, transaction)
        if call_module in ['FinalityTracker', 'Parachains', 'ParaInherent', 'ImOnline',
                           'ElectionProviderMultiPhase', 'Timestamp', 'Council', 'Claims', 'Babe']:
            """
            Simply return transaction since no balance shift takes place in these modules.
            """
            block_transaction_relationship = Relationship(block, "HAS_TRANSACTION", transaction)
            return Utils.merge_subgraph(subgraph, block_transaction_relationship)

        if call_module in ["Staking"] and call_function in ['submit_election_solution_unsigned']:
            block_transaction_relationship = Relationship(block, "HAS_TRANSACTION", transaction)
            return Utils.merge_subgraph(subgraph, block_transaction_relationship)

        if call_module in ["Grandpa"] and call_function in ['report_equivocation_unsigned']:
            block_transaction_relationship = Relationship(block, "HAS_TRANSACTION", transaction)
            return Utils.merge_subgraph(subgraph, block_transaction_relationship)
        """
        For batch calls we handle the individual extrinsics as seperate transactions. The fee paid is split up evenly
        between the individual calls.
        """
        if call_module == 'Utility' and call_function in ['batch', 'as_derivative', 'batch_all', 'force_batch']:
            from_account_address = transaction_data['address']
            from_account = Account.get(subgraph, from_account_address)
            if from_account is None:
                from_account = Account.create(from_account_address)

            event_start = 0
            event_end = len(event_data)
            if call_function == 'as_derivative':
                transaction_structure = dict()
                transaction_structure['extrinsic_hash'] = transaction_data['extrinsic_hash']
                transaction_structure['address'] = transaction_data['address']
                transaction_structure['call'] = transaction_data['call']['call_args'][1]['value']
                return Transaction.create(block=block,
                                          transaction_data=transaction_structure,
                                          event_data=event_data,
                                          batch_from_account=from_account,
                                          batch_transaction=transaction,
                                          subgraph=subgraph)

            transaction_data_list = transaction_data['call']['call_args'][0]['value']
            for j in range(len(transaction_data_list)):
                sub_extrinsic_data = transaction_data_list[j]

                """
                We can't assign events to extrinsics (only after spec 9050 that introduced the 'ItemCompleted' event).
                This is most of the time not a great problem since we get our information from the extrinsic data.
                There is however the case of Staking(PayoutStakers). In this extrinsic the events carry the information
                of who got how much. Fortunately we know that the validator gets payed out first. We can therefore check if
                the next extrinsic is also a payout extrinsic, then iterate through the events until we find the payout event
                for the validator in the next extrinsic and use all events until that one to create the rewards with the right era.
                """
                payout_staker_flag = False
                if (
                        j + 1 < len(transaction_data_list) and
                        transaction_data_list[j]["call_module"] == "Staking" and
                        transaction_data_list[j]["call_function"] == "payout_stakers" and
                        transaction_data_list[j + 1]["call_module"] == "Staking" and
                        transaction_data_list[j + 1]["call_function"] == "payout_stakers"
                ):
                    next_validator = transaction_data_list[j + 1]["call_args"][0]["value"]
                    payout_staker_flag = True

                for i in range(event_start, event_end):
                    if event_data[i]['module_id'] == "Utility":
                        if event_data[i]['event_id'] == "ItemCompleted":
                            was_successful = True
                            break
                        elif event_data[i]['event_id'] == "ItemFailed":
                            was_successful = False
                            break
                        elif event_data[i]['event_id'] == "BatchInterrupted":
                            was_successful = False
                            break
                        elif event_data[i]['event_id'] == "BatchCompleted":
                            was_successful = True
                            break
                    elif event_data[i]['event_id'] == "ProxyExecuted" and event_data[i]['module_id'] == 'Proxy':
                        was_successful = True
                        break
                        # True Horror, an encapsulation of type Sudo->Batch gives no indication as to which events
                        # belong to which item of the batch. we have to handle those by hand.
                    elif block['block_number'] in [240853, 240984, 372203, 500796]:
                        was_successful = True
                        i = i + 2  # take 3 events
                        break
                    elif payout_staker_flag and event_data[i]['module_id'] == "Staking" and\
                            event_data[i]['event_id'] == "Reward":
                        reward_account = Utils.convert_public_key_to_polkadot_address(
                            event_data[i]['attributes'][0]['value'])
                        if next_validator == reward_account and i != event_start:
                            i = i - 1
                            was_successful = True
                            break

                sub_events = event_data[event_start:i + 1]
                event_start = i + 1
                transaction_structure = dict()
                transaction_structure['extrinsic_hash'] = transaction_data['extrinsic_hash']
                transaction_structure['address'] = transaction_data['address']
                transaction_structure['call'] = sub_extrinsic_data
                subgraph = Transaction.create(block=block,
                                              transaction_data=transaction_structure,
                                              event_data=sub_events,
                                              batch_from_account=from_account,
                                              batch_transaction=transaction,
                                              subgraph=subgraph)
        """
        Get account which triggered the transaction and set initial values of variables required for further processing
        """
        from_account_address = Utils.convert_public_key_to_polkadot_address(transaction_data['address'])
        if batch_from_account is None:
            from_account = Account.get(subgraph, from_account_address)
            if from_account is None:
                from_account = Account.create(from_account_address)
        else:
            from_account = batch_from_account
            transaction_batch_relationship = Relationship(transaction, "IS_BATCH", batch_transaction)
            subgraph = Utils.merge_subgraph(subgraph, transaction_batch_relationship)
        subgraph = Utils.merge_subgraph(subgraph, from_account)

        transaction_senderaccount_relationship = Relationship(transaction, "FROM_ACCOUNT", from_account)
        subgraph = Utils.merge_subgraph(subgraph, transaction_senderaccount_relationship)

        """
        Handle Extrinsics which failed. They are required to pay fees regardless of outcome.
        """
        if len(event_data):
            if event_data[-1]["event_id"] == "ExtrinsicFailed":
                transaction['is_succesful'] = False
                block_transaction_relationship = Relationship(block, "HAS_TRANSACTION", transaction)
                subgraph = Utils.merge_subgraph(subgraph, transaction)
                return Utils.merge_subgraph(subgraph, block_transaction_relationship)
        transaction.is_successful = True
        """
        Lastly we make a separate batch node which we connect to the individual call
        """
        if call_module == 'Utility' and call_function in ['batch', 'as_derivative', 'batch_all', 'force_batch']:
            block_transaction_relationship = Relationship(block, "HAS_TRANSACTION", transaction)
            subgraph = Utils.merge_subgraph(subgraph, block_transaction_relationship)

        """
        Similarly to the batch calls we treat the individual proxy calls as separate transactions and connect them
        to the proxy function node
        """
        if call_module == 'Proxy' and call_function == 'proxy':  # todo: handle proxy extrinsics
            transaction_structure = dict()
            transaction_structure['extrinsic_hash'] = transaction_data['extrinsic_hash']
            transaction_structure['address'] = transaction_data['address']
            transaction_structure['call'] = transaction_data['call']['call_args'][2]['value']
            subgraph = Transaction.create(block=block,
                                          transaction_data=transaction_structure,
                                          event_data=event_data,
                                          proxy_transaction=transaction,
                                          subgraph=subgraph)
            # todo: connect with proxy call

        if proxy_transaction is not None:
            transaction_proxy_relationship = Relationship(transaction, "IS_PROXY", proxy_transaction)
            block_transaction_relationship = Relationship(block, "HAS_TRANSACTION", transaction)
            subgraph = Utils.merge_subgraph(subgraph, transaction_proxy_relationship, block_transaction_relationship)

        """
        Here we handle the different function calls that shift balance between accounts.
        """
        if extrinsic_function['name'] in ["transfer", "transfer_all", "transfer_keep_alive"]: #todo: handle force_transfer
            return Transaction.handle_transfer(transaction_data=transaction_data,
                                               event_data=event_data,
                                               block=block,
                                               transaction=transaction,
                                               from_account=from_account,
                                               subgraph=subgraph)

        elif extrinsic_function['name'] in ["bond", "bond_extra"]:
            return Transaction.handle_bond(transaction_data=transaction_data,
                                           event_data=event_data,
                                           block=block,
                                           transaction=transaction,
                                           extrinsic_function=extrinsic_function,
                                           from_account=from_account,
                                           subgraph=subgraph)

        elif extrinsic_function['name'] == "set_controller":
            return Transaction.handle_set_controller(transaction_data=transaction_data,
                                                     event_data=event_data,
                                                     block=block,
                                                     transaction=transaction,
                                                     from_account=from_account,
                                                     subgraph=subgraph)

        elif extrinsic_function['name'] == "set_payee":
            return Transaction.handle_set_payee(transaction_data=transaction_data,
                                                event_data=event_data,
                                                block=block,
                                                transaction=transaction,
                                                from_account=from_account,
                                                subgraph=subgraph)

        elif extrinsic_function['name'] == "payout_stakers":
            return Transaction.handle_payout_stakers(transaction_data=transaction_data,  # todo: improve this sucks ass
                                                     event_data=event_data,
                                                     block=block,
                                                     transaction=transaction,
                                                     subgraph=subgraph,
                                                     batch_transaction=batch_transaction)
        return subgraph

    @staticmethod
    def handle_extrinsics_without_sender(call_module, call_function, subgraph, block, transaction):
        return

    @staticmethod
    def handle_transfer(transaction_data,
                        event_data,
                        block,
                        transaction,
                        from_account,
                        subgraph):

        to_account_address = Utils.convert_public_key_to_polkadot_address(
            transaction_data["call"]["call_args"][0]["value"])
        to_account = Account.get(subgraph, to_account_address)
        if not to_account:
            to_account = Account.create(to_account_address)

        amount_transferred = transaction_data['call']['call_args'][1]['value']

        transaction["amount_transferred"] = str(amount_transferred)
        fromaccount_transferto_relationship = Relationship(from_account, "TRANSFER_TO", to_account)
        # fromaccount_transferto_relationship.properties['block_number'] = block['block_number'] # todo: add property
        subgraph = Utils.merge_subgraph(subgraph, fromaccount_transferto_relationship, from_account, to_account,
                                        Transaction.finish_transaction(block, transaction))
        return subgraph

    @staticmethod
    def handle_set_controller(transaction_data,
                              event_data,
                              block,
                              transaction,
                              from_account,
                              subgraph):

        controller_address = Utils.convert_public_key_to_polkadot_address(
            transaction_data["call"]["call_args"][0]["value"])
        controller_account = Account.get(subgraph, controller_address)
        if not controller_account:
            controller_account = Account.create(controller_address)
        controller_account_relationship = Relationship(controller_account, "CONTROLS", from_account)

        amount_transferred = 0
        transaction["amount_transferred"] = str(amount_transferred)

        return Utils.merge_subgraph(subgraph, from_account, controller_account,
                                    Transaction.finish_transaction(block, transaction), controller_account_relationship)

    @staticmethod
    def handle_bond(transaction_data,
                    event_data,
                    block,
                    transaction,
                    extrinsic_function,
                    from_account,
                    subgraph):

        if extrinsic_function['name'] == "bond":
            amount_transferred = str(transaction_data["call"]["call_args"][1]["value"])

            controller_address = Utils.convert_public_key_to_polkadot_address(
                transaction_data["call"]["call_args"][0]["value"])
            reward_destination = transaction_data["call"]["call_args"][2]["value"]
            if "Account" in reward_destination:
                reward_destination = reward_destination["Account"]
            if controller_address != Utils.convert_public_key_to_polkadot_address(transaction_data["address"]):
                controller_account = Account.get(subgraph, controller_address)
            else:
                controller_account = from_account
            if not controller_account:
                controller_account = Account.create(controller_address)
            controlleraccount_fromaccount_relationship = Relationship(controller_account, "CONTROLS", from_account)
            subgraph = Utils.merge_subgraph(subgraph, controlleraccount_fromaccount_relationship)
            controller_account["reward_destination"] = reward_destination

        elif extrinsic_function['name'] == "bond_extra":
            amount_transferred = str(transaction_data["call"]["call_args"][0]["value"])
            controller_account = None
        else:
            raise NotImplementedError(extrinsic_function.name)

        transaction["amount_transferred"] = amount_transferred

        Account.save(from_account)
        if controller_account is not None:
            subgraph = Utils.merge_subgraph(subgraph, controller_account)
        # controller is the same as from account, else everything updated gets overwritten in update balance.

        return Utils.merge_subgraph(subgraph, from_account, Transaction.finish_transaction(block, transaction))

    @staticmethod
    def pay_fees(event_data,
                 block,
                 transaction,
                 from_account,
                 to_account,
                 amount_transferred,
                 extrinsic_function_name,
                 length_transaction,
                 author_account,
                 transfer,
                 treasury_account):
        """
        This function handles the settlement of transaction fees (validator and treasury).
        There exist some blocks where there are no fees. (i.e. first blocks of era)
        """

        validator_fee = int(Utils.extract_event_attributes(event_data[-2], False) / length_transaction)
        try:
            treasury_fee = int(Utils.extract_event_attributes(event_data[-3], True) / length_transaction)
        except IndexError:
            treasury_fee = 0
        treasury_account.update_balance(transferable=treasury_fee)
        transaction.reward_treasury.add(treasury_account.get_current_balance())
        author_account.update_balance(transferable=validator_fee)
        if validator_fee + treasury_fee:
            transaction.reward_validator.add(author_account.get_current_balance())
        total_fee = int((validator_fee + treasury_fee) / length_transaction)
        if extrinsic_function_name in ['bond', 'bond_extra']:
            from_account.update_balance(transferable=-(amount_transferred + total_fee),
                                        bonded=amount_transferred)
        else:
            if from_account != to_account and to_account is not None and transfer:
                to_account.update_balance(transferable=+amount_transferred)
                from_account.update_balance(block.block_number, to_account,
                                            transferable=-(amount_transferred + total_fee))
            else:
                from_account.update_balance(transferable=-(amount_transferred + total_fee))
        if extrinsic_function_name in ["transfer", "transfer_all", "transfer_keep_alive"] and transaction.is_successful:
            transaction.from_balance.add(from_account.get_current_balance())
            transaction.to_balance.add(to_account.get_current_balance())
        return transaction, author_account, treasury_account, from_account, to_account

    @staticmethod
    def handle_set_payee(transaction_data,
                         event_data,
                         block,
                         transaction,
                         from_account,
                         subgraph):
        reward_destination = transaction_data['call']['call_args'][0]['value']
        if isinstance(reward_destination, Dict):
            reward_destination = reward_destination['Account']
        from_account["reward_destination"] = reward_destination

        Account.save(from_account)
        return Utils.merge_subgraph(subgraph, from_account, Transaction.finish_transaction(block, transaction))

    @staticmethod
    def handle_payout_stakers(transaction_data,
                              event_data,
                              block,
                              transaction,
                              subgraph,
                              batch_transaction):
        """
        handles Staking(Reward) event by creating a nominator node, checking their payout preferences
        (reward_destination) adjusting their transferable/bonded balance respectively.
        We check whether the nominator receiving the reward is the same address as the validator in order to avoid
        creating a nominator node for a validator.
        """
        validator_stash = transaction_data['call']['call_args'][0]['value']
        author_account = Account.get(subgraph, validator_stash)
        if author_account is None:
            author_account = Account.create(validator_stash)
        validator = Validator.get_from_account(author_account)
        if validator is None:
            validator, account_validator_relationship = Validator.create(author_account, current_validatorpool=None,
                                                                         reward_points=None, validator_staking=None,
                                                                         commission=None)
            subgraph = Utils.merge_subgraph(subgraph, validator, account_validator_relationship)

        for event in event_data:
            if event['event_id'] == 'Reward':
                nominator_reward = event['attributes'][1]['value']
                nominator_address = event['attributes'][0]['value']
                nominator_account = Account.get(subgraph, nominator_address)
                if nominator_account is None:
                    nominator_account = Account.create(nominator_address)
                nominator = Nominator.get_from_account(nominator_account)
                if nominator is None:
                    nominator, account_nominator_relationship = Nominator.create(nominator_account=nominator_account,
                                                                                nominator_stake=0)
                    subgraph = Utils.merge_subgraph(subgraph, nominator, account_nominator_relationship)
                nominator['reward'] = nominator_reward
                validator_nominator_relationship = Relationship(validator, "HAS_NOMINATOR", nominator)
                subgraph = Utils.merge_subgraph(subgraph, nominator, validator,
                                                validator_nominator_relationship, nominator_account)

        return Utils.merge_subgraph(subgraph, Transaction.finish_transaction(block, transaction))

    @staticmethod
    def handle_tip(transaction_data, event_data, block, transaction):
        from_address = Utils.convert_public_key_to_polkadot_address(transaction_data["address"])
        from_account = Account.get(from_address)
        if from_account is None:
            from_account = Account.create(from_address)
        to_address = Utils.convert_public_key_to_polkadot_address(transaction_data["call"]["call_args"][0]["value"])
        to_account = Account.get(to_address)
        if not to_account:
            to_account = Account.create(to_address)
        amount_transferred = transaction_data['call']['call_args'][1]['value']
        transaction.amount_transferred = str(amount_transferred)
        return transaction, from_account, to_account, amount_transferred

    @staticmethod
    def handle_move_to_reserved(transaction_data, event_data, block, transaction):
        from_address = Utils.convert_public_key_to_polkadot_address(transaction_data['address'])
        from_account = Account.get(from_address)
        if from_account is None:
            from_account = Account.create(from_address)
        amount_moved_to_reserved = transaction_data['call']['call_args'][0]['value']

        from_account.update_balance(transferable=-amount_moved_to_reserved, reserved=amount_moved_to_reserved)
        amount_transferred = 0
        return transaction, from_account, from_account, amount_transferred

    @staticmethod
    def handle_claim(transaction_data, event_data):
        polkadot_address = transaction_data['call']['call_args'][0]['value']
        polkadot_address_clean = Utils.convert_public_key_to_polkadot_address(polkadot_address)
        polkadot_account = Account.get(polkadot_address_clean)
        if polkadot_account is None:
            polkadot_account = Account.create(polkadot_address_clean)
        amount_claimed = event_data[-2]['attributes'][2]['value']
        polkadot_account.update_balance(transferable=amount_claimed)
        return polkadot_account

class ExtrinsicFunction(GraphObject):
    __primarykey__ = "name"
    name = Property()
    has_module = RelatedTo("ExtrinsicModule")

    @staticmethod
    @profiler("ExtrinsicFunction")
    def get(function_name, subgraph):
        for node in subgraph.nodes:
            if node.has_label('ExtrinsicFunction'):
                if node['name'] == function_name:
                    return node
        extrinsic_function = Driver().get_driver().graph.run(
            "Match (n:ExtrinsicFunction {name: '" + str(function_name) + "'}) return n").evaluate()
        return extrinsic_function

    @staticmethod
    def create(function_name: str):
        extrinsic_function = Node("ExtrinsicFunction",
                                  name=function_name
                                  )
        return extrinsic_function

    @staticmethod
    def save(extrinsic_function: "ExtrinsicFunction"):
        Driver().get_driver().save(extrinsic_function)

class ExtrinsicModule(GraphObject):
    __primarykey__ = "name"

    name = Property()
    has_function = RelatedTo("ExtrinsicFunction")

    @staticmethod
    @profiler("ExtrinsicModule")
    def get(name, subgraph):
        for node in subgraph.nodes:
            if node.has_label('ExtrinsicModule'):
                if node['name'] == name:
                    return node
        extrinsic_module = Driver().get_driver().graph.run(
            "Match (n:ExtrinsicModule {name: '" + str(name) + "'}) return n").evaluate()
        return extrinsic_module

    @staticmethod
    def create(module_name: str) -> Node:
        extrinsic_module = Node("ExtrinsicModule",
                                name=module_name,
                                )
        return extrinsic_module

    @staticmethod
    def save(extrinsic_module: "ExtrinsicModule"):
        Driver().get_driver().save(extrinsic_module)

class EventFunction(GraphObject):
    __primarykey__ = "name"

    name = Property()

class EventModule(GraphObject):
    __primarykey__ = "name"

    name = Property()
    has_function = RelatedTo("EventFunction")

class TransferDenomination(GraphObject):
    __primarykey__ = "name"

    name = Property()

#### Accounts and Balances
class Account(GraphObject):
    __primarykey__ = "address"

    address = Property()
    account_index = Property()
    nonce = Property()
    reward_destination = Property()

    transfer_to = RelatedTo("Account")
    controls = RelatedTo("Account")
    is_validator = RelatedTo("Validator")
    is_nominator = RelatedTo("Nominator")

    @staticmethod
    def create(address: str):
        account = Node("Account",
                       address=address
                       )
        return account

    @staticmethod
    @profiler("Account")
    def get(subgraph, address: str):
        for node in subgraph.nodes:
            if node.has_label('Account'):
                if node['address'] == address:
                    return node
        account = Driver().get_driver().graph.run(
            "Match (n:Account {address: '" + str(address) + "'}) return n").evaluate()

        return account

    @staticmethod
    def save(account: "Account"):
        Driver().get_driver().save(account)

    @staticmethod
    @profiler("Account")
    def get_treasury():
        treasury = Driver().get_driver().graph.run(
            "Match (n:Account {address: " + str(treasury_address) + "}) return n").evaluate()
        if not treasury:
            treasury = Account.create(treasury_address)
        return treasury

class ValidatorPool(GraphObject):
    __primarykey__ = "era"

    era = Property()
    validator_payout = Property()
    treasury_payout = Property()
    total_staked = Property()

    block_number = RelatedTo("Block")
    previous_validator_pool = RelatedTo("ValidatorPool")
    hasValidators = RelatedTo("Validator")

    @staticmethod
    @profiler("ValidatorPool")
    def get(era):
        query = "Match (v:ValidatorPool{era:x}) return v"
        query = query.replace("x", str(era))
        result = Driver().get_driver().graph.run(query).evaluate()
        return result

    @staticmethod
    def create(event,block:"Block"): # todo: get rid of try/except
        try:
            validator_pool =\
                                Node("ValidatorPool",
                                era=event['attributes'][0]["value"],
                                validator_payout=event['attributes'][1]["value"],
                                treasury_payout=event['attributes'][2]["value"],
                                block_number=block['block_number']
                                )
        except (IndexError, TypeError):
            validator_pool = ValidatorPool(
                era = event['attributes'][0],
                validator_payout = event['attributes'][1],
                treasury_payout = event['attributes'][2],
                block_number = block['block_number']
            )
        return validator_pool

class Validator(GraphObject):
    __primarykey__ = "era"

    account = RelatedFrom("Account", "IS_VALIDATOR")
    era = Property()
    reward_points = Property()
    total_stake = Property()
    own_stake = Property()
    commission = Property()

    has_nominator = RelatedTo("Nominator")

    @staticmethod
    @profiler("Validator")
    def get_account_from_validator(validator):
        print(validator.account.triples())

    @staticmethod
    @profiler("Validator")
    def get_from_account(account: "Account") -> "Validator":
        return Driver().get_driver().graph.run(
            "Match (v:Validator)<-[:IS_VALIDATOR]-(a:Account {address: '" + str(
                account['address']) + "'}) return v").evaluate()

    @staticmethod
    def create(validator_account, current_validatorpool=None, reward_points=None,
                                         validator_staking=None,  commission=None):

        if current_validatorpool is None:
            validator = Node("Validator",
                        )
            account_validator_relationship = Relationship(validator_account, "IS_VALIDATOR", validator)
            return validator, account_validator_relationship

        validator = Node("Validator",
                         era=current_validatorpool['era'],
                         total_stake=validator_staking["total"],
                         own_stake=validator_staking["own"],
                         commission=commission["commission"],
                         reward_points=reward_points
                         )
        account_validator_relationship = Relationship(validator_account, "IS_VALIDATOR", validator)
        return validator, account_validator_relationship

class Nominator(GraphObject):
    total_staked = Property()
    reward = Property()

    @staticmethod
    @profiler("Nominator")
    def get_from_account(account: "Account") -> "Nominator":
        return Driver().get_driver().graph.run(
            "Match (n:Nominator)<-[:IS_NOMINATOR]-(a:Account {address: '" + str(
                account['address']) + "'}) return n").evaluate()

    @staticmethod
    def create(nominator_account, nominator_stake):
        nominator = Node("Nominator",
                         total_staked=nominator_stake
                         )
        account_nominator_relationship = Relationship(nominator_account, "IS_NOMINATOR", nominator)
        return nominator, account_nominator_relationship


"""exercicio034.py em 2018-10-09. Projeto Practice Python.

No exercício anterior, criamos um dicionário de aniversários de cientistas famosos.
Neste exercício, modifique seu programa para carregar o dicionário de aniversário de um arquivo JSON no disco,
em vez de ter o dicionário definido no programa.

Bônus: peça ao usuário o nome de outro cientista e o aniversário para adicioná-lo ao dicionário
e atualize o arquivo JSON que você tem no disco com o nome do cientista.
Se você executar o programa várias vezes e continuar adicionando novos nomes,
seu arquivo JSON deverá ficará cada vez maior.

"""
import json
from cicero import cabecalho

def lista_dados(dicionario: dict):
    """Lista as informações do arquivo Json a partir de um dicionário

    :param dicionario: informações do arquivo Json
    :type dicionario: dict
    """
    print()
    for k in dicionario:
        print(f'{str(k).center(60)}')
    print()

def adiciona(dicionario: dict):
    """Adiciona dados no arquivo Json

    :param dicionario: informações lidas do arquivo Json
    :type dicionario: dict
    """
    while True:
        nome = str(input('Nome: ')).strip()
        nascimento = str(input('Nascimento: ')).strip()
        dicionario[nome] = nascimento
        with open('json/cientistas.json', 'w') as f:
            json.dump(dicionario, f)
        print(f'\n{nome} foi adicionado com sucesso.\n')
        pergunta = str(input('Quer adicionar outro cientista? {S/N] \n')).lower().strip()[0]
        if pergunta == 's':
            continue
        else:
            break
    return None

def aniversario(dicionario: dict) -> object:
    """Retorna a consulta no arquivo Json

    :param dicionario: dados do arquivo Json
    :type dicionario: dict
    :return: resultado da pesquisa
    :rtype: object
    """
    nome = str(input('Aniversário de quem você quer saber? ')).strip()
    return f'\n{nome} nasceu em {dicionario.get(nome, f"(sem informações)")}.\n'

def opcoes():
    """Gera as opções para o usuário a partir de um dicionário

    """
    dicionario = {'L': 'Lista os cientistas',
                  'N': 'Mostra a data de nascimento',
                  'A': 'Adiciona mais um cientista',
                  'S': 'Sai do programa'}
    print(' Opções: '.center(30, '-'))
    for k, v in dicionario.items():
        print(f'{k} -> {v}')

def aniversarios():
    """Manipula um arquivo Json (escrita/leitura)

    """
    # lê o arquivo Json e armazena em um dicionário
    with open('json/cientistas.json', 'r') as f:
        cientista = json.load(f)
    cabecalho('Bem-vindo ao dicionário de aniversários. Nós sabemos a data de nascimento de:', 60, ' ')
    lista_dados(cientista)

    while True:
        opcoes()
        pergunta = str(input('\tQual é a sua opção? ')).lower().strip()[0]
        if pergunta == 'l':
            lista_dados(cientista)
        elif pergunta == 'n':
            print(aniversario(cientista))
        elif pergunta == 'a':
            print(adiciona(cientista))
            lista_dados(cientista)
        else:
            cabecalho('Fim', 30, '-')
            break

if __name__ == '__main__':
    cabecalho('Aniversário Com Json')
    aniversarios()


import sys
from pathlib import Path
import datetime

from .version import __version__

def count_docstrings(lines):
    ''' Counts docstrings and remove them from the line list.

    Args:
        lines (list[str]): list of the code lines (in order!)

    Returns:
        int, list[str]: number of docstring lines, list of the code lines with all the docstrings
                        removed

    '''

    lines_minus_docstr = []
    counter = 0
    in_docstr = False

    # Go through each line in order
    for line in lines:

        #Look for the docstring tag
        if line.strip(' ').startswith(("'''", '"""')):
            in_docstr = ~in_docstr
            counter += 1
            continue

        # If I am in a docstring, count the extra lines
        if in_docstr:
            counter += 1
        # Else, keep the line for latter
        else:
            lines_minus_docstr += [line]

    # A small sanity check before closing
    if len(lines_minus_docstr) != len(lines) - counter:
        raise Exception('Ouch! Something is very wrong here!')

    return counter, lines_minus_docstr

def count_comments(lines):
    ''' Counts commented lines, i.e. lines starting with #

    Args:
        lines (list[str]): list of the code lines

    Returns:
        int, list[str]: number of commented lines, list of the code lines with all the comments
                        removed.

    Caution:
        This will look for all lines starting with #, including inside docstrings ! If you care
        about this, you should remove these first using `count_docstrings()`to avoid confusion.

    '''

    counter = 0
    lines_minus_comm = []

    for line in lines:
        if line.strip(' ').startswith('#'):
            counter += 1
        else:
            lines_minus_comm += [line]

    # A small sanity check before closing
    if len(lines_minus_comm) != len(lines) - counter:
        raise Exception('Ouch! Something is very wrong here!')

    return counter, lines_minus_comm

def count_empty(lines):
    ''' Counts empty lines.

    Args:
        lines (list[str]): list of the code lines

    Returns:
        int, list[str]: number of empty lines, list of the code lines with all the empty ones
                        removed.

    Caution:
        This will look for all the empty lines, including inside docstrings !
        If you care about this, you should remove these first using `count_docstrings()` to avoid
        confusion.

    '''

    counter = 0
    lines_minus_empty = []

    for line in lines:
        # Let us not forget about the last line, which may not contain a return tag !
        if line.strip(' ').startswith('\n') or len(line.strip(' ')) == 0:
            counter += 1
        else:
            lines_minus_empty += [line]

    # A small sanity check before closing
    if len(lines_minus_empty) != len(lines) - counter:
        raise Exception('Ouch! Something is very wrong here!')

    return counter, lines_minus_empty

def extract_line_stats(search_path, recursive=False, save_to_file=None, verbose=False):
    ''' Computes the number of blank, commented, docstringed and actual code lines in Python code.

    The code can process either a specific .py file, or, if a directory name if given, all the .py
    files within it (including the options of making a recursive search).

    Args:
        search_path (pathlib.Path, str): path to file or folder to process.
        recursive (bool): if True, will run a recursive search for .py files in subfolders.
        save_to_file (pathlib.Path, str): if set, all code output will be stored in this file
        verbose (bool): if True, will give info for every individual file processed (not just the
            total).

    Raises:
        Exception: If the search_path is invalid

    '''

    # Set up the message output channel if needed
    if save_to_file is None:
        mess_chan = sys.stdout
    elif isinstance(save_to_file, (str, type(Path(' ')))):
        mess_chan = open(save_to_file, 'w')
    else:
        raise Exception('Ouch ! save-to_file should be of type (str or %s), not: %s' %
                        (type(Path('.'), type(save_to_file))))

    # Set the scene
    print(' ', file=mess_chan)
    print('linestats %s - https://github.com/fpavogt/linestats' % (__version__), file=mess_chan)
    print('Copyright (c) 2020-2022 F.P.A. Vogt', file=mess_chan)
    print(' ', file=mess_chan)

    start_time = datetime.datetime.now()
    print('Start time: %s' % (start_time), file=mess_chan)
    print(' ', file=mess_chan)

    # Prepare some variables.
    file_total = 0
    grand_total = 0
    code_total = 0
    comm_total = 0
    empty_total = 0
    docstr_total = 0

    # If I got a string, turn this into a path
    if isinstance(search_path, str):
        search_path = Path(search_path)

    elif not isinstance(search_path, type(Path('.'))):
        raise Exception('Ouch ! Type %s for search_path is invalid.' % (type(search_path)))

    # Was a single file specified ?
    if search_path.is_file():
        fnlist = [search_path]

    # Was I given a proper directory ?
    elif search_path.is_dir():
        if recursive:
            txt = ' recursively'
            mthd = '**/*'
        else:
            txt = ''
            mthd = '*'

        print('Looking%s for Python script files in %s' % (txt, search_path), file=mess_chan)
        print(' ', file=mess_chan)
        fnlist = search_path.glob(mthd)

    else:
        raise Exception('Please specify a valid path or a filename')

    # Now go through each file and extract the line statistics
    for file_path in fnlist:

        # Make sure I'm only touching regular files ...
        if not file_path.is_file():
            #print('%s is not a file. Skipping it ...' % (file_path))
            continue

        # Only deal with .py files
        if file_path.suffix != '.py':
            if verbose:
                print('%s' % (file_path), file=mess_chan)
                print('  does not look like Python code. Skipping it.', file=mess_chan)

            continue

        # Very well, let's extract the file lines.
        this_file = open(file_path, 'r')
        lines = this_file.readlines()
        this_file.close()

        # I have a code file, so let's count it
        file_total += 1

        # Get the total line count
        total_lines = len(lines)
        grand_total += total_lines

        # If the file is empty, avoid a bad division by 0
        if total_lines == 0 and verbose:
            print(file_path, file=mess_chan)
            print('  Total: 0', file=mess_chan)
            continue

        # Track the docstrings
        docstr, lines_minus_docstr = count_docstrings(lines)
        docstr_total += docstr

        # Now the comments (feed it all except the docstrings, to avoid counting these twice)
        comm, lines_minus_comm = count_comments(lines_minus_docstr)
        comm_total += comm

        # And finally count the blank lines
        empty, _ = count_empty(lines_minus_comm)
        empty_total += empty

        code = total_lines - empty - docstr - comm
        code_total += code

        if verbose:
            print(file_path, file=mess_chan)
            print(f'  Total: {total_lines} - ' +
                  f'Code: {code} [{100*code/total_lines:.1f}%] - '+
                  f'Comment+docstr: {comm+docstr} [{100*(comm+docstr)/total_lines:.1f}%] - '+
                  f'Blank: {empty} [{100*empty/total_lines:.1f}%]', file=mess_chan)

    # Print the final numbers
    print(' ', file=mess_chan)
    print('Grand total: %i lines in %i files' % (grand_total, file_total), file=mess_chan)
    if grand_total > 0:
        print('    Code: %i [%.1f%%]' % (code_total, code_total/grand_total*100), file=mess_chan)
        print('    Docstrings: %i [%.1f%%]' % (docstr_total, docstr_total/grand_total*100),
              file=mess_chan)
        print('    Comments: %i [%.1f%%]' % (comm_total, comm_total/grand_total*100),
              file=mess_chan)
        print('    Empty: %i [%.1f%%]' % (empty_total, empty_total/grand_total*100), file=mess_chan)

    print(' ', file=mess_chan)
    print('All done in %ss.' %(datetime.datetime.now() - start_time).total_seconds(),
          file=mess_chan)
    print(' ', file=mess_chan)

    # If I was writing to file, let's remember to close it.
    if save_to_file is not None:
        mess_chan.close()
        print(' ')
        print(' All done in  %ss.' %(datetime.datetime.now() - start_time).total_seconds())
        print(' Output saved under %s' % save_to_file)
        print(' ')


'''
Does the sound for the game
'''

import os
import pygame

from deploy import SiteDeployment

class Sound:
    '''
    Handles all the sound for the game
    '''

    def __init__(self, game_engine):
        self.game_engine = game_engine
        game_music_path = os.path.join(SiteDeployment.resource_path, "sound", "music.mp3")
        self.music_loaded = False

        try:
            pygame.mixer.music.load(game_music_path)
            self.music_loaded = True
        except BaseException as e:
            print("Unable to load music: ", str(e))
            self.music_loaded = False

        self.playing = False

    def is_playing(self):
        '''
        Getter for instance variable playing
        '''
        return self.playing

    def play_game_music(self):
        '''
        Play the game music
        '''
        if not self.music_loaded:
            return

        pygame.mixer.music.play(loops=-1)
        self.playing = True

    def pause_game_music(self):
        '''
        Pause the game music
        '''
        if not self.music_loaded:
            return

        pygame.mixer.music.pause()
        self.playing = False

    def toggle_game_music(self):
        '''
        Toggle the game music on and off
        '''
        if not self.music_loaded:
            return

        if self.is_playing():
            self.pause_game_music()
        else:
            self.play_game_music()


# https://blog.knshnb.com/posts/ahc007-optuna/

import os
import optuna
import joblib
import subprocess

n_parallel = 50
n_files = 100
n_trials = 100
storage_name = "AHCxxx"
study_name = "study_name"
answer_file_name = "main"

def calc_score_each(seed, Ps):
    in_file = f"in/{seed:04}.txt"
    out_file = f"/dev/null"

    cmd = f"tools/target/release/tester ./src/cpp/{answer_file_name} " + \
        " ".join(map(str, Ps)) + f" < {in_file} > {out_file}"
    child = subprocess.Popen(cmd, shell=True,
                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    _, stderr = child.communicate()

    try:
        score = int(stderr.split()[-1])
    except ValueError:
        print(stderr, flush=True)
        score = -1

    print(f"seed{seed}:{score}", end=" ", flush=True)

    return score

def calc_scores(Ps):
    scores = joblib.Parallel(n_jobs=n_parallel)(
        joblib.delayed(calc_score_each)(i, Ps) for i in range(n_files)
    )
    return scores

def objective(trial):
    print("start")

    P01 = trial.suggest_int("P01", 0, 1)
    P02 = trial.suggest_float("P02", 0.0, 1.0)

    PARAMS = [P01, P02]

    print("params:", PARAMS, sep=" ")

    scores = calc_scores(PARAMS)

    scores = [score for score in scores if score != -1]

    print(f"success:{len(scores)}")

    return sum(scores)

if __name__ == "__main__":
    print("setup start")

    os.system(
        f"/usr/bin/g++-10 -fdiagnostics-color=always ./src/cpp/{answer_file_name}.cpp -o ./src/cpp/{answer_file_name} -std=c++17 -Wall -Wextra -O3")

    study = optuna.create_study(
        direction="minimize",
        storage=f"sqlite:///{storage_name}.db",
        study_name=study_name,
        load_if_exists=True,
    )

    print("setup done")

    study.optimize(objective, n_trials=n_trials)


from glob import glob
import json
import os
import re
import util

sectionsplit = re.compile('((\n)+(==.*?==)?)+((\n)+|$)')

def format_content(content):
    return sectionsplit.sub('\n\n', content)

def validate_subcategories(pageid, title, cats):
    global flood_control_count, dams_count, works_about_count, films_about_count

    for cat in cats:
        if cat.startswith('Flood control in'):
            util.log('{} [{}]: FLOOD CONTROL'.format(pageid, title), 'totext-subcats')
            return False
        if cat.startswith('Dams in'):
            util.log('{} [{}]: DAMS'.format(pageid, title), 'totext-subcats')
            return False
        if cat.startswith('Works about'):
            util.log('{} [{}]: WORKS ABOUT'.format(pageid, title), 'totext-subcats')
            return False
        if cat.startswith('Films about'):
            util.log('{} [{}]: FILMS ABOUT'.format(pageid, title), 'totext-subcats')
            return False
        if cat == 'Dust Bowl':
            util.log('{} [{}]: DUST BOWL'.format(pageid, title), 'totext-subcats')
            return False
    return True

def normalize(pageid, props):
    title, content = props['title'], props['content']

    if title.startswith('List of '):
        util.log('{} [{}]: LIST OF'.format(pageid, title), 'totext-content')
    elif title.startswith('Template:'):
        util.log('{} [{}]: TEMPLATE'.format(pageid, title), 'totext-content')
    elif title.startswith('Book:'):
        util.log('{} [{}]: BOOK'.format(pageid, title), 'totext-content')
    elif not validate_subcategories(pageid, title, props['subcats']):
        return False, False
    else:
        content = format_content(content)
        if len(content) >= 10:
            return title, content
        util.log('{} [{}]: CONTENT LENGTH {}'.format(pageid, title, len(content)), 'totext-content')

    return False, False

def run():
    util.ensure_empty_dir('plaintext')

    print('Converting documents to plaintext...', end='\r')

    total_count, removed_count = 0, 0
    for input_filename in glob('raw/*/*.txt'):
        _, cat, filename = input_filename.split('/')

        output_dir = 'plaintext/{}'.format(cat)
        os.makedirs(output_dir, exist_ok=True)
        output_filename = '{}/{}'.format(output_dir, filename)

        with open(input_filename) as input_f:
            props = json.load(input_f)
            title, content = normalize(filename, props)

            if title:
                with open(output_filename, 'w', encoding='utf-8') as output_f:
                    output_f.write(title + '\n\n')
                    output_f.write(content)
            else:
                removed_count += 1
        total_count += 1

    percent = round(removed_count/total_count * 100)
    print('Converted documents. Removed {}/{} docs [{}%]'.format(removed_count, total_count, percent))


import pandas as pd
import numpy as np
import os
from sklearn import linear_model

def read_data(path):
    data = np.load(path)
    column_name = data[0].decode('UTF-8').strip().split(',')
    data_list = []
    for train in data[1:]:
        tmp = train.decode('UTF-8').split(',')
        tmp = [0 if t == 'NA' else float(t) for t in tmp]
        data_list.append(tmp)

    df = pd.DataFrame(data=data_list, columns=column_name)

    return df

def main():
    if os.path.exists('train.df'):
        train_df = pd.read_pickle('train.df')
    else:
        train_df = read_data("ecs171train.npy")
        train_df.to_pickle('train.df')
    # if os.path.exists('test.df'):
    #     test_df = pd.read_pickle('test.df')
    # else:
    #     test_df = read_data("ecs171test.npy")
    #     test_df.to_pickle('test.df')
    main = train_df
    nunique = main.apply(pd.Series.nunique)
    drop_cols = nunique[nunique == 1].index
    main.drop(drop_cols, axis=1, inplace=True)

    msk = np.random.rand(len(main)) < 0.8
    train_df = main[msk]
    test_df = main[~msk]
    train_x = train_df.iloc[:, :-1]
    train_y = train_df.iloc[:, -1]
    test_x = test_df.iloc[:, :-1]
    test_y = test_df.iloc[:, -1]
    train_y = train_y.apply(lambda x: 0 if x == 0 else 1)
    test_y = test_y.apply(lambda x: 0 if x == 0 else 1)

    logreg = linear_model.LogisticRegression()
    logreg.fit(train_x, train_y)
    accuracy = logreg.score(test_x, test_y)
    print(accuracy)

if __name__ == '__main__':
    main()


import cv2
import imutils
import pytesseract

pytesseract.pytesseract.tesseract_cmd =r"C:/Program Files/Tesseract-OCR/tesseract.exe"

image = cv2.imread("D:/Self_learning/My_project/car_license_plate/archive/images/Cars3.png")
image = imutils.resize(image, width=500)

cv2.imshow('Original Image', image)

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

smooth = cv2.bilateralFilter(gray, 11, 17, 17)
#cv2.imshow("Gray Image", gray)

corner = cv2.Canny(gray, 170, 200)
#cv2.imshow("Highlighted edges", corner)

seg , new = cv2.findContours(corner.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

image1 = image.copy()
cv2.drawContours(image1, seg, -1, (0,0,255),3)
#cv2.imshow('Edge segmention', image1)

seg=sorted(seg , key=cv2.contourArea, reverse=True)[:30]
NoPlate = None

image2 = image.copy()
cv2.drawContours(image2, seg, -1, (0,255,0),3)
#cv2.imshow("Number plate segmention", image2)

count = 0
name = 1

for i in seg:
    perimeter = cv2.arcLength(i, True)
    approx = cv2.approxPolyDP(i, 0.02*perimeter, True)

    if(len(approx == 4)):
        NoPlate = approx
        x, y, w, h = cv2.boundingRect(i)
        crp_image = image[y:y+h, x:x+w]

        cv2.imwrite(str(name)+ '.png', crp_image)
        name += 1

        break

cv2.drawContours(image,[NoPlate], -1, (0,255,0),3)
#cv2.imshow("Final Image", image)

img_plate = cv2.imread('1.png')
cv2.imshow('Number Plate', img_plate)
cv2.waitKey(0)
cv2.destroyAllWindows()

extracted_text = pytesseract.image_to_string(img_plate)
print("Number Plate: ", extracted_text)


from utils.dnn_methods import *
from utils.polar_pla import preprocess, preprocess_from_pickle
from models import *
import optuna

''' This script should be used to test a configuration. Below are the datasets.
        Fields which can be modified between tests are marked with an x.'''
                        #---------------------#
                        # "DataSets/Name.csv" #  (This is what each of the fields mean below)
                        #  Median Fiter Size  #
                        #    PLA Max Error    #

datasets = [["DataSets/CTtemp.csv",10,6000],["DataSets/snp500.csv",10,10],["DataSets/Power_hour.csv",4,0.5],["DataSets/N225P.csv",10,10], ["DataSets/AirPassengers.csv",0,0.1], ["DataSets/JSE.csv",5,0]]

# dataset 1 gets about 100% accuracy with 500 epochs [10, 6000]

models_to_average = 1 # keep this constant across tests

        #--------- your test goes here, modifiable attributes are labelled with an x ---------#

# dataset and model type #
dataset = datasets[2]  # Change the index to test different datasets.                                       # x
component = 2 # 0 to predict trend, 1 to predict duration, 2 for a dual approach (trend and duration)       # x

# hyperparameters #                                                                                # x

#inputs, outputs = preprocess_from_pickle("DataSets/jse.pkl", seq_length, component)

# now just simply uncomment the model you'd like to test:

# This system uses walk forward validation. Define your k here.
k = 4
train_ratio = 4 # : 1 : 1 <- ratio of train to validate to test.

outputfile = "" # if this is empty it will just print instead

print_output = True if outputfile == "" else False
if not print_output:
    outf = open(outputfile, 'a')
import math
import statistics
res_1 = []
res_2 = []
tp = 0 # true positives
tn = 0 # true negatives
fp = 0 # false positives
fn = 0 # false negatives
def objective(trial):

    n_layers = trial.suggest_int('n_layers', 2, 4)
    hidden_size = trial.suggest_int('hidden_size', 32, 128)
    lr=trial.suggest_float('lr', 0.0001, 0.05)
    seq_length=trial.suggest_int('seq_len', 6, 10)
    dropout=trial.suggest_float('dropout', 0, 0.5)
    training_epochs=200
    # TCN only ↓
    kernel_size=trial.suggest_int('kernel', 2, 4)

    seq_length = max(seq_length, kernel_size*2-1)
    inputs, outputs = preprocess(dataset[0], dataset[1], dataset[2], seq_length, component, trenet=False)

    print("KERNEL SIZE", kernel_size)
    print("SEQ LENGTH", seq_length)

    def create_DNN():                                                                                # x
        #return MLP(seq_length*2, hidden_size, max(1,component), dropout).to(dev)
        #return CNN(seq_length, hidden_size, max(1,component), kernel_size, dropout).to(dev)
        return TCN(seq_length,max(1, component), [hidden_size]*n_layers, kernel_size, dropout).to(dev)
        #return RNN(max(1,component), 2, hidden_size, 1, dropout).to(dev)
        #return LSTM(max(1,component), 2, hidden_size, 1, dropout).to(dev)
        #return BiLSTM(max(1,component), 2, hidden_size, 1, dropout).to(dev)
        #return TreNet(max(1,component), 2, hidden_size, 1, dropout, seq_length).to(dev)
    try:
        return train_and_test(create_DNN, inputs, outputs, lr, 64, seq_length, training_epochs, component, k, train_ratio, validation=True)[0] # train and test it
    except:
        print("Model crashed, continuing")
        return 9999999999
import time
start_Time = time.time()
study = optuna.create_study()
study.optimize(objective, n_trials=50)
output = open('optuna_results_BiLSTM_HPC.txt', 'w')
output.write(str(study.best_params))
os.remove("temp.pt")
print("Time taken: ", time.time() - start_Time)


import requests
import json
url = ('http://saral.navgurukul.org/api/courses')
response = requests.get(url)
string = response.text
data = response.json()

my_files = open("courses.json",'r+')
files = json.loads(string)
# print(files)

cource = files["availableCourses"]
for i in range(len(cource)):
	print(i+1,cource[i]['name'],"id =",cource[i]['id'])
course_select=int(input("enter a cource number:"))
print(cource[course_select-1]['name'])

course_select=int(input("enter a cource number:"))

saral_data = requests.get("http://saral.navgurukul.org/api/courses/"+str(cource[course_select-1]['id'])+"/exercises")
data = saral_data.json()
api=open("saral_data.json",'w')
dat=json.dump(data,api,indent=2)
parents=data['data']
for j in range(len(parents)):
	print(j+1,"-",parents[j]['name'])
	counter = 1
	for index in parents[j-1]["childExercises"]:
		print("      ",counter,".",index["name"])
		counter+=1
Up_Navigation=input("enter 'up' for going back to all courses:")
cource2=files["availableCourses"]
if Up_Navigation=="up":
	for i in range(len(cource)):
		print(i+1,cource2[i]['name'],"id =",cource[i]['id'])
	course_select=int(input("enter a cource number:"))
	saral_data = requests.get("http://saral.navgurukul.org/api/courses/"+str(cource[course_select-1]['id'])+"/exercises")
	data = saral_data.json()
	api=open("saral_data.json",'w')
	dat=json.dump(data,api,indent=2)
	parents=data['data']
	for j in range(len(parents)):
		print(j+1,"-",parents[j]['name'])
		counter = 1
		for index in parents[j-1]["childExercises"]:
			print("      ",counter,".",index["name"])
			counter+=1

select_parent=int(input("select a parent"))
print(parents[select_parent-1]["name"])
count=1
for index in parents[select_parent-2]["childExercises"]:
	print("   ",count,index["name"])
	count+=1
select_child=int(input("select a child"))
slug = parents[select_parent-2]["childExercises"][select_child-1]['slug']

link="http://saral.navgurukul.org/api/courses/"+str(cource[select_child-1]['id'])+"/exercise/getBySlug?slug="+str(slug)
slug1=requests.get(link)
slug2=slug1.json()
print(slug2["content"])

Previous_Navigation=input("enter'p'if you want previous line:")
if Previous_Navigation =='p':
    print(parents[select_parent-2]["childExercises"][select_child-2]['slug'])
    link2="http://saral.navgurukul.org/api/courses/"+str(cource[select_child]['id'])+"/exercise/getBySlug?slug="+str(slug)
    slug_open=requests.get(link2)
    slug2_hit=slug_open.json()
    print(slug2_hit["content"])


from tkbuilder.image_readers.image_reader import ImageReader
import numpy
import gdal
from PIL import Image

gdal_to_numpy_data_types = {
    "Byte": numpy.uint8,
    "UInt16": numpy.uint16,
    "Int16": numpy.int16,
    "UInt32": numpy.uint32,
    "Int32": numpy.int32,
    "Float32": numpy.float32,
    "Float64": numpy.float64,
    "CInt16": numpy.complex64,
    "CInt32": numpy.complex64,
    "CFloat32": numpy.complex64,
    "CFloat64": numpy.complex64
}

class GeotiffImageReader(ImageReader):
    fname = None
    full_image_nx = int
    full_image_ny = int
    n_bands = int

    display_bands = [0, 1, 2]        # type: [int, int, int]
    all_image_data = None           # type: numpy.ndarray

    _dset = None

    def __init__(self,
                 fname,          # type: str
                 ):
        self._dset = gdal.Open(fname, gdal.GA_ReadOnly)
        self.full_image_ny = self._dset.RasterYSize
        self.full_image_nx = self._dset.RasterXSize
        self.n_bands = self._dset.RasterCount
        self.n_overviews = self._dset.GetRasterBand(1).GetOverviewCount()
        self.numpy_data_type = self.get_numpy_data_type()

        if self.n_bands == 1:
            self.display_bands = [0]

    def get_numpy_data_type(self):
        gdal_data_type = gdal.GetDataTypeName(self._dset.GetRasterBand(1).DataType)
        return gdal_to_numpy_data_types[gdal_data_type]

    def read_full_image_data_from_disk(self):  # type: (...) -> ndarray
        bands = range(self.n_bands)
        return self.read_full_display_image_data_from_disk(bands)

    def read_full_display_image_data_from_disk(self,
                                               bands,  # type: []
                                               ):  # type: (...) -> ndarray
        n_bands = len(bands)
        image_data = numpy.zeros((self.full_image_ny, self.full_image_nx, n_bands),
                                  dtype=self.numpy_data_type)
        for i in range(n_bands):
            image_data[:, :, i] = self._dset.GetRasterBand(bands[i] + 1).ReadAsArray()
        if image_data.shape[2] == 1:
            image_data = numpy.squeeze(image_data)
        return image_data

    def __getitem__(self, key):
        if self.n_overviews == 0:
            if self.all_image_data is None:
                self.all_image_data = self.read_full_display_image_data_from_disk(self.display_bands)
            return self.all_image_data[key]
        else:
            full_image_step_y = key[0].step
            full_image_step_x = key[1].step

            min_step = min(full_image_step_y, full_image_step_x)

            full_image_start_y = key[0].start
            full_image_stop_y = key[0].stop
            full_image_start_x = key[1].start
            full_image_stop_x = key[1].stop

            overview_level = int(numpy.log2(min_step)) - 1
            overview_decimation_factor = numpy.power(2, overview_level + 1)

            overview_start_y = int(full_image_start_y / overview_decimation_factor)
            overview_stop_y = int(full_image_stop_y / overview_decimation_factor)
            overview_start_x = int(full_image_start_x / overview_decimation_factor)
            overview_stop_x = int(full_image_stop_x / overview_decimation_factor)

            overview_x_size = overview_stop_x - overview_start_x - 1
            overview_y_size = overview_stop_y - overview_start_y - 1

            n_display_bands = len(self.display_bands)
            d = numpy.zeros((overview_y_size, overview_x_size, n_display_bands), dtype=self.numpy_data_type)
            if overview_level >= 0:
                for i in range(n_display_bands):
                    d[:, :, i] = self._dset.GetRasterBand(self.display_bands[i] + 1).\
                        GetOverview(overview_level).ReadAsArray(overview_start_x,
                                                                overview_start_y,
                                                                overview_x_size,
                                                                overview_y_size)
            else:
                full_image_x_size = full_image_stop_x - full_image_start_x - 1
                full_image_y_size = full_image_stop_y - full_image_start_y - 1
                for i in range(n_display_bands):
                    d[:, :, i] = self._dset.GetRasterBand(self.display_bands[i] + 1).\
                        ReadAsArray(full_image_start_x,
                                    full_image_start_y,
                                    full_image_x_size,
                                    full_image_y_size)
            y_resize = int(numpy.ceil((full_image_stop_y - full_image_start_y) / full_image_step_y))
            x_resize = int(numpy.ceil((full_image_stop_x - full_image_start_x) / full_image_step_x))

            pil_image = Image.fromarray(d)
            resized_pil_image = Image.Image.resize(pil_image, (x_resize, y_resize))
            resized_numpy_image = numpy.array(resized_pil_image)
            return resized_numpy_image


import pandas as pd
import numpy as np
import json
import preprocess

def fetchCircularBiPlotData(type_to_display, class_to_display):
	'''
	This function is used to fetch the data to build a Circular BiPlot and is called from app.py
	Input:
		type_to_display:
			type_to_display - A string keyword that is used to specify what kind of data is to be
			displayed in the graph.
			Values could be 'CONSOLIDATED' or the name of the genre.
	'''

	plot_data = []
	if type_to_display == 'CONSOLIDATED':
		for genre in ALL_GENRES:

			movies_in_genre = list(GENRE_MOVIE_MAPPER[genre]) # This list stores all the movies for the particular genre

			genre_budget = 0
			genre_revenue = 0

			for movie in movies_in_genre:
				movie_details = MOVIE_DETAILS_MAPPER[movie]
				budget = movie_details["budget"]
				revenue = movie_details["revenue"]

				genre_budget += budget
				genre_revenue += revenue

			GENRE_BUDGETS[genre]  = int(genre_budget / 1e6) # Dividing values by a million
			GENRE_REVENUES[genre] = int(genre_revenue / 1e6) # Dividing values by a million

			AVG_GENRE_BUDGETS[genre]  = int(genre_budget / 1e6) /len(movies_in_genre) # Dividing values by a million
			AVG_GENRE_REVENUES[genre] = int(genre_revenue / 1e6) /len(movies_in_genre) # Dividing values by a million

			genre_details_dict = {}
			genre_details_dict["genre"]   = genre
			genre_details_dict["budget"]  = int(genre_budget / 1e6) # Dividing values by a million
			genre_details_dict["revenue"] = int(genre_revenue / 1e6) # Dividing values by a million
			genre_details_dict["avg_genre_budget"] = int((genre_budget / 1e6) /len(movies_in_genre)) # Dividing values by a million
			genre_details_dict["avg_genre_revenue"] = int((genre_revenue / 1e6) /len(movies_in_genre)) # Dividing values by a million
			plot_data.append(genre_details_dict)
		return plot_data

	else:
		'''
		This section is executed when the User clicks on a separate genre and we need to provide genre-specific information to the User.
		The current iteration takes the minimum of 30 or total_movies in the genre sorted by revenue/budget ratio and returns the information to the User
		'''
		genre = type_to_display

		movies_in_genre = list(GENRE_MOVIE_MAPPER[genre]) # Fetching all the movies in the genre
		num_movies = min(len(movies_in_genre), 30) # Taking the minimum of total movies in the genre and 30 because some genres have less than 30 movies in them.

		movie_details_sorter = [] # List that maintains movie_title and revenue_budget ratio of it so that we can sort on it. Movie details are added as elements in it.

		for movie in movies_in_genre:
			movie_details = MOVIE_DETAILS_MAPPER[movie]
			movie_revenue_budget_ratio = movie_details["revenue_budget_ratio"]
			movie_budget = movie_details["budget"]
			movie_revenue = movie_details["revenue"]

			movie_details_sorter.append((movie, movie_budget, movie_revenue, movie_revenue_budget_ratio))

		if class_to_display == "top10":

			movie_details_sorter.sort(key = lambda x: x[3], reverse=True)
			for movie_index in range(num_movies):
				movie_details_dict = {}
				movie_details = movie_details_sorter[movie_index]

				# Adding only the movie_name, movie_revenue and movie_budget as we don't need the ratio
				movie_details_dict["movie"]    = movie_details[0]
				movie_details_dict["budget"]   = int(movie_details[1]/1e6)
				movie_details_dict["revenue"]  = int(movie_details[2]/1e6)

				plot_data.append(movie_details_dict)

		elif class_to_display == "mid10":

			movie_details_sorter.sort(key = lambda x: x[3], reverse=True)

			mid_index = int(len(movies_in_genre)/2)
			#mid_movies_range = [mid_index - 15, mid_index + 14]

			for movie_index in range(mid_index - 15, mid_index + 14):
				movie_details_dict = {}
				movie_details = movie_details_sorter[movie_index]

				# Adding only the movie_name, movie_revenue and movie_budget as we don't need the ratio
				movie_details_dict["movie"]    = movie_details[0]
				movie_details_dict["budget"]   = int(movie_details[1]/1e6)
				movie_details_dict["revenue"]  = int(movie_details[2]/1e6)

				plot_data.append(movie_details_dict)
		elif class_to_display == "bottom10":

			movie_details_sorter.sort(key = lambda x: x[3])
			for movie_index in range(num_movies):

				movie_details_dict = {}
				movie_details = movie_details_sorter[movie_index]

				# Adding only the movie_name, movie_revenue and movie_budget as we don't need the ratio
				movie_details_dict["movie"]    = movie_details[0]
				movie_details_dict["budget"]   = int(movie_details[1]/1e4)
				movie_details_dict["revenue"]  = int(movie_details[2]/1e4)

				plot_data.append(movie_details_dict)

		return plot_data

def fetchRadarPlotData(type_to_display):
	'''

	'''
	plot_data = pd.DataFrame(columns = ["genre", "budget", "revenue", "popularity", "vote_avg"])

	GROUPED_CATEGORY_TAGS = ["cat1", "cat2", "cat3", "cat4"] # Figure out proper names for these categories

	global CONSOLIDATED_RADAR_DATA

	specific_flag = False

	if type_to_display == "CONSOLIDATED":

		for genre_index, genre in enumerate(ALL_GENRES):

			movies_in_genre = list(GENRE_MOVIE_MAPPER[genre])
			num_movies_in_genre = len(movies_in_genre)
			num_movies = min(len(movies_in_genre), 30)

			total_budget = 0
			total_revenue = 0
			total_popularity = 0
			total_runtime = 0
			total_vote_avg = 0

			for movie in movies_in_genre:
				movie_details = MOVIE_DETAILS_MAPPER[movie]

				total_budget += movie_details["budget"]
				total_revenue += movie_details["revenue"]
				total_popularity += movie_details["popularity"]
				# total_runtime += movie_details["runtime"]
				total_vote_avg += movie_details["runtime"]

			total_budget /= num_movies_in_genre
			total_revenue /= num_movies_in_genre
			total_popularity /= num_movies_in_genre
			total_vote_avg /= num_movies_in_genre

			genre_details = []
			genre_details.append(genre)
			genre_details.append(total_budget)
			genre_details.append(total_revenue)
			genre_details.append(total_popularity)
			genre_details.append(total_vote_avg)

			plot_data.loc[genre_index] = genre_details

		plot_data.loc[genre_index+1] = ["IGNORE", 0, 0, 0, 0]
		CONSOLIDATED_RADAR_DATA = plot_data

	elif type_to_display in GROUPED_CATEGORY_TAGS:

		GENRE_CATEGORY_MAPPER = {
			"cat1": ["Action", "Adventure", "War"],
			"cat2": ["Drama", "Comedy", "Romance"],
			"cat3": ["Science Fiction", "Fantasy", "Animation"],
			"cat4": ["Thriller", "Crime", "Mystery", "Horror",]
		}

		genres = GENRE_CATEGORY_MAPPER[type_to_display]

		for genre_index, genre in enumerate(genres):

			movies_in_genre = list(GENRE_MOVIE_MAPPER[genre]) # Fetching all the movies in the genre
			num_movies = min(len(movies_in_genre), 30) # Taking the minimum of total movies in the genre and 30 because some genres have less than 30 movies in them.
			num_movies_in_genre = len(movies_in_genre)

			total_budget = 0
			total_revenue = 0
			total_popularity = 0
			total_runtime = 0
			total_vote_avg = 0

			for movie in movies_in_genre:
				movie_details = MOVIE_DETAILS_MAPPER[movie]

				total_budget += movie_details["budget"]
				total_revenue += movie_details["revenue"]
				total_popularity += movie_details["popularity"]
				total_vote_avg += movie_details["runtime"]
				#total_runtime += movie_details["runtime"]

			genre_details = []
			genre_details.append(genre)
			genre_details.append(total_budget)
			genre_details.append(total_revenue)
			genre_details.append(total_popularity)
			genre_details.append(total_vote_avg)

			plot_data.loc[genre_index] = genre_details
		plot_data.loc[genre_index+1] = ["IGNORE", 0, 0, 0, 0]

	else:
		specific_flag = True

		genre = type_to_display
		for genre_index, genre_data in CONSOLIDATED_RADAR_DATA.iterrows():
			if genre_data["genre"] == genre:
				plot_data.loc[0] = list(genre_data)

	data = []

	if not specific_flag:
		from sklearn.preprocessing import MinMaxScaler
		scaler = MinMaxScaler()
		genres = plot_data["genre"]
		plot_data = plot_data.drop(columns=["genre"])
		plot_data = scaler.fit_transform(plot_data)

		for index in range(len(genres) - 1):
			genre_dict = {}
			genre_dict["name"] = genres[index]

			genre_axes = []

			genre_axes.append({"axis": "budget", "value": (plot_data[index][0]) * 100})
			genre_axes.append({"axis": "revenue", "value": (plot_data[index][1]) * 100})
			genre_axes.append({"axis": "popularity", "value": plot_data[index][2] * 100})
			genre_axes.append({"axis": "runtime", "value": plot_data[index][3] * 100})

			genre_dict["axes"] = genre_axes
			data.append(genre_dict)

	else:
		genre_dict = {}
		genre_dict["name"] = type_to_display
		genre_axes = []
		index = 0
		genre_axes.append({"axis": "budget", "value": (plot_data["budget"][index]/1e5)})
		genre_axes.append({"axis": "revenue", "value": (plot_data["revenue"][index]/1e5)})
		genre_axes.append({"axis": "popularity", "value": plot_data["popularity"][index] * 10})
		genre_axes.append({"axis": "runtime", "value": plot_data["vote_avg"][index] * 10})

		genre_dict["axes"] = genre_axes
		data.append(genre_dict)
	return data

def get_rb_ratio(row):
	ratio = row
	if ratio >4:
		return 3
	if ratio >1.5:
		return 2
	else:
		return 1

def filter_rb_ratio(data):
	#print("==========In FIlter Funciton=========")
	data = data[(data['year'] > '2010') & (data['year'] <= '2015')]
	ac = 0
	if ac ==1:
		a = data[data['rb_quantile'] <=0.1]
		a['rb_ratio'] = 1

		c = data[(data['rb_quantile'] >0.40) & (data['rb_quantile'] <=0.55)]
		d = data[(data['rb_quantile'] >0.2) & (data['rb_quantile'] <=0.3)]
		e = data[(data['rb_quantile'] >0.65) & (data['rb_quantile'] <=0.8)]
		#print(e.shape[0],d.shape[0],c.shape[0])
		#print("==========")
		# size = data[(data['rb_quantile'] >=0.45) & (data['rb_quantile'] <=0.55)].shape[0]
		# c = data[(data['rb_quantile'] >0.2) & (data['rb_quantile'] <=0.8)].sample(size)
		c['rb_ratio'] = 2
		d['rb_ratio'] = 1
		e['rb_ratio'] = 3

		b = data[data['rb_quantile'] >=0.9]
		b['rb_ratio'] = 3
		# print(b.head(10))
		data = pd.concat([a, c,d,e, b])
	else:
		a = data[(data['rb_quantile'] >0) & (data['rb_quantile'] <=0.33)]
		b = data[(data['rb_quantile'] >0.33) & (data['rb_quantile'] <=0.67)]
		c = data[(data['rb_quantile'] >0.67) & (data['rb_quantile'] <=1)]

		a['rb_ratio'] = 1
		b['rb_ratio'] = 2
		c['rb_ratio'] = 3
		data = pd.concat([a,b,c])
	# print(data.head(5))
	return data

def fetchScatterPlotData(chart_type, type_to_display):

	data = MOVIES_DF
	if type_to_display.upper() != 'CONSOLIDATED':
		data = data[data.name.isin(GENRE_MOVIE_MAPPER[type_to_display])]

	# data['rb_ratio'] = data['revenue'] / data['budget']
	# data['rb_ratio'] = data['rb_ratio'].astype(int)
	# data['rb_ratio'] = data['rb_ratio'].apply(get_rb_ratio)
	data = filter_rb_ratio(data)

	return data
	# return data[data['budget'] > 5].head(400)

def fetchBoxPlotData(chart_type, type_to_display):

	data = MOVIES_DF
	data = filter_rb_ratio(data)
	data['year'] = data['release_date'].apply(lambda x: str(x)[:4])
	if type_to_display.upper() != 'CONSOLIDATED':
		'''
		print(type_to_display)
		print(GENRE_MOVIE_MAPPER.keys())
		print(GENRE_MOVIE_MAPPER[type_to_display][:10])
		print("============================")
		'''
		data = data[data.name.isin(GENRE_MOVIE_MAPPER[type_to_display])]

	year_dict_0 = {}
	year_dict_0['2011'] = list(data[data['year'] == '2011']['popularity'])#[:100]
	year_dict_0['2012'] = list(data[data['year'] == '2012']['popularity'])
	year_dict_0['2013'] = list(data[data['year'] == '2013']['popularity'])
	year_dict_0['2014'] = list(data[data['year'] == '2014']['popularity'])
	year_dict_0['2015'] = list(data[data['year'] == '2015']['popularity'])

	year_dict_1 = {}
	year_dict_1['2011'] = list(data[data['year'] == '2011']['runtime'])#[:100]
	year_dict_1['2012'] = list(data[data['year'] == '2012']['runtime'])
	year_dict_1['2013'] = list(data[data['year'] == '2013']['runtime'])
	year_dict_1['2014'] = list(data[data['year'] == '2014']['runtime'])
	year_dict_1['2015'] = list(data[data['year'] == '2015']['runtime'])

	return [year_dict_0, year_dict_1]

def fetchParallelPlotData(chart_type, type_to_display):

	columns = ['name','rb_ratio','year','budget','revenue','profit','runtime','popularity']#,'no_production_companies',]
	data = filter_rb_ratio(MOVIES_DF)
	data = data[columns]
	# print("=======Parallel========")
	# print(data.head(5))
	if type_to_display.upper() != 'CONSOLIDATED':
		data = data[data.name.isin(GENRE_MOVIE_MAPPER[type_to_display])][columns]
		data = data[(data['year'] > '2010') & (data['year'] <= '2015')]

	else:
		data = PARALLEL_DF

	# sample data on a strategy
	return data#.head(100)

def driver_fetch_data(chart_type, type_to_display, class_to_display=None):
	'''
	This method is the driver function to fetch data for any specific chart type for any data.

	Input:
		chart_type:
			A string keyword which mentions the type of chart to be displayed and therefore allows us to know
			which method to call to retrieve appropriate data.
		type_to_display:
			type_to_display - A string keyword that is used to specify what kind of data is to be
			displayed in the graph.
			Values could be 'CONSOLIDATED' or the name of the genre.
	Output:
		If 'type_to_display' is CONSOLIDATED, we send the aggregated budget/revenue across all
		available genres.
		If 'type_to_display' is the name of the genre, we send the [1-10, 45-55, 90-100] ranged
		values in revenue/budget sorted list.
	'''
	method_mapper_dict = {"circular_biplot": "fetchCircularBiPlotData(type_to_display, class_to_display)", "scatterplot": "fetchScatterPlotData(chart_type, type_to_display)",
							"radar_plot": "fetchRadarPlotData(type_to_display)", "boxplot": "fetchBoxPlotData(chart_type, type_to_display)",
							"parallelplot":"fetchParallelPlotData(chart_type, type_to_display)"}

	method_name = method_mapper_dict[chart_type]

	result = eval(method_name)
	return result;

def initialize_global_vars():
	'''
	This method is used to initialize all the Global variables for the functions_library.py module.
	These Global variables used across this module are fetched from preprocess.py function and used here.
	This is just used as an initializer and is called in the initialize() method at app.py
	The line "return True" is simply added to make sure that all the Global variables are calculated and only then
	the next line in app.py initilize() method is executed.
	'''

	print("Currently initializing global variables")

	global ALL_GENRES
	global GENRE_MOVIE_MAPPER
	global MOVIE_DETAILS_MAPPER
	global MOVIES_DF
	global PARALLEL_DF

	preprocess.preprocess_data()

	ALL_GENRES = preprocess.ALL_GENRES
	GENRE_MOVIE_MAPPER = preprocess.GENRE_MOVIE_MAPPER
	MOVIE_DETAILS_MAPPER = preprocess.MOVIE_DETAILS_MAPPER
	MOVIES_DF = preprocess.MOVIES_DF
	PARALLEL_DF = preprocess.PARALLEL_DF

	global GENRE_BUDGETS
	global GENRE_REVENUES
	global AVG_GENRE_BUDGETS
	global AVG_GENRE_REVENUES

	GENRE_BUDGETS = {}
	GENRE_REVENUES = {}
	AVG_GENRE_BUDGETS = {}
	AVG_GENRE_REVENUES = {}

	for genre in ALL_GENRES:
		GENRE_BUDGETS[genre] = 0
		GENRE_REVENUES[genre] = 0
		AVG_GENRE_BUDGETS[genre] = 0
		AVG_GENRE_REVENUES[genre] = 0

	print("Done with initializing global variables")

	return True