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

import mock
import uuid

from gbpservice.neutron.services.grouppolicy.common import constants
from gbpservice.neutron.services.grouppolicy import config
from gbpservice.neutron.services.grouppolicy.drivers.odl import odl_manager
from gbpservice.neutron.services.grouppolicy.drivers.odl import odl_mapping
from gbpservice.neutron.services.grouppolicy.drivers import resource_mapping
from gbpservice.neutron.services.grouppolicy import plugin as g_plugin
from gbpservice.neutron.tests.unit.services.grouppolicy import (
    test_grouppolicy_plugin as test_plugin)
from neutron.plugins.ml2 import plugin as ml2_plugin
from neutron.tests.unit.ml2 import test_ml2_plugin

TENANT_ID = 'aaaabbbbccccaaaabbbbccccaaaabbbb'
TENANT_UUID = 'aaaabbbb-cccc-aaaa-bbbb-ccccaaaabbbb'

ACTION_1_ID = '1111aaaa-1111-1111-1111-1111bbbb1111'
ACTION_1_NAME = 'fake_name_for_action_1'
ACTION_1_DESC = 'Fake policy action 1'
ACTION_1_TYPE = constants.GP_ACTION_ALLOW

ACTION_2_ID = '1111aaaa-1111-2222-1111-1111bbbb1111'
ACTION_2_NAME = 'fake_name_for_action_2'
ACTION_2_DESC = 'Fake policy action 2'
ACTION_2_TYPE = constants.GP_ACTION_ALLOW

# ACTION_3 is used for negative testing as this action
# is not supported at the moment
ACTION_3_ID = '1111aaaa-1111-3333-1111-1111bbbb1111'
ACTION_3_NAME = 'fake_name_for_action_3'
ACTION_3_DESC = 'Fake policy action 3'
ACTION_3_TYPE = constants.GP_ACTION_REDIRECT

CLASSIFIER_1_ID = '1111aaaa-2222-1111-1111-1111bbbb1111'
CLASSIFIER_1_NAME = 'fake_name_for_classifier_1'
CLASSIFIER_1_DESC = 'Fake policy classifier 1'
CLASSIFIER_1_PROTOCOL = 'tcp'
CLASSIFIER_1_PORT = '321'
CLASSIFIER_1_DIRECTION = 'bi'
CLASSIFIER_1_DEFINITION_ID = '4250ab32-e8b8-445a-aebb-e1bd2cdd291f'

CLASSIFIER_2_ID = '1111aaaa-2222-2222-1111-1111bbbb1111'
CLASSIFIER_2_NAME = 'fake_name_for_classifier_2'
CLASSIFIER_2_DESC = 'Fake policy classifier 2'
CLASSIFIER_2_PROTOCOL = 'tcp'
CLASSIFIER_2_PORT = '123'
CLASSIFIER_2_DIRECTION = 'in'
CLASSIFIER_2_DEFINITION_ID = '4250ab32-e8b8-445a-aebb-e1bd2cdd291f'

CLASSIFIER_3_ID = '1111aaaa-2222-3333-1111-1111bbbb1111'
CLASSIFIER_3_NAME = 'fake_name_for_classifier_3'
CLASSIFIER_3_DESC = 'Fake policy classifier 3'
CLASSIFIER_3_PROTOCOL = 'icmp'
CLASSIFIER_3_DIRECTION = 'bi'
CLASSIFIER_3_DEFINITION_ID = '79c6fdb2-1e1a-4832-af57-c65baf5c2335'

RULE_1_ID = '1111aaaa-3333-1111-1111-1111bbbb1111'
RULE_1_NAME = 'fake_name_for_policy_rule_1'
RULE_1_DESC = 'Fake policy rule 1'

RULE_2_ID = '1111aaaa-3333-2222-1111-1111bbbb1111'
RULE_2_NAME = 'fake_name_for_policy_rule_2'
RULE_2_DESC = 'Fake policy rule 2'

RULE_3_ID = '1111aaaa-3333-3333-1111-1111bbbb1111'
RULE_3_NAME = 'fake_name_for_policy_rule_3'
RULE_3_DESC = 'Fake policy rule 3'

RULE_SET_1_ID = '1111aaaa-4444-1111-1111-1111bbbb1111'
RULE_SET_1_NAME = 'fake_name_for_rule_set_1'
RULE_SET_1_DESC = 'Fake policy rule set 1'

RULE_SET_2_ID = '1111aaaa-4444-2222-1111-1111bbbb1111'
RULE_SET_2_NAME = 'fake_name_for_rule_set_2'
RULE_SET_2_DESC = 'Fake policy rule set 2'

L3P_ID = '2222bbbb-1111-1111-1111-1111cccc1111'
L3P_NAME = 'fake_name_for_l3_policy'
L3P_DESC = 'Fake L3 policy'

NETWORK_ID = '2222bbbb-2222-1111-1111-1111cccc1111'
NETWORK_NAME = 'fake_name_for_network'

L2P_ID = '2222bbbb-3333-1111-1111-1111cccc1111'
L2P_NAME = 'fake_name_for_l2_policy'
L2P_DESC = 'Fake L2 policy'

SUBNET_ID = '2222bbbb-4444-1111-1111-1111cccc1111'
SUBNET_CIDR = '10.10.1.0/24'
SUBNET_GATEWAY_IP = '10.10.1.1'

GROUP_ID = '2222bbbb-5555-1111-1111-1111cccc1111'
GROUP_NAME = 'fake_name_for_ptg'
GROUP_DESC = 'Fake PTG'

PORT_ID = '3333cccc-1111-1111-1111-1111dddd1111'
PORT_MAC = 'fa:33:33:11:11:11'
PORT_IP = '10.10.1.11'
NEUTRON_PORT_ID = 'tap3333cccc-11'

POLICY_TARGET_ID = '3333cccc-2222-1111-1111-1111dddd1111'
POLICY_TARGET_NAME = 'fake_name_for_policy_target'
POLICY_TARGET_DESC = 'Fake Policy Target'

FAKE_CONTEXT = 'fake_context'
FAKE_PLUGIN_CONTEXT = 'fake_plugin_context'

class FakeCorePlugin(object):
    """ A fake plugin to simulate the ML2 plugin

    This plugin provides a minimum set of methods that
    will be used during testing
    """

    def __init__(self):
        self._networks = {}
        self._subnets = {}
        self._ports = {}

    def add_network(self, net_id, net):
        self._networks[net_id] = net

    def get_network(self, plugin_context, net_id):
        return self._networks[net_id]

    def add_subnet(self, subnet_id, subnet):
        self._subnets[subnet_id] = subnet

    def get_subnet(self, plugin_context, subnet_id):
        return self._subnets[subnet_id]

    def add_port(self, port_id, port):
        self._ports[port_id] = port

    def get_port(self, plugin_context, port_id):
        return self._ports[port_id]

class FakeGBPPlugin(object):
    """ A fake plugin to simulate the GBP plugin

    This plugin provides a minimum set of methods that
    will be used during testing
    """

    def __init__(self):
        self._l3ps = {}
        self._l2ps = {}
        self._ptgs = {}
        self._pts = {}
        self._classifiers = {}
        self._actions = {}
        self._rules = {}
        self._rule_sets = {}

    def add_l3_policy(self, l3p_id, l3p):
        self._l3ps[l3p_id] = l3p

    def get_l3_policy(self, plugin_context, l3p_id):
        return self._l3ps[l3p_id]

    def add_l2_policy(self, l2p_id, l2p):
        self._l2ps[l2p_id] = l2p

    def get_l2_policy(self, plugin_context, l2p_id):
        return self._l2ps[l2p_id]

    def add_policy_target_group(self, ptg_id, ptg):
        self._ptgs[ptg_id] = ptg

    def get_policy_target_group(self, plugin_context, ptg_id):
        return self._ptgs[ptg_id]

    def add_policy_target(self, pt_id, pt):
        self._pts[pt_id] = pt

    def get_policy_target(self, plugin_context, pt_id):
        return self._pts[pt_id]

    def add_policy_classifier(self, classifier_id, classifier):
        self._classifiers[classifier_id] = classifier

    def get_policy_classifier(self, plugin_context, classifier_id):
        return self._classifiers[classifier_id]

    def add_policy_action(self, action_id, action):
        self._actions[action_id] = action

    def get_policy_action(self, plugin_context, action_id):
        return self._actions[action_id]

    def add_policy_rule(self, rule_id, rule):
        self._rules[rule_id] = rule

    def get_policy_rule(self, plugin_context, rule_id):
        return self._rules[rule_id]

    def add_policy_rule_set(self, rule_set_id, rule_set):
        self._rule_sets[rule_set_id] = rule_set

    def get_policy_rule_set(self, plugin_context, rule_set_id):
        return self._rule_sets[rule_set_id]

class OdlMappingTestCase(
        test_plugin.GroupPolicyPluginTestCase):
    """ Base test case for ODL mapping driver testing

    Set up the common testing environment
    """

    def setUp(self):
        config.cfg.CONF.set_override('policy_drivers',
                                     ['implicit_policy', 'odl'],
                                     group='group_policy')
        super(OdlMappingTestCase, self).setUp(
            core_plugin=test_ml2_plugin.PLUGIN_NAME)

        self.fake_core_plugin = FakeCorePlugin()
        self.fake_gbp_plugin = FakeGBPPlugin()
        self.driver = odl_mapping.OdlMappingDriver.get_initialized_instance()

        self.fake_gbp_plugin.add_policy_action(
            ACTION_1_ID,
            {
                'id': ACTION_1_ID,
                'tenant_id': TENANT_ID,
                'name': ACTION_1_NAME,
                'description': ACTION_1_DESC,
                'action_type': ACTION_1_TYPE,
            }
        )

        self.fake_gbp_plugin.add_policy_action(
            ACTION_2_ID,
            {
                'id': ACTION_2_ID,
                'tenant_id': TENANT_ID,
                'name': ACTION_2_NAME,
                'description': ACTION_2_DESC,
                'action_type': ACTION_2_TYPE,
            }
        )

        self.fake_gbp_plugin.add_policy_action(
            ACTION_3_ID,
            {
                'id': ACTION_3_ID,
                'tenant_id': TENANT_ID,
                'name': ACTION_3_NAME,
                'description': ACTION_3_DESC,
                'action_type': ACTION_3_TYPE,
            }
        )

        self.fake_gbp_plugin.add_policy_classifier(
            CLASSIFIER_1_ID,
            {
                'id': CLASSIFIER_1_ID,
                'tenant_id': TENANT_ID,
                'name': CLASSIFIER_1_NAME,
                'description': CLASSIFIER_1_DESC,
                'protocol': CLASSIFIER_1_PROTOCOL,
                'port_range': CLASSIFIER_1_PORT,
                'direction': CLASSIFIER_1_DIRECTION
            }
        )

        self.fake_gbp_plugin.add_policy_classifier(
            CLASSIFIER_2_ID,
            {
                'id': CLASSIFIER_2_ID,
                'tenant_id': TENANT_ID,
                'name': CLASSIFIER_2_NAME,
                'description': CLASSIFIER_2_DESC,
                'protocol': CLASSIFIER_2_PROTOCOL,
                'port_range': CLASSIFIER_2_PORT,
                'direction': CLASSIFIER_2_DIRECTION
            }
        )

        self.fake_gbp_plugin.add_policy_classifier(
            CLASSIFIER_3_ID,
            {
                'id': CLASSIFIER_3_ID,
                'tenant_id': TENANT_ID,
                'name': CLASSIFIER_3_NAME,
                'description': CLASSIFIER_3_DESC,
                'protocol': CLASSIFIER_3_PROTOCOL,
                'direction': CLASSIFIER_3_DIRECTION
            }
        )

        self.fake_gbp_plugin.add_policy_rule(
            RULE_1_ID,
            {
                'id': RULE_1_ID,
                'tenant_id': TENANT_ID,
                'name': RULE_1_NAME,
                'description': RULE_1_DESC,
                'policy_classifier_id': CLASSIFIER_1_ID,
                'policy_actions': [ACTION_1_ID]
            }
        )

        self.fake_gbp_plugin.add_policy_rule(
            RULE_2_ID,
            {
                'id': RULE_2_ID,
                'tenant_id': TENANT_ID,
                'name': RULE_2_NAME,
                'description': RULE_2_DESC,
                'policy_classifier_id': CLASSIFIER_2_ID,
                'policy_actions': [ACTION_2_ID]
            }
        )

        self.fake_gbp_plugin.add_policy_rule(
            RULE_3_ID,
            {
                'id': RULE_3_ID,
                'tenant_id': TENANT_ID,
                'name': RULE_3_NAME,
                'description': RULE_3_DESC,
                'policy_classifier_id': CLASSIFIER_1_ID,
                'policy_actions': [ACTION_1_ID, ACTION_2_ID]
            }
        )

        self.fake_gbp_plugin.add_policy_rule_set(
            RULE_SET_1_ID,
            {
                'id': RULE_SET_1_ID,
                'tenant_id': TENANT_ID,
                'name': RULE_SET_1_NAME,
                'description': RULE_SET_1_DESC,
                'policy_rules': [RULE_1_ID]
            }
        )

        self.fake_gbp_plugin.add_policy_rule_set(
            RULE_SET_2_ID,
            {
                'id': RULE_SET_2_ID,
                'tenant_id': TENANT_ID,
                'name': RULE_SET_2_NAME,
                'description': RULE_SET_2_DESC,
                'policy_rules': [RULE_2_ID]
            }
        )

        self.fake_gbp_plugin.add_l3_policy(
            L3P_ID,
            {
                'id': L3P_ID,
                'tenant_id': TENANT_ID,
                'name': L3P_NAME,
                'description': L3P_DESC
            }
        )

        self.fake_core_plugin.add_network(
            NETWORK_ID,
            {
                'id': NETWORK_ID,
                'name': NETWORK_NAME
            }
        )

        self.fake_gbp_plugin.add_l2_policy(
            L2P_ID,
            {
                'id': L2P_ID,
                'tenant_id': TENANT_ID,
                'name': L2P_NAME,
                'description': L2P_DESC,
                'l3_policy_id': L3P_ID,
                'network_id': NETWORK_ID

            }
        )

        self.fake_core_plugin.add_subnet(
            SUBNET_ID,
            {
                'id': SUBNET_ID,
                'cidr': SUBNET_CIDR,
                'network_id': NETWORK_ID,
                'gateway_ip': SUBNET_GATEWAY_IP
            }
        )

        self.fake_gbp_plugin.add_policy_target_group(
            GROUP_ID,
            {
                'id': GROUP_ID,
                'tenant_id': TENANT_ID,
                'name': GROUP_NAME,
                'description': GROUP_DESC,
                'l2_policy_id': L2P_ID,
                'subnets': [SUBNET_ID],
                'provided_policy_rule_sets': [RULE_SET_1_ID],
                'consumed_policy_rule_sets': [RULE_SET_2_ID]
            }
        )

        self.fake_core_plugin.add_port(
            PORT_ID,
            {
                'id': PORT_ID,
                'mac_address': PORT_MAC,
                'fixed_ips': [
                    {
                        'ip_address': PORT_IP,
                        'subnet_id': SUBNET_ID
                    }
                ],
                'network_id': NETWORK_ID
            }
        )

        self.fake_gbp_plugin.add_policy_target(
            POLICY_TARGET_ID,
            {
                'id': POLICY_TARGET_ID,
                'tenant_id': TENANT_ID,
                'name': POLICY_TARGET_NAME,
                'description': POLICY_TARGET_DESC,
                'policy_target_group_id': GROUP_ID,
                'port_id': PORT_ID
            }
        )

class ExternalSegmentTestCase(OdlMappingTestCase):
    """ Test case related with external segment operations

    Currently, ODL cannot handle any external segment operations,
    and should throw an exception in these cases.
    """

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

    def _test_exception_handling(self, method):
        func = getattr(self.driver, method)
        self.assertRaises(
            odl_mapping.ExternalSegmentNotSupportedOnOdlDriver,
            func,
            FAKE_CONTEXT
        )

    def test_create_external_segment_precommit(self):
        self._test_exception_handling('create_external_segment_precommit')

    def test_update_external_segment_precommit(self):
        self._test_exception_handling('update_external_segment_precommit')

    def test_delete_external_segment_precommit(self):
        self._test_exception_handling('delete_external_segment_precommit')

    def test_create_external_policy_precommit(self):
        self._test_exception_handling('create_external_policy_precommit')

    def test_update_external_policy_precommit(self):
        self._test_exception_handling('update_external_policy_precommit')

    def test_delete_external_policy_precommit(self):
        self._test_exception_handling('delete_external_policy_precommit')

    def test_create_nat_pool_precommit(self):
        self._test_exception_handling('create_nat_pool_precommit')

    def test_update_nat_pool_precommit(self):
        self._test_exception_handling('update_nat_pool_precommit')

    def test_delete_nat_pool_precommit(self):
        self._test_exception_handling('delete_nat_pool_precommit')

class PolicyTargetTestCase(OdlMappingTestCase):
    """ Test case for policy target operations
    """

    def setUp(self):
        super(PolicyTargetTestCase, self).setUp()
        self.context = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_target(
                FAKE_CONTEXT,
                POLICY_TARGET_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )

    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_l2_policy')
    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_policy_target_group')
    @mock.patch.object(ml2_plugin.Ml2Plugin, 'get_port')
    @mock.patch.object(odl_manager.OdlManager, 'register_endpoints')
    @mock.patch.object(resource_mapping.ResourceMappingDriver,
                  'create_policy_target_postcommit')
    def test_create_policy_target_postcommit(
            self,
            mock_create_policy_target_commit,
            mock_register_endpoints,
            mock_get_port,
            mock_get_policy_target_group,
            mock_get_l2_policy):

        # core_plugin and gbp_plugin are mocked and simulated by
        # the fake core plugin and fake gbp plugin
        mock_get_port.side_effect = self.fake_core_plugin.get_port
        mock_get_policy_target_group.side_effect = (
            self.fake_gbp_plugin.get_policy_target_group)
        mock_get_l2_policy.side_effect = self.fake_gbp_plugin.get_l2_policy
        ep = {
            "endpoint-group": GROUP_ID,
            "l2-context": L2P_ID,
            "l3-address": [
                {
                    "ip-address": PORT_IP,
                    "l3-context": L3P_ID
                }
            ],
            "mac-address": PORT_MAC,
            "port-name": NEUTRON_PORT_ID,
            "tenant": TENANT_UUID
        }

        self.driver.create_policy_target_postcommit(self.context)
        mock_create_policy_target_commit.assert_called_once_with(self.context)
        mock_register_endpoints.assert_called_once_with([ep])

    def test_update_policy_target_precommit(self):
        self.assertRaises(
            odl_mapping.UpdatePTNotSupportedOnOdlDriver,
            getattr(self.driver, 'update_policy_target_precommit'),
            self.context
        )

    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_l2_policy')
    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_policy_target_group')
    @mock.patch.object(ml2_plugin.Ml2Plugin, 'get_port')
    @mock.patch.object(odl_manager.OdlManager, 'unregister_endpoints')
    @mock.patch.object(resource_mapping.ResourceMappingDriver,
                       'delete_policy_target_postcommit')
    def test_delete_policy_target_postcommit(
            self,
            mock_delete_policy_target_commit,
            mock_unregister_endpoints,
            mock_get_port,
            mock_get_policy_target_group,
            mock_get_l2_policy):

        # core_plugin and gbp_plugin are mocked and simulated by
        # the fake core plugin and fake gbp plugin
        mock_get_port.side_effect = self.fake_core_plugin.get_port
        mock_get_policy_target_group.side_effect = (
            self.fake_gbp_plugin.get_policy_target_group)
        mock_get_l2_policy.side_effect = self.fake_gbp_plugin.get_l2_policy
        ep = {
            "l2": [
                {
                    "l2-context": L2P_ID,
                    "mac-address": PORT_MAC
                }
            ],
            "l3": [
                {
                    "ip-address": PORT_IP,
                    "l3-context": L3P_ID
                }
            ],
        }

        self.driver.delete_policy_target_postcommit(self.context)
        mock_delete_policy_target_commit.assert_called_once_with(self.context)
        mock_unregister_endpoints.assert_called_once_with([ep])

class L3PolicyTestCase(OdlMappingTestCase):
    """ Test case for L3 policy operations
    """

    def setUp(self):
        super(L3PolicyTestCase, self).setUp()
        self.context = mock.Mock(
            current=self.fake_gbp_plugin.get_l3_policy(
                FAKE_CONTEXT,
                L3P_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )

    @mock.patch.object(odl_manager.OdlManager, 'create_update_l3_context')
    def test_create_l3_policy_postcommit(
            self,
            mock_create_update_l3_context):

        l3ctx = {
            "id": L3P_ID,
            "name": L3P_NAME,
            "description": L3P_DESC
        }

        self.driver.create_l3_policy_postcommit(self.context)
        mock_create_update_l3_context.assert_called_once_with(
            TENANT_UUID, l3ctx)

    def test_update_l3_policy_precommit(self):
        self.assertRaises(
            odl_mapping.UpdateL3PolicyNotSupportedOnOdlDriver,
            getattr(self.driver, 'update_l3_policy_precommit'),
            self.context
        )

    @mock.patch.object(odl_manager.OdlManager, 'delete_l3_context')
    def test_delete_l3_policy_postcommit(
            self,
            mock_delete_l3_context):

        l3ctx = {
            "id": L3P_ID,
        }

        self.driver.delete_l3_policy_postcommit(self.context)
        mock_delete_l3_context.assert_called_once_with(TENANT_UUID, l3ctx)

class L2PolicyTestCase(OdlMappingTestCase):
    """ Test case for L2 policy operations
    """
    def setUp(self):
        super(L2PolicyTestCase, self).setUp()
        self.context = mock.Mock(
            current=self.fake_gbp_plugin.get_l2_policy(
                FAKE_CONTEXT,
                L2P_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )

    @mock.patch.object(ml2_plugin.Ml2Plugin, 'get_network')
    @mock.patch.object(odl_manager.OdlManager,
                       'create_update_l2_flood_domain')
    @mock.patch.object(odl_manager.OdlManager,
                       'create_update_l2_bridge_domain')
    @mock.patch.object(resource_mapping.ResourceMappingDriver,
                       'create_l2_policy_postcommit')
    def test_create_l2_policy_postcommit(
            self,
            mock_create_l2_policy_postcommit,
            mock_create_update_l2_bridge_domain,
            mock_create_update_l2_flood_domain,
            mock_get_network):

        # core_plugin is mocked and simulated by the fake core plugin
        mock_get_network.side_effect = self.fake_core_plugin.get_network
        l2bd = {
            "id": L2P_ID,
            "name": L2P_NAME,
            "description": L2P_DESC,
            "parent": L3P_ID
        }
        l2fd = {
            "id": NETWORK_ID,
            "name": NETWORK_NAME,
            "parent": L2P_ID
        }

        self.driver.create_l2_policy_postcommit(self.context)
        mock_create_l2_policy_postcommit.assert_called_once_with(self.context)
        mock_create_update_l2_bridge_domain.assert_called_once_with(
            TENANT_UUID, l2bd)
        mock_create_update_l2_flood_domain.assert_called_with(TENANT_UUID,
                                                              l2fd)

    def test_update_l2_policy_precommit(self):
        self.assertRaises(
            odl_mapping.UpdateL2PolicyNotSupportedOnOdlDriver,
            getattr(self.driver, 'update_l2_policy_precommit'),
            self.context
        )

    @mock.patch.object(odl_manager.OdlManager, 'delete_l2_flood_domain')
    @mock.patch.object(odl_manager.OdlManager, 'delete_l2_bridge_domain')
    @mock.patch.object(resource_mapping.ResourceMappingDriver,
                       'delete_l2_policy_postcommit')
    def test_delete_l2_policy_postcommit(
            self,
            mock_delete_l2_policy_postcommit,
            mock_delete_l2_bridge_domain,
            mock_delete_l2_flood_domain):

        l2bd = {
            "id": L2P_ID,
        }
        l2fd = {
            "id": NETWORK_ID,
        }

        self.driver.delete_l2_policy_postcommit(self.context)
        mock_delete_l2_policy_postcommit.assert_called_once_with(self.context)
        mock_delete_l2_bridge_domain.assert_called_once_with(TENANT_UUID,
                                                             l2bd)
        mock_delete_l2_flood_domain.assert_called_with(TENANT_UUID, l2fd)

class PolicyTargetGroupTestCase(OdlMappingTestCase):
    """ Test case for policy target group operations
    """

    def setUp(self):
        super(PolicyTargetGroupTestCase, self).setUp()
        self.context = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_target_group(
                FAKE_CONTEXT,
                GROUP_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )

    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_policy_action')
    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_policy_classifier')
    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_policy_rule')
    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'get_policy_rule_set')
    @mock.patch.object(ml2_plugin.Ml2Plugin, 'get_subnet')
    @mock.patch.object(odl_manager.OdlManager, 'create_update_subnet')
    @mock.patch.object(odl_manager.OdlManager, 'create_update_endpoint_group')
    @mock.patch.object(odl_manager.OdlManager, 'create_update_contract')
    @mock.patch.object(resource_mapping.ResourceMappingDriver,
                       'create_policy_target_group_postcommit')
    def test_create_policy_target_postcommit(
            self,
            mock_create_policy_target_group_postcommit,
            mock_create_update_contract,
            mock_create_update_endpoint_group,
            mock_create_update_subnet,
            mock_get_subnet,
            mock_get_policy_rule_set,
            mock_get_policy_rule,
            mock_get_policy_classifier,
            mock_get_policy_action):

        # core_plugin and gbp_plugin are mocked and simulated by
        # the fake core plugin and fake gbp plugin
        mock_get_subnet.side_effect = self.fake_core_plugin.get_subnet
        mock_get_policy_rule_set.side_effect = (self.fake_gbp_plugin.
                                                get_policy_rule_set)
        mock_get_policy_rule.side_effect = (self.fake_gbp_plugin.
                                            get_policy_rule)
        mock_get_policy_classifier.side_effect = (self.fake_gbp_plugin.
                                                  get_policy_classifier)
        mock_get_policy_action.side_effect = (self.fake_gbp_plugin.
                                              get_policy_action)

        provided_contract_id = (
            uuid.uuid3(uuid.NAMESPACE_DNS, RULE_SET_1_NAME).urn[9:])
        provided_contract = {
            "id": provided_contract_id,
            "clause": [
                {
                    "name": RULE_SET_1_NAME,
                    "subject-refs": [RULE_SET_1_NAME]
                }
            ],
            "subject": [
                {
                    "name": RULE_SET_1_NAME,
                    "rule": [
                        {
                            "name": RULE_1_NAME,
                            "classifier-ref": [
                                {
                                    "name": CLASSIFIER_1_NAME + '-sourceport'
                                },
                                {
                                    "name": CLASSIFIER_1_NAME + '-destport'
                                }
                            ]
                        }

                    ]
                }
            ]
        }
        consumed_contract_id = (
            uuid.uuid3(uuid.NAMESPACE_DNS, RULE_SET_2_NAME).urn[9:])
        consumed_contract = {
            "id": consumed_contract_id,
            "clause": [
                {
                    "name": RULE_SET_2_NAME,
                    "subject-refs": [RULE_SET_2_NAME]
                }
            ],
            "subject": [
                {
                    "name": RULE_SET_2_NAME,
                    "rule": [
                        {
                            "name": RULE_2_NAME,
                            "classifier-ref": [
                                {
                                    "name": CLASSIFIER_2_NAME + '-sourceport',
                                    "direction": 'out'
                                },
                                {
                                    "name": CLASSIFIER_2_NAME + '-destport',
                                    "direction": 'in'
                                },
                            ]
                        }

                    ]
                }
            ]
        }
        epg = {
            "id": GROUP_ID,
            "name": GROUP_NAME,
            "network-domain": SUBNET_ID,
            "provider-named-selector": {
                "name": 'Contract-' + provided_contract_id,
                "contract": provided_contract_id
            },
            "consumer-named-selector": {
                "name": 'Contract-' + consumed_contract_id,
                "contract": consumed_contract_id
            }
        }
        odl_subnet = {
            "id": SUBNET_ID,
            "ip-prefix": SUBNET_CIDR,
            "parent": NETWORK_ID,
            "virtual-router-ip": SUBNET_GATEWAY_IP
        }

        self.driver.create_policy_target_group_postcommit(self.context)
        mock_create_policy_target_group_postcommit.assert_called_once_with(
            self.context)
        mock_create_update_contract.assert_any_call(TENANT_UUID,
                                                    provided_contract)
        mock_create_update_contract.assert_any_call(TENANT_UUID,
                                                    consumed_contract)
        mock_create_update_endpoint_group.assert_called_once_with(TENANT_UUID,
                                                                  epg)
        mock_create_update_subnet.assert_called_once_with(TENANT_UUID,
                                                          odl_subnet)

    def test_update_policy_target_group_precommit(self):
        self.assertRaises(
            odl_mapping.UpdatePTGNotSupportedOnOdlDriver,
            getattr(self.driver, 'update_policy_target_group_precommit'),
            self.context
        )

    @mock.patch.object(odl_mapping.OdlMappingDriver, '_cleanup_subnet')
    @mock.patch.object(odl_manager.OdlManager, 'delete_endpoint_group')
    @mock.patch.object(odl_manager.OdlManager, 'delete_subnet')
    def test_delete_policy_target_group_postcommit(
            self,
            mock_delete_subnet,
            mock_delete_endpoint_group,
            mock__cleanup_subnet):

        odl_subnet = {
            "id": SUBNET_ID
        }
        epg = {
            "id": GROUP_ID
        }

        self.driver.delete_policy_target_group_postcommit(self.context)
        mock_delete_subnet.assert_called_once_with(TENANT_UUID, odl_subnet)
        mock_delete_endpoint_group.assert_called_once_with(TENANT_UUID, epg)
        mock__cleanup_subnet.assert_called_once_with(
            self.context._plugin_context, SUBNET_ID, None)

class PolicyActionTestCase(OdlMappingTestCase):
    """ Test case for policy action operations
    """

    def setUp(self):
        super(PolicyActionTestCase, self).setUp()
        self.context_1 = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_action(
                FAKE_CONTEXT,
                ACTION_1_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )
        self.context_3 = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_action(
                FAKE_CONTEXT,
                ACTION_3_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )

    def test_create_policy_action_precommit(self):
        # No exception should be raised
        self.driver.create_policy_action_precommit(self.context_1)

        # Exception should be raised only when ACTION is redirect
        self.assertRaises(
            odl_mapping.RedirectActionNotSupportedOnOdlDriver,
            getattr(self.driver, 'create_policy_action_precommit'),
            self.context_3
        )

    @mock.patch.object(resource_mapping.ResourceMappingDriver,
                       'create_policy_action_postcommit')
    def test_create_policy_action_postcommit(
            self,
            mock_create_policy_action_postcommit):

        # Exception should be raised only when ACTION is redirect
        self.driver.create_policy_action_postcommit(self.context_1)
        mock_create_policy_action_postcommit.assert_called_once_with(
            self.context_1)
        self.assertRaises(
            odl_mapping.OnlyAllowActionSupportedOnOdlDriver,
            getattr(self.driver, 'create_policy_action_postcommit'),
            self.context_3
        )

    def test_update_policy_action_precommit(self):
        self.assertRaises(
            odl_mapping.UpdatePolicyActionNotSupportedOnOdlDriver,
            getattr(self.driver, 'update_policy_action_precommit'),
            self.context_1
        )

    @mock.patch.object(resource_mapping.ResourceMappingDriver,
                       'delete_policy_action_postcommit')
    def test_delete_policy_action_postcommit(
            self,
            mock_delete_policy_action_postcommit):
        self.driver.delete_policy_action_postcommit(self.context_1)
        mock_delete_policy_action_postcommit.assert_called_once_with(
            self.context_1)

class PolicyClassifierTestCase(OdlMappingTestCase):
    """ Test case for policy classifier operations
    """

    def setUp(self):
        super(PolicyClassifierTestCase, self).setUp()
        self.context_1 = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_classifier(
                FAKE_CONTEXT,
                CLASSIFIER_1_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )
        self.context_3 = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_classifier(
                FAKE_CONTEXT,
                CLASSIFIER_3_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )

    @mock.patch.object(odl_manager.OdlManager, 'create_classifier')
    def test_create_policy_classifier_postcommit(
            self,
            mock_create_classifier):

        # Ensure two classifiers are created in ODL for TCP
        classifier_instance_1_dest = {
            "classifier-definition-id": CLASSIFIER_1_DEFINITION_ID,
            "name": CLASSIFIER_1_NAME + "-destport",
            "parameter-value": [
                {
                    "name": "type",
                    "string-value": CLASSIFIER_1_PROTOCOL
                },
                {
                    "name": "destport",
                    "int-value": CLASSIFIER_1_PORT
                }
            ]
        }
        classifier_instance_1_source = {
            "classifier-definition-id": CLASSIFIER_1_DEFINITION_ID,
            "name": CLASSIFIER_1_NAME + "-sourceport",
            "parameter-value": [
                {
                    "name": "type",
                    "string-value": CLASSIFIER_1_PROTOCOL
                },
                {
                    "name": "sourceport",
                    "int-value": CLASSIFIER_1_PORT
                }
            ]
        }
        self.driver.create_policy_classifier_postcommit(self.context_1)
        mock_create_classifier.assert_any_call(TENANT_UUID,
                                               classifier_instance_1_source)
        mock_create_classifier.assert_any_call(TENANT_UUID,
                                               classifier_instance_1_dest)
        mock_create_classifier.reset_mock()

        # only one classifier for ICMP
        classifier_instance_3 = {
            "classifier-definition-id": CLASSIFIER_3_DEFINITION_ID,
            "name": CLASSIFIER_3_NAME,
            "parameter-value": [
                {
                    "name": "proto",
                    "int-value": 1
                }
            ]
        }
        self.driver.create_policy_classifier_postcommit(self.context_3)
        mock_create_classifier.assert_called_once_with(TENANT_UUID,
                                                       classifier_instance_3)

    def test_update_policy_classifier_precommit(self):
        self.assertRaises(
            odl_mapping.UpdateClassifierNotSupportedOnOdlDriver,
            getattr(self.driver, 'update_policy_classifier_precommit'),
            self.context_1
        )

    @mock.patch.object(odl_manager.OdlManager, 'delete_classifier')
    def test_delete_policy_classifier_postcommit(
            self,
            mock_delete_classifier):

        # Ensure both classifiers are deleted for TCP/UDP
        classifier_instance_1_dest = {
            "name": CLASSIFIER_1_NAME + "-destport"
        }
        classifier_instance_1_source = {
            "name": CLASSIFIER_1_NAME + "-sourceport"
        }
        self.driver.delete_policy_classifier_postcommit(self.context_1)
        mock_delete_classifier.assert_any_call(TENANT_UUID,
                                               classifier_instance_1_source)
        mock_delete_classifier.assert_any_call(TENANT_UUID,
                                               classifier_instance_1_dest)
        mock_delete_classifier.reset_mock()

        # Ensure only one classifier is deleted for ICMP
        classifier_instance_3 = {
            "name": CLASSIFIER_3_NAME,
        }
        self.driver.delete_policy_classifier_postcommit(self.context_3)
        mock_delete_classifier.assert_called_once_with(TENANT_UUID,
                                                       classifier_instance_3)

class PolicyRuleTestCase(OdlMappingTestCase):
    """ Test case for policy rule operations
    """
    def setUp(self):
        super(PolicyRuleTestCase, self).setUp()
        self.context_1 = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_rule(
                FAKE_CONTEXT,
                RULE_1_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )
        self.context_3 = mock.Mock(
            current=self.fake_gbp_plugin.get_policy_rule(
                FAKE_CONTEXT,
                RULE_3_ID
            ),
            _plugin_context=FAKE_PLUGIN_CONTEXT,
            _plugin=self.fake_gbp_plugin
        )

    def test_create_policy_rule_precommit(self):
        # No exception should be raised
        self.driver.create_policy_rule_precommit(self.context_1)

        # Ensure exception be raised only when multiple actions appear
        self.assertRaises(
            odl_mapping.ExactlyOneActionPerRuleIsSupportedOnOdlDriver,
            getattr(self.driver, 'create_policy_rule_precommit'),
            self.context_3
        )

    def test_update_policy_rule_precommit(self):
        self.assertRaises(
            odl_mapping.PolicyRuleUpdateNotSupportedOnOdlDriver,
            getattr(self.driver, 'update_policy_rule_precommit'),
            self.context_1
        )

class DHCPTestCase(OdlMappingTestCase):
    """ Test case for DHCP related operations
    """

    def setUp(self):
        super(DHCPTestCase, self).setUp()
        mock_sql = mock.Mock()
        mock_sql.query.return_value = mock_sql
        mock_sql.join.return_value = mock_sql
        mock_sql.filter.return_value = mock_sql
        mock_sql.first.return_value = {
            "id": GROUP_ID,
            "name": GROUP_NAME
        }
        self.plugin_context = mock.Mock(
            session=mock_sql
        )
        self.port = {
            "id": PORT_ID,
            "network_id": NETWORK_ID,
            "tenant_id": TENANT_ID
        }

    @mock.patch.object(g_plugin.GroupPolicyPlugin, 'create_policy_target')
    @mock.patch.object(odl_mapping.OdlMappingDriver, '_port_is_owned')
    @mock.patch.object(ml2_plugin.Ml2Plugin, '_get_subnets_by_network')
    def test_create_dhcp_policy_target_if_needed(
            self,
            mock_get_subnets_by_network,
            mock_port_is_owned,
            mock_create_policy_target):

        mock_get_subnets_by_network.return_value = [
            {
                "id": SUBNET_ID
            }
        ]
        attrs = {
            "policy_target": {
                "tenant_id": TENANT_ID,
                "name": 'dhcp-' + GROUP_ID,
                "description": "Implicitly created DHCP policy target",
                "policy_target_group_id": GROUP_ID,
                "port_id": PORT_ID
            }
        }

        # Test that gbp_plugin.create_policy_target was NOT called
        mock_port_is_owned.return_value = True
        self.driver.create_dhcp_policy_target_if_needed(self.plugin_context,
                                                        self.port)
        self.assertFalse(mock_create_policy_target.called,
                         'Failed not to create DHCP PT when not needed')

        # Test that gbp_plugin.create_policy_target was called indeed
        mock_port_is_owned.return_value = False
        self.driver.create_dhcp_policy_target_if_needed(self.plugin_context,
                                                        self.port)
        mock_create_policy_target.assert_called_once_with(self.plugin_context,
                                                          attrs)


import random as rnd

class Household(object):
    def __init__(self, id, address, ruler):
        self.id = id
        self.pv = None
        self.address = address  # aka public key - address, wallet
        self.ruler = ruler  # rulerAgent
        self.status = None

        self.loyalty = 1.0  # used for DA price formation

    def getBasicInfo(self):
        return "Household id is {0}, RA: {1}, \
                address in Ethereum blockchain:{2}".format(
            self.id, self.ruler.id, self.address
        )

    def checkPV(self):
        if self.pv:
            return True
        else:
            return False

    def getHomeProbStatus(self, hour):
        # True - at home, False - away
        rnd.seed()
        actual_prob = rnd.random()
        fun_prob = [
            0.92,
            0.891,
            0.859,
            0.823,
            0.784,
            0.741,
            0.696,
            0.649,
            0.6,
            0.5,
            0.55,
            0.599,
            0.646,
            0.691,
            0.734,
            0.773,
            0.809,
            0.841,
            0.87,
            0.894,
            0.92,
            0.9,
            0.9,
            0.96,
        ]

        if actual_prob > fun_prob[hour]:
            state = False
        else:
            state = True

        self.ruler.homeStatus = state

        return state

class RulerAgent(object):
    def __init__(self, id, batteryCap):
        self.id = id
        self.batteryCap = batteryCap
        self.batteryBalance = 0

        self.tokenBalance = 0
        self.moneyBalance = 0

        self.consumption = 0

        self.production = 0
        self.pvSq = None

        self.homeStatus = None

        self.avgConsRateList = []

    def getLastHourProduction(self, currentHour, weather):
        # production for prev hour (23-0)
        rnd.seed()
        if currentHour > 5 and currentHour < 21:
            if self.pvSq > 0:
                val = self.pvSq * weather[1][1] * rnd.randint(1, 11)
                return 100 * int(val)
            else:
                return 0
        else:
            return 0

    def getNextHourConsumption(self, currentHour, weather):
        # consumption for next hour (0-1)
        status = self.homeStatus
        rnd.seed()
        if status:
            if currentHour > 7 and currentHour < 23:
                return int(rnd.randint(5, 20) * (weather[1][0])) * 100  # 1-20
            else:
                return int(rnd.randint(2, 8) * weather[1][0]) * 100  # 1-20
        else:
            return int(rnd.randint(0, 5) * weather[1][0]) * 100  # 0-5


import pandas as pd
from bs4 import BeautifulSoup
import requests

headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36', "Upgrade-Insecure-Requests": "1","DNT": "1","Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Language": "en-US,en;q=0.5","Accept-Encoding": "gzip, deflate"}
base_url = 'https://www.tripadvisor.com/'

all_reviews = []
AMOUNT_REVIEWS_HOTEL = 10

def get_soup(url):
    r = requests.get(url, headers=headers)#, proxies=proxies)
    content = r.content
    return BeautifulSoup(content)

def get_hotels(no_page):
    page_request_text = ''

    if (no_page > 1):
        page_request_text = '-oa' + str(no_page * 30)

    soup = get_soup( base_url + 'Hotels-g186338' + page_request_text + '-London_England-Hotels.html')

    for d in soup.findAll('a', href=True, attrs={'class':'property_title prominent'}):
        all_reviews.extend(get_review_from_hotels(d['href']))

def get_review_from_hotels(hotel_url):
    reviews_hotel = []
    next_page = True
    to_replace = "Reviews-"
    review_counter = 0
    while(next_page):
        review_counter += 5
        soup = get_soup( base_url + hotel_url)
        review = []

        for review_div in soup.findAll('div', attrs={'class':'cWwQK MC R2 Gi z Z BB dXjiy'}):
            print('going trough review')
            review_q = review_div.find('q', attrs={'class':'XllAv H4 _a'})
            review_text = review_q.find('span').text
            review.append(review_text)
            review_bubble = review_div.find('span', attrs={'class':'ui_bubble_rating'})['class'][1]
            if review_bubble == 'bubble_40':
                review_label = 'Positive'
            elif review_bubble == 'bubble_50':
                review_label = 'Positive'
            else:
                review_label = 'Negative'
            review.append(review_label)
            reviews_hotel.append(review)
            review = []

        replacement = 'Reviews-or' + str(review_counter) + '-'
        hotel_url.replace(to_replace, replacement)
        to_replace = replacement

        if len(reviews_hotel) >= AMOUNT_REVIEWS_HOTEL:
            return reviews_hotel

        if soup.find('span', attrs={'class':'ui_button nav next primary disabled'}):
            next_page = False

def scrape():
    get_hotels(1)
    df = pd.DataFrame(all_reviews, columns=['Review','Label'])
    return df


import random

class Perceptron:
    def __init__(self, numInputs, learningRate):
        self.weights = [None] * (numInputs + 1) #+1 for bias
        self.lr = learningRate

        #randomising weights
        for weight in self.weights:
            weight = random.uniform(-1,1)

    def train(self, inputs, target):
        guess = self.guess(inputs)
        error = target - guess

        #Recalculating weights
        for inp, weight in zip(inputs, self.weights):
            weight += error * inp * lr

    def guess(self, inputs):
        for inp, weight in zip(inputs, self.weights):
            sum += inp * weight
        return this.activate(sum) #Returning -1 or 1

    #Activation function
    def activate(self, sum):
        #Sign function
        if(sum > 0):
            return 1
        else:
            return -1


from question import Question
import csv
import random
class Game:
    def __init__(self, filepath, is_double_score):
        self.questions = self._read_questions(filepath, is_double_score)
    categories = dict()

    def _read_questions(self, filepath, is_double_jepoardy):
        with open(filepath) as f:
            reader = csv.reader(f)
            questions =[]
            for row in reader:
                #design a constructor that takes category, question, answer
                q = Question(row[0].upper(), row[1], row[2])
                if q.category in self.categories.keys():
                    self.categories[q.category] += 1
                else:
                    self.categories[q.category] = 1
                q.set_score(self.categories[q.category], is_double_jepoardy)
                questions.append(q)
            return questions

    def make_file(self, filename):
        if not filename.endswith(".txt"):
            filename += ".txt"
        random.shuffle(self.questions)
        divider = "=================================\n"
        d_space = "\n\n"
        with open(filename, 'w') as out_file:
            out_file.write(divider)
            for q in self.questions:
                out_file.write(q.category + " : $" +str(q.score) + d_space)
                out_file.write(q.question + d_space)
                out_file.write(q.answer + "\n")
                out_file.write(divider)


from .__init__ import *

def divisionToIntFunc(maxA=25, maxB=25):
    a = random.randint(1, maxA)
    b = random.randint(1, maxB)

    divisor = a * b
    dividend = random.choice([a, b])

    problem = f"{divisor}/{dividend} = "
    solution = int(divisor / dividend)
    return problem, solution

int_division = Generator("Easy Division", 13, "a/b=", "c", divisionToIntFunc)


"""
Here are your instructions:

Modify the classFactory.py source code so that the DataRow class returned by the build_row function has another method:

retrieve(self, curs, condition=None)

self is (as usual) the instance whose method is being called, curs is a database cursor on an existing database connection, and condition (if present) is a string of condition(s) which must be true of all received rows.

The retrieve method should be a generator, yielding successive rows of the result set until it is completely exhausted. Each row should be a next object of type DataRow.
"""

"""
classFactory: function to return tailored classes
"""

def build_row(table, cols):
    """Build a class that creates instances of specific rows"""
    class DataRow:
        """Generic data row class, specialized by surrounding function"""
        def __init__(self, data):
            """Uses data and column names to inject attributes"""
            assert len(data)==len(self.cols)
            for colname, dat in zip(self.cols, data):
                setattr(self, colname, dat)

        def __repr__(self):
            return "{0}_record({1})".format(self.table, ", ".join(["{0!r}".format(getattr(self, c)) for c in self.cols]))

        def retrieve(self, curs, condition=None):
            if condition == None:
                string = ("SELECT {0} FROM {1}".format(",".join(cols.split()),table))
            elif isinstance(condition, str): #one string, one condition
                string = ("SELECT {0} FROM {1} WHERE {2}".format(",".join(cols.split()),table, condition))
            else: #condition is a list - multiple conditions
                string = ("SELECT {0} FROM {1} WHERE {2}".format(",".join(cols.split()),table, " AND ".join(condition)))

            curs.execute (string)
            for t in curs.fetchall():
                yield DataRow(t)

    DataRow.table = table
    DataRow.cols = cols.split()
    return DataRow


""" https://leetcode.com/problems/ugly-number-iii/
Given x, the ugly number less or equal than x can be found by inclusion-exclusion principle:
    multiple of a or b or c - multiple of lcm(a,b) - multiple of lcm(a,c) - multiple of lcm(b, c) + multiple of lcm(a, b, c)

x = 10, a = 2, b = 4, c = 6, lcm(a,b) = 4, lcm(a,c) = 6, lcm(b,c) = 12, lcm(a, b, c) = 12
a: 2 4 6 8 10
b: 4 8
c: 6
"""
from header import *

class Solution:
    def nthUglyNumber(self, n: int, a: int, b: int, c: int) -> int:
        def fn(x):
            # return True if there are n ugly numbers less than x
            cnt = x//a + x//b + x//c - x//lcm(a, b) - x//lcm(b, c) - x//lcm(a, c) + x//lcm(a, b, c)
            return cnt>=n

        a, b, c = sorted([a, b, c])
        l = min(a, b, c)
        r = min(a, b, c) * n + 1

        while l<r:
            m = (l+r)//2
            if fn(m): r = m
            else: l = m+1
        return l


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

from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.core.exceptions import MultipleObjectsReturned
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _

from import_export.admin import ExportActionModelAdmin, ImportExportMixin
import impersonate

from . import resources
from .forms import GradeAdminForm, EventAdminForm, CupAdminForm, ParticipationAdminForm, AscentInlineFormSet, UserCreateForm
from .mixins import AutoSelectGymMixin, LimitOptionsMixin
from .models import Cup, Position, AgeGroup, Event, Route, Gym, Sector, Grade, Points, Participation, Ascent, UserProfile

# INLINES

class UserProfileInline(admin.StackedInline):
    model = UserProfile

class UserParticipationInlineReadOnly(LimitOptionsMixin, admin.TabularInline):
    """
    This InlineAdmin displays all event participations that the current user
    has no privileges to edit for. Nothing can be added, changed or deleted
    here.
    """
    model = Participation
    extra = 0
    can_delete = False
    verbose_name_plural = "Wettkampfteilnahmen anderer Hallen"

    # TODO: make start and end times editable to the editors when the event in
    # question needs this info
    exclude = ['start', 'end']

    def has_add_permission(self, request):
        return False

    readonly_fields = [
        'event',
        'category',
        'gender',
        'age',
        'city',
        'confirmed'
    ]

    def get_queryset(self, request):
        qs = super(UserParticipationInlineReadOnly, self).get_queryset(request)
        # Only display those participation objects that belong to a gym that
        # the current user is *not* a manager of.
        return qs.exclude(event__gym__managers=request.user)

class UserParticipationInline(LimitOptionsMixin, admin.TabularInline):
    """
    This InlineAdmin displays all event participations that the current user
    (or superuser) can edit and delete.
    """
    model = Participation
    extra = 0

    def get_queryset(self, request):
        qs = super(UserParticipationInline, self).get_queryset(request)
        # Display all participations for this person if the current user has
        # admin privileges
        if request.user.is_superuser:
            return qs
        # But only display those participation objects that belong to a gym that
        # the current user is a manager of if they're not an admin.
        return qs.filter(event__gym__managers=request.user)

class PositionInlineAdmin(admin.TabularInline):
    """
    Inline model admin for selecting who scored how during the finals of an
    event.
    """
    model = Position
    extra = 0
    ordering = ["participation__category", "position"]

    class ParticipationChoiceField(forms.ModelChoiceField):
        """
        I once tried to use a AutoSelect2Field but due to the way the Django
        clones the DOM when inserting new inline forms, the widget breaks.

        Right now it works, but only slowly because the query is not cached and
        needs to be retrieved several times once a few finalists are entered.
        But generally the event is done by then so long loading times are not
        a big deal.

        Don't use Participation.__unicode__() as a label because that would
        generate a lot of database queries for events and, also, the user
        should think that participants are selected and not participation
        objects.
        """
        def label_from_instance(self, obj):
            return obj.participant.__unicode__()

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        """ Limit choices to users who participated in that event. """
        if db_field.name == "participation":
            kwargs["queryset"] = Participation.objects.\
                select_related("participant").filter(
                    event=request._obj_
                ).order_by('participant__first_name', 'participant__last_name')
            return self.ParticipationChoiceField(**kwargs)

class AscentInlineAdmin(admin.TabularInline):
    model = Ascent
    raw_id_fields = ['route']
    readonly_fields = ('grade', '_route__event')
    formset = AscentInlineFormSet
    extra = 0

    def get_fields(self, request, obj=None):
        """
        Display Ascent fields depending on whether this event is a bouldering
        or setter's competition.
        """
        fields = ['route', 'grade']
        if obj and obj.event:
            if obj.event.is_bouldering_contest:
                fields.append('style')
            else:
                fields.append(('comment', 'rating', 'grade_correct'))
        fields.append('_route__event')
        return fields

# FILTERS

class EventGymListFilter(admin.SimpleListFilter):
    """ Filter through event's gym relation. """
    title = "Halle"
    parameter_name = "event__gym"

    def lookups(self, request, model_admin):
        gyms = Gym.objects.all()
        if not request.user.is_superuser:
            gyms = gyms.filter(managers=request.user)
        return [(g.id, g.name) for g in gyms]

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(event__gym_id=self.value())

class ParticipationYearFilter(admin.SimpleListFilter):
    title = "Jahr"
    parameter_name = "year"

    def lookups(self, request, model_admin):
        qs = model_admin.get_queryset(request)
        dates = qs.distinct("event").values_list("event__begin", flat=True).order_by()
        return sorted(set((d.year, d.year) for d in dates), reverse=True)

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(event__begin__year=self.value())

# MODELADMINS

class SectorAdmin(AutoSelectGymMixin, LimitOptionsMixin, admin.ModelAdmin):
    search_fields = ('name',)
    list_display = ('name', 'gym', 'sort')
    list_editable = ('sort',)
    list_filter = (
        ('gym', admin.RelatedOnlyFieldListFilter),
    )

    def get_queryset(self, request):
        """
        Limit Sectors to those that belong to the current user unless they're
        an admin.
        """
        qs = super(SectorAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(gym__managers=request.user)

class AgeGroupAdmin(admin.ModelAdmin):
    list_display = ('name', 'min_age', 'max_age')

class GymAdmin(admin.ModelAdmin):
    filter_horizontal = ["managers"]
    list_display = ('name', 'manager_list', 'city', 'event_num',)

    def manager_list(self, obj):
        """
        Displays a list of names of all those people who are in charge of
        this gym and the related objects.
        """
        return ", ".join([u.get_full_name() for u in obj.managers.all()])
    manager_list.short_description = "verantwortlich"

    def event_num(self, obj):
        """ Returns the number of events that are related to this gym. """
        return obj.event_set.count()
    event_num.short_description = "Wettkämpfe"

    def get_queryset(self, request):
        """
        Limit gyms to those that belong to the current user unless they're
        an admin.
        """
        qs = super(GymAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(managers=request.user)

    def has_add_permission(self, request):
        # Only administrators are able to add new gyms.
        return request.user.is_superuser

    def has_change_permission(self, request, obj=None):
        if obj is not None:
            # The change form of a particular object is requested and only
            # displayed to superusers or managers of that particular gym.
            return request.user.is_superuser or \
                    obj.managers.filter(pk=request.user.pk).exists()
        else:
            # The changelist is requested and only shown to superusers or
            # people who are managers of at least one gym. Returning `False`
            # will make the changelist not even viewable.
            return request.user.is_superuser or \
                    self.model.objects.filter(managers=request.user).exists()

    def has_delete_permission(self, *args, **kwargs):
        # This works exactly the same as `has_change_permission` except that
        # obj=None equals the "delete selected" admin action on the
        # changelist.

        # There's a bug in Django in the way that no object level permissions
        # are checked when running an admin action.
        # https://code.djangoproject.com/ticket/11383
        return self.has_change_permission(*args, **kwargs)

    def changelist_view(self, request, extra_context=None):
        # Skip the changelist if there's only one object to edit and go
        # directly to the changeform.
        # This is inspired by django-single-model-admin.
        info = '{0}_{1}'.format(self.model._meta.app_label, getattr(self.model._meta, 'model_name'))

        try:
            instance = self.get_queryset(request).get()

        except self.model.DoesNotExist:
            return redirect(reverse('admin:{0}_add'.format(info)))

        except MultipleObjectsReturned:
            return super(GymAdmin, self).changelist_view(request, extra_context=extra_context)

        else:
            return redirect(reverse('admin:{0}_change'.format(info), args=[instance.pk]))

class CupAdmin(admin.ModelAdmin):
    form = CupAdminForm
    list_display = ["name", "events"]

    def events(self, obj):
        event_names = [e.name for e in obj.event_set.all()]
        return format_html("" + "
".join(event_names) + "")
    events.short_description = "Zugeordnete Wettkämpfe"

    def has_delete_permission(self, request, obj=None):
        # Regular users should not be able to delete Cup objects in general.
        # Right now, there's no ownership implemented in the Cup model because
        # no one (besides myself) needed to create a cup. If a user needs to
        # have a Cup deleted, they have to contact an admin.
        if request.user.is_superuser:
            return True
        return False

class RouteAdmin(LimitOptionsMixin, ImportExportMixin, ExportActionModelAdmin):
    list_display = ['number', 'colored_grade', 'sector', 'points', 'event', 'name', 'creator', ]
    list_display_links = ('number',)
    list_select_related = ('event', 'sector', 'grade',)
    search_fields = ['number', 'name']
    date_hierarchy = 'created'
    ordering = ('event', 'number')
    readonly_fields = ('created',)

    list_filter = [
        EventGymListFilter,
        ('event', admin.RelatedOnlyFieldListFilter),
        ('grade', admin.RelatedOnlyFieldListFilter),
        ('sector', admin.RelatedOnlyFieldListFilter),
    ]

    fields = (
        ('number', 'name'),
        'event',
        'grade',
        'sector',
        'points',
        ('created', 'creator'),
    )
    inlines = [AscentInlineAdmin]
    # What gets im- and exported through django-import-export is defined in
    # this class
    resource_class = resources.RouteResource
    import_template_name = "admin/import_export/route_import.html"

    def get_formsets_with_inlines(self, request, obj=None):
        # Don't display AscentInlineAdmin on blank forms
        for inline in self.get_inline_instances(request, obj):
            if isinstance(inline, AscentInlineAdmin) and obj is None:
                continue
            yield inline.get_formset(request, obj), inline

    def get_form(self, request, obj=None, **kwargs):
        # Attach object to request in order to change field options
        # accordingly later on.
        if obj:
            request._route_ = obj
        return super(RouteAdmin, self).get_form(request, obj, **kwargs)

    def get_queryset(self, request):
        # Limit Routes to those that belong to the current user unless they're
        # an admin.
        qs = super(RouteAdmin, self).get_queryset(request).prefetch_related('ascent_set')
        if request.user.is_superuser:
            return qs
        return qs.filter(event__gym__managers=request.user)

class GradeAdmin(LimitOptionsMixin, admin.ModelAdmin):
    list_display = ['name', 'color', 'gym', 'relaxed', 'power', 'sort']
    list_editable = ['sort', 'color']
    list_filter = ['gym', 'relaxed', 'power']
    ordering = ['gym', 'sort']
    search_fields = ['name', 'gym__name']
    form = GradeAdminForm

    def get_queryset(self, request):
        """
        Limit Grades to those that belong to the current user unless they're an
        admin.
        """
        qs = super(GradeAdmin, self).get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(gym__managers=request.user)

class ParticipationAdmin(LimitOptionsMixin, ImportExportMixin, ExportActionModelAdmin):
    list_display = ('participant', 'confirmed', 'category', 'event')
    list_editable = ('confirmed',)
    list_filter = [
        EventGymListFilter,
        ParticipationYearFilter,
        'confirmed',
        'category',
        ('event', admin.RelatedOnlyFieldListFilter)
    ]
    search_fields = [
        'participant__first_name',
        'participant__last_name',
        'participant__email'
    ]

    inlines = [AscentInlineAdmin]
    readonly_fields = ("datetime",)
    form = ParticipationAdminForm
    resource_class = resources.ParticipationResource

    def get_fields(self, request, obj=None):
        # Don't do anything irregular when a new object is created
        if not obj:
            return super(ParticipationAdmin, self).get_fields(request)

        fields = ['participant',]

        # When event does not rely on confirming participations, don't display
        # the respective checkbox
        if obj.event.needs_confirmation:
            fields.append(('event', 'confirmed'))
        else:
            fields.append('event')

        # When event does not use a separation of levels, don't display the
        # level select widget
        if obj.event.uses_levels:
            fields.append('category')

        # Only display start/end times when the corresponding mode is in use
        if obj.event.mode == Event.ALL_YOU_CAN_TOP:
            fields.append(('start', 'end'))

        fields.extend(('age', 'gender', 'city', 'datetime'))

        return fields

    def get_form(self, request, obj=None, **kwargs):
        """ Hide `category` field if there's no separation in levels. """
        self.exclude = []
        if obj and not obj.event.uses_levels:
            self.exclude.append('category')
        return super(ParticipationAdmin, self).get_form(request, obj, **kwargs)

    def get_changelist_form(self, request, **kwargs):
        return ParticipationAdminForm

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "participant":
            kwargs["queryset"] = User.objects.order_by("first_name", "last_name") # NOQA
        return super(ParticipationAdmin, self).\
            formfield_for_foreignkey(db_field, request, **kwargs)

    def get_queryset(self, request):
        """
        Limit participations to those that belong to the current user's event
        unless they're an admin.
        """
        qs = super(ParticipationAdmin, self).get_queryset(request).prefetch_related('ascent_set', 'event')
        if not request.user.is_superuser:
            return qs.filter(event__gym__managers=request.user)
        return qs

class EventAdmin(LimitOptionsMixin, admin.ModelAdmin):
    form = EventAdminForm
    list_display = [
        'name', 'begin', 'participant_count', 'public',
        'needs_confirmation', 'uses_levels',
        'boulder_count'
    ]
    date_hierarchy = 'begin'
    ordering = ["-begin"]
    inlines = [PositionInlineAdmin]
    filter_horizontal = ['age_groups']
    list_filter = (
        ('gym', admin.RelatedOnlyFieldListFilter),
        'cup'
    )
    fieldsets = [
        [None, {
            'fields': ['name',
                      ('begin', 'end'),
                      'begin_checkin',
                      'poster',
                      'description',
                      ('gym', 'cup'),
          ]
        }],
        ('Erweiterte Einstellungen', {
            'fields': (
                'is_bouldering_contest',
                'hide_boulder_ranking',
                'uses_levels',
                'needs_confirmation',
                'max_participants',
                'age_groups',
                'mode',
            ), 'classes': ('collapse',),
        }),
        ('Sichtbarkeit', {
            'fields': (
                'public',
                'sandboxed',
            ), 'classes': ('collapse',),
        }),
    ]

    class Media:
        # Include jQuery script that handles hiding and displaying fields
        # according to which mode the event runs (i.e. setter's competition,
        # bouldering competition)
        js = ("js/boulderdb.admin.js",)

    def get_queryset(self, request):
        """
        Limits events to those that belong to the current user's gym unless
        they're an admin.
        """
        qs = super(EventAdmin, self).get_queryset(request)
        if not request.user.is_superuser:
            qs = qs.filter(gym__managers=request.user)
        return qs.prefetch_related(
            'boulders', 'boulders__grade', 'boulders__sector')

    def get_form(self, request, obj=None, **kwargs):
        form = super(EventAdmin, self).get_form(request, obj, **kwargs)

        # Automatically select the first gym that the user is a manager of
        try:
            form.base_fields["gym"].initial = Gym.objects.filter(managers=request.user).first()
        except IndexError:
            pass

        # add instance to request for processing through inline formsets,
        # because at least PositionInline needs to filter the queryset of
        # users down to only those that partook in the event
        request._obj_ = obj

        return form

    def participant_count(self, obj):
        p = obj.participants.count()
        s = "%s" % p

        if obj.needs_confirmation:
            confirmed_p = obj.participants.filter(
                participation__confirmed=True).count()
            s = s + u" (%s bestätigt)" % confirmed_p

        if obj.max_participants:
            s = s + " von max. %s" % obj.max_participants

        return s
    participant_count.short_description = "Teilnehmende"

    def boulder_count(self, obj):
        return obj.boulders.count()
    boulder_count.short_description = "Boulder"

class UserProfileAdmin(UserAdmin):
    """
    Custom user admin that not only displays information about the user object
    but also on the related profile and all of the user's event
    participations.
    """
    list_display = ('full_name', 'email', 'birthday', 'is_staff', 'impersonate',)
    list_display_links = ('full_name', 'email')
    list_select_related = ('profile',)
    add_form = UserCreateForm

    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('first_name', 'last_name', 'password1', 'password2'),
        }),
    )

    def full_name(self, obj):
        return obj.get_full_name()
    full_name.short_description = 'Name'

    def birthday(self, obj):
        return obj.profile.birthday
    birthday.short_description = 'Geburtsdatum'
    birthday.oder_field = 'profile__birthday'

    def impersonate(self, obj):
        if obj in self.users_impersonable:
            url = reverse('impersonate-start', kwargs={'uid': obj.pk})
            return format_html(u"{} verkörpern", url, obj.first_name)
        else:
            return "(nicht erlaubt)"
    impersonate.short_description = 'als … ausgeben'

    def get_list_display(self, request):
        # Store a list of all users that can be impersonated
        self.users_impersonable = impersonate.helpers.users_impersonable(request)
        return self.list_display

    def get_fieldsets(self, request, obj=None):
        # Prevent permission escalation
        if request.user.is_superuser:
            return super(UserProfileAdmin, self).get_fieldsets(request, obj)

        safe_fieldset = (
            (
                None, {'fields': ('username', 'password')}
            ),
            (
                _('Personal info'), {'fields': ('first_name', 'last_name', 'email')}
            )
        )
        return safe_fieldset

    def get_inline_instances(self, request, obj=None):
        # No need to seperate between events that the user is manager of and
        # other events when the user is a superuser; just let them edit all.
        all_inlines = [
            UserProfileInline,
            UserParticipationInline,
            UserParticipationInlineReadOnly
        ]
        superuser_inlines = [
            UserProfileInline,
            UserParticipationInline
        ]

        if request.user.is_superuser:
            self.inlines = superuser_inlines
        else:
            self.inlines = all_inlines
        return super(UserProfileAdmin, self).get_inline_instances(request)

class PointsAdmin(admin.ModelAdmin):
    # TODO: Add PointsResource to export the previous year's points and import
    # them for the next year easily. Creating them one by one is too much
    # work.
    list_filter = ["cup"]
    list_display = ["cup", "position", "points"]
    model = Points
    list_editable = ("points",)

admin.site.register(AgeGroup, AgeGroupAdmin)
admin.site.register(Cup, CupAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(Grade, GradeAdmin)
admin.site.register(Gym, GymAdmin)
admin.site.register(Participation, ParticipationAdmin)
admin.site.register(Route, RouteAdmin)
admin.site.register(Points, PointsAdmin)
admin.site.register(Sector, SectorAdmin)

# Unregister Django's own UserAdmin because we need a modified version to
# display the full name etc.
admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin)

admin.site.site_header = "Willkommen auf blocsport.de/admin"
admin.site.site_title = "Blocsport-Verwaltung"
admin.site.index_title = "Blocsport-Verwaltung"


from django.contrib import admin

from advice_on_evidence.models import CommonCauseIssues, DisputedIssues, SkeletalAdviceOnEvidenceCore, Notices, Witness

class CommonCauseIssuesAdmin(admin.StackedInline):
    model = CommonCauseIssues
    extra = 0
    fields = ('title', 'summary_tags',)
    list_display = ('title', 'summary_tags',)
    suit_classes = 'suit-tab suit-tab-commoncauseissues'

class WitnessAdmin(admin.StackedInline):
    model = Witness
    extra = 0
    fields = ('witness', 'intention', 'when_order',)
    list_display = ('witness',)
    suit_classes = 'suit-tab suit-tab-witness'

class Notices(admin.StackedInline):
    fieldsets = [
        ('Rule 36(10) notices / deliverables / papers', {
            'description': 'Consider whether the notice is required',
            'classes': ('collapse', 'closed'),
            'fields': ['deliver_rule_36_10',
                       'rule_36_10_documents',
                       'rule_36_10_reason']
        }),
        ('Rule 35(9) notices / deliverables / papers', {
            'description': 'Consider whether the notice is required',
            'classes': ('collapse', 'closed'),
            'fields': ['deliver_rule_35_9',
                       'rule_35_9_documents',
                       'rule_35_9_reason']
        }),
        ('Section 22 of Act 25 of 1965 notices / deliverables / papers', {
            'description': 's22 of the Civil Proceedings Amendment Act of 1965 - be specific',
            'classes': ('collapse', 'closed'),
            'fields': ['deliver_s_22_25_1965',
                       's_22_25_1965_documents',
                       's_22_25_1965_reason', ]
        }),
        ('Section 30 of Act 25 of 1965 notices / deliverables / papers', {
            'description': 's30 of the Civil Proceedings Amendment Act of 1965 - be specific',
            'classes': ('collapse', 'closed'),
            'fields': ['deliver_s_30_25_1965',
                       's_30_25_1965_documents',
                       's_30_25_1965_reason', ]
        }),
        ###########
        ('Rule 35(3) notices / deliverables / papers', {
            'description': 'Consider whether the notice is required',
            'classes': ('collapse', 'closed'),
            'fields': ['deliver_rule_35_3',
                       'rule_35_3_documents',
                       'rule_35_3_reason', ]
        }),
        ('Rule 36(9) notices / deliverables / papers', {
            'description': 'Consider whether the notice is required',
            'classes': ('collapse', 'closed'),
            'fields': ['deliver_rule_36_9',
                       'rule_36_9_documents',
                       'rule_36_9_reason', ]
        }),
        ('Other notices / deliverables / papers', {
            'description': 'May be other Rules which you want to refer ...',
            'classes': ('collapse', 'closed'),
            'fields': ['other_documents','other_notices', ]
        }),

    ]

    # suit_form_tabs = (
    #     ('general', 'General'),
    #     ('cities', 'Cities'),
    #     ('flag', 'Flag'),
    #     ('info', 'Info on tabs')
    # )

    """
    'deliver_rule_36_10',
    'rule_36_10_documents',
    'rule_36_10_reason',

    'deliver_rule_35_9',
    'rule_35_9_documents',
    'rule_35_9_reason',

    'deliver_s_22_25_1965',
    's_22_25_1965_documents',
    's_22_25_1965_reason',

    'deliver_s_30_25_1965',
    's_30_25_1965_documents',
    's_30_25_1965_reason',

    'deliver_rule_35_3',
    'rule_35_3_documents',
    'rule_35_3_reason',

    'deliver_rule_36_9',
    'rule_36_9_documents',
    'rule_36_9_reason',

    'other_notices',

    """
    model = Notices
    extra = 0
    # fields = ('deliver_rule_36_10',
    #           'rule_36_10_documents',
    #           'rule_36_10_reason',
    #
    #           'deliver_rule_35_9',
    #           'rule_35_9_documents',
    #           'rule_35_9_reason',
    #
    #           'deliver_s_22_25_1965',
    #           's_22_25_1965_documents',
    #           's_22_25_1965_reason',
    #
    #           'deliver_s_30_25_1965',
    #           's_30_25_1965_documents',
    #           's_30_25_1965_reason',
    #
    #           'deliver_rule_35_3',
    #           'rule_35_3_documents',
    #           'rule_35_3_reason',
    #
    #           'deliver_rule_36_9',
    #           'rule_36_9_documents',
    #           'rule_36_9_reason',
    #
    #           'other_notices')

    list_display = ('deliver_rule_36_10',
                    'rule_36_10_documents',
                    'rule_36_10_reason',

                    'deliver_rule_35_9',
                    'rule_35_9_documents',
                    'rule_35_9_reason',

                    'deliver_s_22_25_1965',
                    's_22_25_1965_documents',
                    's_22_25_1965_reason',

                    'deliver_s_30_25_1965',
                    's_30_25_1965_documents',
                    's_30_25_1965_reason',

                    'deliver_rule_35_3',
                    'rule_35_3_documents',
                    'rule_35_3_reason',

                    'deliver_rule_36_9',
                    'rule_36_9_documents',
                    'rule_36_9_reason',

                    'other_notices',)

    suit_classes = 'suit-tab suit-tab-notices'

class DisputedIssuesAdmin(admin.StackedInline):
    model = DisputedIssues
    extra = 0
    fields = ('title',
              'summary_tags',
              'request_discovery',
              'request_documents',
              'party_bearing_onus',
              'how_to_prove',
              'tactics_by_opponent',
              'investigative_steps',
              'conclusion',)

    list_display = ('title',
                    'request_discovery',
                    'request_documents',
                    )
    suit_classes = 'suit-tab suit-tab-disputedissues'

@admin.register(SkeletalAdviceOnEvidenceCore)
class SkeletalAdviceOnEvidenceCoreAdmin(admin.ModelAdmin):
    inlines = [CommonCauseIssuesAdmin, DisputedIssuesAdmin, Notices, WitnessAdmin,]
    list_select_related = True
    search_fields = ['file_name', ]
    # readonly_fields = ['file_name',]

    fieldsets = [
        (None, {
            'classes': ('suit-tab', 'suit-tab-general',),
            'fields': ['file_name', 'brief_summary', ]
        }),

    ]

    suit_form_tabs = (('general', 'Our File'),
                      ('commoncauseissues', 'Common Cause Issues'),
                      ('disputedissues', 'Disputed Issues'),
                      ('notices', 'Notices'),
                      ('witness', 'Witnesses')
                      )


import matplotlib
import matplotlib.pyplot as plt
import numpy as np
matplotlib.rc('text', usetex = True)
import pylab
import sys
import os
from multiplypoints import *
def cm2inch(*tupl):
    inch = 2.54
    if isinstance(tupl[0], tuple):
        return tuple(i/inch for i in tupl[0])
    else:
        return tuple(i/inch for i in tupl)

#inputfilename = os.path.join(sys.argv[1], sys.argv[2])
#outputfilename = os.path.join(sys.argv[1], sys.argv[3])
#inputfilename = "D:/results/invprop_bad/InverseProportionBad_sample_960_0.txt"
inputfilename = "D:/results/switchingobs_common_mcmnf_revised/SwitchingObservations_sample_0.txt"

t, x, y = np.loadtxt(inputfilename, delimiter = ' ', usecols=(0,1,2), unpack=True, dtype=float)

t, x, y = multx(t), multy(x), multy(y)

from pylab import *

f = plt.figure(num=None, figsize=cm2inch((12,9)), dpi=200)
plt.subplots_adjust(left=0.06, bottom=0.07, right=0.98, top=0.95, wspace=0.1)
ax = plt.subplot(111)

ls_x = (0, ()) #solid
ls_y =  (0, (1, 1)) #dots

params = {
            'color' : 'black',
            'linewidth' : 1.5,
            }

ax.plot(t, x, **params, linestyle=ls_x, label='$x_t$')
ax.plot(t, y, **params, linestyle=ls_y, label='$y_t$')

#ax.set_ylim(-10.0, 10.0)
ax.legend()
plt.tight_layout()
plt.show()


# Given an object/dictionary with keys and values that consist of both strings and integers, design an algorithm to calculate and return the sum of all of the numeric values.
# For example, given the following object/dictionary as input:
# {
#   "cat": "bob",
#   "dog": 23,
#   19: 18,
#   90: "fish"
# }
# Your algorithm should return 41, the sum of the values 23 and 18.

def int_sum(dictionary):
    # if type(dictionary) is int:
    #     return dictionary

    # int_values = []
    sum_value = 0

    for x in dictionary.values():
        if type(x) is int:
            # int_values.append(x)
            sum_value += x

    # for i in int_values:
    #     sum_value + i

    return sum_value

new_dict = {
  "cat": "bob",
  "dog": 23,
  19: 18,
  90: "fish"
}

print(int_sum(new_dict))


# Name score of p022 based of alphabetical order
# OPTIMAL (<0.1s)
#
# APPROACH:
#   Read and sort the words, then get their score using python internal ord.

import os

FILE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'p022_names.txt')

def get_scores_of_names():
    acc = 0
    with open(FILE_DIR, 'r') as f:
        content = f.read().replace('"', '').replace('\n', '').split(',')
        content.sort()

        for index, name in enumerate(content):
        	acc += (index+1) * get_alpha_score(name)

    return acc

def get_alpha_score(n):
    return sum(ord(i) - 64 for i in n)

assert(get_alpha_score('COLIN') == 53)
result = get_scores_of_names()


from numpy import zeros, float16, float32, float64
from time import perf_counter
import matplotlib.pylab as plt

Ns = [1,5,10]
for i in range (20,600,20):
	Ns.append(i)
Ns[-1]=8000
Ns[-2]=5000
Ns[-3]=2000
Ns[-4]=1000

#print (Ns)
#print (len(Ns))
# exit(0)

dts = []
mems = []

for i in range(10):
	fid = open(f"rendimiento{i}.txt", "w")

	for N in Ns:

		uso_memoria = 0

		A = zeros ((N, N), dtype=float16) + 1

		uso_memoria_teorico = 4*N*N #bytes... float32 4bytes
		uso_memoria_numpy  = A.nbytes
		#print (f"uso_memoria_teorico = {uso_memoria_teorico}")
		#print (f"uso_memoria_numpy = {uso_memoria_numpy}")

		B = zeros ((N, N))+2

		# print (f"A = {A}")
		# print (f"B = {B}")

		t1 = perf_counter()
		C = A@B
		t2 = perf_counter()

		uso_memoria_total = A.nbytes + B.nbytes + C.nbytes

		dt = t2-t1
		dts.append(dt)
		mems.append(uso_memoria_total)

		# print (f"N = {N} dt = {dt} s _ uso_memoria_total= {uso_memoria_total} bytes _ flops = {N**3/dt} flops/s")

		fid.write(f"N = {N} dt = {dt} s _ uso_memoria_total= {uso_memoria_total} bytes \n" )

	print (f"iteración = {i+1} lista")

	fid.close()

### END ###


from lib import data
from app import filter_years, filter_authors

def wrong_input():
    print('''
    ######### ERROR #########
    Something went wrong
    Check whether you inputed wrong
    #########################
    ''')

def run():
    print( '''
    -------------------------
    Welcome to library filter
    -------------------------
    Chose 1 to filter your data by authors
    Chose 2 to filter your data by years

    Chose 0 to exit
    ''' )
    user_choose = input(' > ')
    if user_choose == '1':
        authors = filter_authors(data)
        for author in authors:
            info_arr = authors[author]
            for info in info_arr: #such as there can be many books written by the same author
                print() #next line
                print('  ' + author + ' :' + ' '*( 20 - len(author) )  + info['title'] + ' '*( 40 - len(info['title']) ) + str(info['year']) + ' '*( 4 - len(str(info['year'])) ))
            print('_' * 69)
        run()
    elif user_choose == '2':
        years = filter_years(data)
        for year in years:
            info_arr = years[year]
            for info in info_arr: #such as there can be many books written by the same author
                print() #next line
                print('  ' + str(year) + ' :' + ' '*( 4 - len( str(year) ) ) + str(info['author']) + ' '*( 20 - len(str(info['author'])) ) + info['title'] + ' '*( 40 - len(info['title']) ) )
            print('_' * 69)
        run()
    elif user_choose == '0':
        exit()
    else:
        wrong_input()
        run()

run()


import numpy as np
from sklearn import datasets, linear_model
from sklearn.metrics import mean_squared_error, r2_score
from matplotlib import pyplot

# Load the diabetes dataset
diabetes = datasets.load_diabetes()

##my impleentation
#jtester = Ridge()
ridge_model =linear_model.Ridge(fit_intercept= True, alpha=0,normalize=False)
ridge_model.fit(np.copy(diabetes.data ),np.copy(diabetes.target))
reg_coef,reg_inter = ridge_model.coef_ ,ridge_model.intercept_
X = np.copy(diabetes.data)
X_mu = np.mean(X,axis=0)
X_std = np.std(X,axis=0)
y_std = np.std(diabetes.target)
y_mu = np.mean(diabetes.target)
y = (diabetes.target - y_mu)/y_std
X =  (X - X_mu )/X_std
ridge_model =linear_model.Ridge(fit_intercept= True, alpha=0,normalize=False)
ridge_model.fit(X,y)
(std_coef,std_inter) = (ridge_model.coef_,ridge_model.intercept_)
print("----Normal Ridge coefficients -----")
print( reg_coef)
print("----- Standardzied Coefficients----")
print(std_coef)
print("----- Conversion of Coefficients ---")
print( np.multiply(np.true_divide(X_std,y_std ),reg_coef) )


config = {
    "initial_self": 8,
    "initial_opp": 8,
    "1_player_init": (1, 1),
    "2_player_init": (1, 1),
    "1_player_tick": (0, 4, 3, 1, 2),
    "2_player_tick": (0, 4, 3, 1, 2)
}
# Init configs:
# 2 args for 2 cups
#   1: 1 of own colour
#   2: 2 of opp colour
# Tick configs:
# Args for priority
#   0: 0
#   1: 1 of own colour
#   2: 2 of own colour
#   3: 1 of opp colour
#   4: 2 of opp colour


# Import pandas for data manipulation
import pandas as pd
from math import sqrt

class DemandForecast:
    """
    Class used to import, transform and predict data. Provides friendly
    interface for usage in subsequent steps.
    """
    def __init__(self, file_path, prediction_model):
        self.file_path = file_path
        self.model = prediction_model

    def _create_data(self):
        """
        Read in the Excel file provided as self.file_path,
        return the raw data.

        Output: pd.DataFrame with data
        """
        _raw_data = pd.read_excel(self.file_path,
                              parse_dates=['Date'])
        return _raw_data

    def prepare_data(self):
        """
        Calls _create_data() and enriches self._data by:
        - renaming columns to easier names
        - adding weekday dumy columns
        - adding lagged variables
        - removes data that can lead to leakage: demand itself
        - removes data that XGB can't work with: the date column

        Output: None, only self._data is modified
        """
        self._data = self._create_data()
        # Add logging
        self._data.rename(columns={'# items demanded':'demand',
                   'Avg temp in 0.1oC': 'temperature',
                   'Rainfall in 24h in 0.1mm':'precipitation',
                  }, inplace=True)

        # Add logging // add weekday dummies
        _weekday_series = self._data['Date'].dt.day_name()
        _weekday_dummies = pd.get_dummies(_weekday_series)
        self._data = pd.concat([self._data, _weekday_dummies],
                               axis=1)

        # Add logging // add lagged variables
        self._data['t-7'] = self._data['demand'].shift(7).fillna(self._data['demand'].mean())
        self._data['t-14'] = self._data['demand'].shift(14).fillna(self._data['demand'].mean())

    def predict_demand(self):
        """
        Save a predicted demand list for the entire self._data
        DataFrame, based on the `predict` method of the trained XGB
        regressor model. Only predict on valid columns (i.e. drop Date and demand
        column to prevent data leakage or incompatibility of data type for XGB).

        Output: self._data['demand_pred'] pd Series
        """
        self._data['demand_pred'] = self.model.predict(self._data.drop(['Date',
                                        'demand'], axis=1))
        print("Predictions made")

    # Function to obtain demand prediction for day n and day n+1
    def get_prediction_for_day(self, n):
        """
        Obtain the predictions for day n (zero-based) and
        day n+1 as a tuple of floats.

        Input: integer n, indicating the day
        Output: (pred_n <float>, pred_n_plus_1 <float>)
        """
        assert n>=0 and n<len(self._data), "Provide 0 <= n < n_observations"

        _pred_day_n = self._data.loc[n, 'demand_pred']
        _pred_day_n_1 = self._data.loc[n+1, 'demand_pred']

        return _pred_day_n, _pred_day_n_1

    # Function to obtain true demand for day n and day n+1
    def get_observed_demand_for_day(self, n):
        """
        Obtain the true demand for day n (zero-based) and
        day n+1 as a tuple of floats.

        Input: integer n, indicating the day
        Output: (pred_n <float>, pred_n_plus_1 <float>)
        """
        assert n>=0 and n<len(self._data), "Provide 0 <= n < n_observations"

        _true_day_n = self._data.loc[n, 'demand']
        _true_day_n_1 = self._data.loc[n+1, 'demand']

        return _true_day_n, _true_day_n_1


"""Manage outbound ON command to a device."""
from ...topics import PRODUCT_DATA_REQUEST
from .direct_command import DirectCommandHandlerBase

class ProductDataRequestCommand(DirectCommandHandlerBase):
    """Manage an outbound ON command to a device."""

    def __init__(self, address):
        """Init the OnLevelCommand class."""
        super().__init__(topic=PRODUCT_DATA_REQUEST, address=address)

    # pylint: disable=arguments-differ
    async def async_send(self):
        """Send the OFF command async."""
        return await super().async_send()

    def _update_subscribers_on_direct_ack(
        self, cmd1, cmd2, target, user_data, hops_left
    ):
        """Update subscribers."""
        if user_data is None:
            return
        product_bytes = bytearray([user_data["d2"], user_data["d3"], user_data["d4"]])
        product_id = int.from_bytes(product_bytes, byteorder="big")
        cat = user_data["d5"]
        subcat = user_data["d6"]
        self._call_subscribers(product_id=product_id, cat=cat, subcat=subcat)