import graphene
from graphql_jwt.decorators import permission_required

from ..core.enums import ReportingPeriod
from ..core.fields import FilterInputConnectionField, PrefetchingConnectionField
from ..core.types import FilterInputObjectType
from ..descriptions import DESCRIPTIONS
from ..translations.mutations import (
    AttributeTranslate,
    AttributeValueTranslate,
    CategoryTranslate,
    CollectionTranslate,
    ProductTranslate,
    ProductVariantTranslate,
)
from .bulk_mutations.attributes import AttributeBulkDelete, AttributeValueBulkDelete
from .bulk_mutations.products import (
    CategoryBulkDelete,
    CollectionBulkDelete,
    CollectionBulkPublish,
    ProductBulkDelete,
    ProductBulkPublish,
    ProductImageBulkDelete,
    ProductTypeBulkDelete,
    ProductVariantBulkDelete,
)
from .enums import StockAvailability
from .filters import CollectionFilter, ProductFilter, ProductTypeFilter
from .mutations.attributes import (
    AttributeCreate,
    AttributeDelete,
    AttributeUpdate,
    AttributeValueCreate,
    AttributeValueDelete,
    AttributeValueUpdate,
)
from .mutations.digital_contents import (
    DigitalContentCreate,
    DigitalContentDelete,
    DigitalContentUpdate,
    DigitalContentUrlCreate,
)
from .mutations.products import (
    CategoryCreate,
    CategoryDelete,
    CategoryUpdate,
    CollectionAddProducts,
    CollectionCreate,
    CollectionDelete,
    CollectionRemoveProducts,
    CollectionReorderProducts,
    CollectionUpdate,
    ProductCreate,
    ProductDelete,
    ProductImageCreate,
    ProductImageDelete,
    ProductImageReorder,
    ProductImageUpdate,
    ProductTypeCreate,
    ProductTypeDelete,
    ProductTypeUpdate,
    ProductUpdate,
    ProductVariantCreate,
    ProductVariantDelete,
    ProductVariantUpdate,
    VariantImageAssign,
    VariantImageUnassign,
)
from .resolvers import (
    resolve_attributes,
    resolve_categories,
    resolve_collections,
    resolve_digital_contents,
    resolve_product_types,
    resolve_product_variants,
    resolve_products,
    resolve_report_product_sales,
)
from .scalars import AttributeScalar
from .types import (
    Attribute,
    Category,
    Collection,
    DigitalContent,
    Product,
    ProductOrder,
    ProductType,
    ProductVariant,
)

class ProductFilterInput(FilterInputObjectType):
    class Meta:
        filterset_class = ProductFilter

class CollectionFilterInput(FilterInputObjectType):
    class Meta:
        filterset_class = CollectionFilter

class ProductTypeFilterInput(FilterInputObjectType):
    class Meta:
        filterset_class = ProductTypeFilter

class ProductQueries(graphene.ObjectType):
    digital_content = graphene.Field(
        DigitalContent, id=graphene.Argument(graphene.ID, required=True)
    )
    digital_contents = PrefetchingConnectionField(
        DigitalContent,
        query=graphene.String(),
        level=graphene.Argument(graphene.Int),
        description="List of the digital contents.",
    )
    attributes = PrefetchingConnectionField(
        Attribute,
        description="List of the shop's attributes.",
        query=graphene.String(description=DESCRIPTIONS["attributes"]),
        in_category=graphene.Argument(
            graphene.ID,
            description="""Return attributes for products
            belonging to the given category.""",
        ),
        in_collection=graphene.Argument(
            graphene.ID,
            description="""Return attributes for products
            belonging to the given collection.""",
        ),
    )
    categories = PrefetchingConnectionField(
        Category,
        query=graphene.String(description=DESCRIPTIONS["category"]),
        level=graphene.Argument(graphene.Int),
        description="List of the shop's categories.",
    )
    category = graphene.Field(
        Category,
        id=graphene.Argument(graphene.ID, required=True),
        description="Lookup a category by ID.",
    )
    collection = graphene.Field(
        Collection,
        id=graphene.Argument(graphene.ID, required=True),
        description="Lookup a collection by ID.",
    )
    collections = FilterInputConnectionField(
        Collection,
        filter=CollectionFilterInput(),
        query=graphene.String(description=DESCRIPTIONS["collection"]),
        description="List of the shop's collections.",
    )
    product = graphene.Field(
        Product,
        id=graphene.Argument(graphene.ID, required=True),
        description="Lookup a product by ID.",
    )
    products = FilterInputConnectionField(
        Product,
        filter=ProductFilterInput(),
        attributes=graphene.List(
            AttributeScalar, description="Filter products by attributes."
        ),
        categories=graphene.List(
            graphene.ID, description="Filter products by category."
        ),
        collections=graphene.List(
            graphene.ID, description="Filter products by collections."
        ),
        price_lte=graphene.Float(
            description="Filter by price less than or equal to the given value."
        ),
        price_gte=graphene.Float(
            description="Filter by price greater than or equal to the given value."
        ),
        sort_by=graphene.Argument(ProductOrder, description="Sort products."),
        stock_availability=graphene.Argument(
            StockAvailability, description="Filter products by the stock availability"
        ),
        query=graphene.String(description=DESCRIPTIONS["product"]),
        description="List of the shop's products.",
    )
    product_type = graphene.Field(
        ProductType,
        id=graphene.Argument(graphene.ID, required=True),
        description="Lookup a product type by ID.",
    )
    product_types = FilterInputConnectionField(
        ProductType,
        filter=ProductTypeFilterInput(),
        description="List of the shop's product types.",
    )
    product_variant = graphene.Field(
        ProductVariant,
        id=graphene.Argument(graphene.ID, required=True),
        description="Lookup a variant by ID.",
    )
    product_variants = PrefetchingConnectionField(
        ProductVariant,
        ids=graphene.List(graphene.ID),
        description="Lookup multiple variants by ID",
    )
    report_product_sales = PrefetchingConnectionField(
        ProductVariant,
        period=graphene.Argument(
            ReportingPeriod, required=True, description="Span of time."
        ),
        description="List of top selling products.",
    )

    def resolve_attributes(
        self, info, in_category=None, in_collection=None, query=None, **_kwargs
    ):
        return resolve_attributes(info, in_category, in_collection, query)

    def resolve_categories(self, info, level=None, query=None, **_kwargs):
        return resolve_categories(info, level=level, query=query)

    def resolve_category(self, info, id):
        return graphene.Node.get_node_from_global_id(info, id, Category)

    def resolve_collection(self, info, id):
        return graphene.Node.get_node_from_global_id(info, id, Collection)

    def resolve_collections(self, info, query=None, **_kwargs):
        return resolve_collections(info, query)

    @permission_required("product.manage_products")
    def resolve_digital_content(self, info, id):
        return graphene.Node.get_node_from_global_id(info, id, DigitalContent)

    @permission_required("product.manage_products")
    def resolve_digital_contents(self, info, **_kwargs):
        return resolve_digital_contents(info)

    def resolve_product(self, info, id):
        return graphene.Node.get_node_from_global_id(info, id, Product)

    def resolve_products(self, info, **kwargs):
        return resolve_products(info, **kwargs)

    def resolve_product_type(self, info, id):
        return graphene.Node.get_node_from_global_id(info, id, ProductType)

    def resolve_product_types(self, info, **_kwargs):
        return resolve_product_types(info)

    def resolve_product_variant(self, info, id):
        return graphene.Node.get_node_from_global_id(info, id, ProductVariant)

    def resolve_product_variants(self, info, ids=None, **_kwargs):
        return resolve_product_variants(info, ids)

    @permission_required(["order.manage_orders", "product.manage_products"])
    def resolve_report_product_sales(self, *_args, period, **_kwargs):
        return resolve_report_product_sales(period)

class ProductMutations(graphene.ObjectType):
    attribute_create = AttributeCreate.Field()
    attribute_delete = AttributeDelete.Field()
    attribute_bulk_delete = AttributeBulkDelete.Field()
    attribute_update = AttributeUpdate.Field()
    attribute_translate = AttributeTranslate.Field()

    attribute_value_create = AttributeValueCreate.Field()
    attribute_value_delete = AttributeValueDelete.Field()
    attribute_value_bulk_delete = AttributeValueBulkDelete.Field()
    attribute_value_update = AttributeValueUpdate.Field()
    attribute_value_translate = AttributeValueTranslate.Field()

    category_create = CategoryCreate.Field()
    category_delete = CategoryDelete.Field()
    category_bulk_delete = CategoryBulkDelete.Field()
    category_update = CategoryUpdate.Field()
    category_translate = CategoryTranslate.Field()

    collection_add_products = CollectionAddProducts.Field()
    collection_create = CollectionCreate.Field()
    collection_delete = CollectionDelete.Field()
    collection_reorder_products = CollectionReorderProducts.Field()
    collection_bulk_delete = CollectionBulkDelete.Field()
    collection_bulk_publish = CollectionBulkPublish.Field()
    collection_remove_products = CollectionRemoveProducts.Field()
    collection_update = CollectionUpdate.Field()
    collection_translate = CollectionTranslate.Field()

    product_create = ProductCreate.Field()
    product_delete = ProductDelete.Field()
    product_bulk_delete = ProductBulkDelete.Field()
    product_bulk_publish = ProductBulkPublish.Field()
    product_update = ProductUpdate.Field()
    product_translate = ProductTranslate.Field()

    product_image_create = ProductImageCreate.Field()
    product_image_delete = ProductImageDelete.Field()
    product_image_bulk_delete = ProductImageBulkDelete.Field()
    product_image_reorder = ProductImageReorder.Field()
    product_image_update = ProductImageUpdate.Field()

    product_type_create = ProductTypeCreate.Field()
    product_type_delete = ProductTypeDelete.Field()
    product_type_bulk_delete = ProductTypeBulkDelete.Field()
    product_type_update = ProductTypeUpdate.Field()

    digital_content_create = DigitalContentCreate.Field()
    digital_content_delete = DigitalContentDelete.Field()
    digital_content_update = DigitalContentUpdate.Field()

    digital_content_url_create = DigitalContentUrlCreate.Field()

    product_variant_create = ProductVariantCreate.Field()
    product_variant_delete = ProductVariantDelete.Field()
    product_variant_bulk_delete = ProductVariantBulkDelete.Field()
    product_variant_update = ProductVariantUpdate.Field()
    product_variant_translate = ProductVariantTranslate.Field()

    variant_image_assign = VariantImageAssign.Field()
    variant_image_unassign = VariantImageUnassign.Field()


import cv2
import numpy

#rgb分量
fn='timg.jpeg'
image1=cv2.imread(fn)
image=numpy.mat
bgrs=numpy.mat
hsv=numpy.mat
hsvs=numpy.mat
image=cv2.pyrDown(image1)
bgrs=cv2.split(image)
cv2.imshow('source image',image)
cv2.imshow('b',bgrs[0])
cv2.imshow('g',bgrs[1])
cv2.imshow('r',bgrs[2])
cv2.waitKey()

#hsv分量
hsv=cv2.cvtColor(image1,cv2.COLOR_BGR2HSV)
hsvs=cv2.split(hsv)
cv2.imshow('Hue',hsvs[0])
cv2.imshow('Saturation',hsvs[1])
cv2.imshow('Value',hsvs[2])
cv2.waitKey()


import tensorflow as tf
import numpy as np
from data_utils import BatchGenerator, ImageGenerator
from keras.models import Sequential
from config import *
from keras.layers import *

slim = tf.contrib.slim

layer_norm = lambda x: tf.contrib.layers.layer_norm(inputs=x, center=True, scale=True, activation_fn=None,
                                                    trainable=True)

def get_optimizer(loss, lrate):
    optimizer = tf.train.AdamOptimizer(learning_rate=lrate)
    gradvars = optimizer.compute_gradients(loss)
    gradients, v = list(zip(*gradvars))
    print([x.name for x in v])
    gradients, _ = tf.clip_by_global_norm(gradients, 15.0)
    return optimizer.apply_gradients(zip(gradients, v))

def apply_vision_simple(image, keep_prob, batch_size, seq_len, scope=None, reuse=None):
    video = tf.reshape(image, shape=[batch_size, LEFT_CONTEXT + seq_len, HEIGHT, WIDTH, CHANNELS])
    with tf.variable_scope(scope, 'Vision', [image], reuse=reuse):
        net = slim.convolution(video, num_outputs=64, kernel_size=[3, 12, 12], stride=[1, 6, 6], padding="VALID")
        net = tf.nn.dropout(x=net, keep_prob=keep_prob)
        aux1 = slim.fully_connected(tf.reshape(net[:, -seq_len:, :, :, :], [batch_size, seq_len, -1]), 128,
                                    activation_fn=None)

        net = slim.convolution(net, num_outputs=64, kernel_size=[2, 5, 5], stride=[1, 2, 2], padding="VALID")
        net = tf.nn.dropout(x=net, keep_prob=keep_prob)
        aux2 = slim.fully_connected(tf.reshape(net[:, -seq_len:, :, :, :], [batch_size, seq_len, -1]), 128,
                                    activation_fn=None)

        net = slim.convolution(net, num_outputs=64, kernel_size=[2, 5, 5], stride=[1, 1, 1], padding="VALID")
        net = tf.nn.dropout(x=net, keep_prob=keep_prob)
        aux3 = slim.fully_connected(tf.reshape(net[:, -seq_len:, :, :, :], [batch_size, seq_len, -1]), 128,
                                    activation_fn=None)

        net = slim.convolution(net, num_outputs=64, kernel_size=[2, 5, 5], stride=[1, 1, 1], padding="VALID")
        net = tf.nn.dropout(x=net, keep_prob=keep_prob)
        # at this point the tensor 'net' is of shape batch_size x seq_len x ...
        aux4 = slim.fully_connected(tf.reshape(net, [batch_size, seq_len, -1]), 128, activation_fn=None)

        net = slim.fully_connected(tf.reshape(net, [batch_size, seq_len, -1]), 1024, activation_fn=tf.nn.relu)
        net = tf.nn.dropout(x=net, keep_prob=keep_prob)
        net = slim.fully_connected(net, 512, activation_fn=tf.nn.relu)
        net = tf.nn.dropout(x=net, keep_prob=keep_prob)
        net = slim.fully_connected(net, 256, activation_fn=tf.nn.relu)
        net = tf.nn.dropout(x=net, keep_prob=keep_prob)
        net = slim.fully_connected(net, 128, activation_fn=None)
        return layer_norm(tf.nn.elu(net + aux1 + aux2 + aux3 + aux4))  # aux[1-4] are residual connections (shortcuts)

class SamplingRNNCell(tf.nn.rnn_cell.RNNCell):
    """Simple sampling RNN cell."""

    def __init__(self, num_outputs, use_ground_truth, internal_cell):
        """
        if use_ground_truth then don't sample
        """
        self._num_outputs = num_outputs
        self._use_ground_truth = use_ground_truth  # boolean
        self._internal_cell = internal_cell  # may be LSTM or GRU or anything

    @property
    def state_size(self):
        return self._num_outputs, self._internal_cell.state_size  # previous output and bottleneck state

    @property
    def output_size(self):
        return self._num_outputs  # steering angle, torque, vehicle speed

    def __call__(self, inputs, state, scope=None):
        (visual_feats, current_ground_truth) = inputs
        prev_output, prev_state_internal = state
        context = tf.concat([prev_output, visual_feats], 1)
        new_output_internal, new_state_internal = self._internal_cell(context,
                                                                prev_state_internal)  # here the internal cell (e.g. LSTM) is called
        new_output = tf.contrib.layers.fully_connected(
            inputs=tf.concat([new_output_internal, prev_output, visual_feats], 1),
            num_outputs=self._num_outputs,
            activation_fn=None,
            scope="OutputProjection")
        # if self._use_ground_truth == True, we pass the ground truth as the state; otherwise, we use the model's predictions
        return new_output, (current_ground_truth if self._use_ground_truth else new_output, new_state_internal)

class Model:
    def make_model(self, mean, std):
        raise NotImplementedError

    def do_epoch(self, session, sequences, labels, mode):
        raise NotImplementedError

class CNN(Model):
    def __init__(self, graph, mean, std, dir='deep-cnn', batch_size=IMAGE_BATCH_SIZE):
        self.global_train_step = 0
        self.global_valid_step = 0
        self.KEEP_PROB_CONV_TRAIN = 0.75
        self.KEEP_PROB_FC_TRAIN = 0.5
        self.batch_size = batch_size

        self.train_writer = tf.summary.FileWriter('{}/train_summary'.format(dir), graph=graph)
        self.valid_writer = tf.summary.FileWriter('{}/valid_summary'.format(dir), graph=graph)

        print('Building model')
        self.make_model(mean, std)
        print('finished making model')

    def make_model(self, mean, std):
        def weight_variable(shape):
            '''
            Initialize weights
            :param shape: shape of weights, e.g. [w, h ,Cin, Cout] where
            w: width of the filters
            h: height of the filters
            Cin: the number of the channels of the filters
            Cout: the number of filters
            :return: a tensor variable for weights with initial values
            '''
            initial = tf.truncated_normal(shape, stddev=0.1)
            return tf.Variable(initial)

        def variable_summaries(var):
            """Attach a lot of summaries to a Tensor (for TensorBoard visualization)."""
            with tf.name_scope('summaries'):
                mean = tf.reduce_mean(var)
                tf.summary.scalar('mean', mean)
                stddev = tf.sqrt(tf.reduce_mean(tf.square(var - mean)))
                tf.summary.scalar('stddev', stddev)
                tf.summary.scalar('max', tf.reduce_max(var))
                tf.summary.scalar('min', tf.reduce_min(var))
                tf.summary.histogram('histogram', var)

        def bias_variable(shape):
            '''
            Initialize biases
            :param shape: shape of biases, e.g. [Cout] where
            Cout: the number of filters
            :return: a tensor variable for biases with initial values
            '''

            # IMPLEMENT YOUR BIAS_VARIABLE HERE
            initial = tf.constant(0.1, shape=shape)
            return tf.Variable(initial)

        def conv2d(x, W, stride):
            return tf.nn.conv2d(x, W, strides=[1, stride, stride, 1], padding='VALID')

        def max_pool_2x2(x):
            '''
            Perform non-overlapping 2-D maxpooling on 2x2 regions in the input data
            :param x: input data
            :return: the results of maxpooling (max-marginalized + downsampling)
            '''

            # IMPLEMENT YOUR MAX_POOL_2X2 HERE
            return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

        def batch_norm(x, bias):
            gamma = tf.Variable(tf.constant(1.0, shape=[x.shape[-1]]),
                                name='gamma', trainable=True)
            batch_mean, batch_var = tf.nn.moments(x, list(range(len(x.shape) - 1)))
            # return tf.nn.batch_normalization(x, batch_mean, batch_var, bias, gamma, var_epsilon)
            return tf.nn.batch_normalization(x, batch_mean, batch_var, bias, gamma)

        # inputs
        self.inputs = tf.placeholder(shape=(None, HEIGHT, WIDTH, CHANNELS), dtype=tf.float32, name="inputs") # images
        inputs = self.inputs - mean[0] / std[0]
        self.preprocessed_inputs = tf.image.resize_images(inputs, (160, 120))

        print('Input Shape: {}\nDownsample: {}'.format(self.inputs.get_shape().as_list(),
                                                       self.preprocessed_inputs.get_shape().as_list())
              )
        self.targets = tf.placeholder(shape=(self.batch_size,),
                                 dtype=tf.float32, name="targets")  # seq_len x batch_size x OUTPUT_DIM
        targets_normalized = (self.targets - mean[0]) / std[0]

        self.conv_dropout = tf.placeholder(tf.float32, name="conv_dropout")
        self.fc_dropout = tf.placeholder(tf.float32, name="fc_dropout")

        # conv1 = weight_variable([3, 3, 3, 32])
        # b1 = bias_variable([32])
        # h1 = tf.nn.relu(conv2d(self.preprocessed_inputs, conv1) + b1)
        # # d1 = tf.nn.dropout(h1, self.conv_dropout)
        # # pool1 = max_pool_2x2(d1)
        #
        # conv2 = weight_variable([3, 3, 32, 32])
        # b2 = bias_variable([32])
        # h2 = tf.nn.relu(conv2d(h1, conv2) + b2)
        # pool1 = max_pool_2x2(h2) # 25% dropout
        # d1 = tf.nn.dropout(pool1, self.conv_dropout)
        #
        # conv3 = weight_variable([3, 3, 32, 64])
        # b3 = bias_variable([64])
        # h3 = tf.nn.relu(conv2d(d1, conv3) + b3)
        #
        # conv4 = weight_variable([3,3,64,64])
        # b4 = bias_variable([64])
        # h4 = tf.nn.relu(conv2d(h3, conv4) + b4)
        # pool2 = max_pool_2x2(h4)
        # d2 = tf.nn.dropout(pool2, self.conv_dropout)
        #
        # # None, 64, 35, 64
        # print('POOL ', d2.get_shape().as_list())
        # new_shape = 1
        # for s in d2.get_shape().as_list()[1:]:
        #     new_shape *= s
        # fc1 = weight_variable([new_shape, 512])
        # bfc1 = bias_variable([512])
        # pool2_flat = tf.reshape(d2, [-1, new_shape])
        # h3 = tf.nn.relu(tf.matmul(pool2_flat, fc1) + bfc1)
        # d3 = tf.nn.dropout(h3, self.fc_dropout)
        #
        # fc2 = weight_variable([512, 1])
        # bfc2 = bias_variable([1])
        W_conv1 = weight_variable([5, 5, 3, 24])
        b_conv1 = bias_variable([24])

        h_conv1 = tf.nn.relu(conv2d(self.preprocessed_inputs, W_conv1, 2) + b_conv1)

        # second convolutional layer
        W_conv2 = weight_variable([5, 5, 24, 36])
        b_conv2 = bias_variable([36])

        h_conv2 = tf.nn.relu(conv2d(h_conv1, W_conv2, 2) + b_conv2)

        # third convolutional layer
        W_conv3 = weight_variable([5, 5, 36, 48])
        b_conv3 = bias_variable([48])

        h_conv3 = tf.nn.relu(conv2d(h_conv2, W_conv3, 2) + b_conv3)

        # fourth convolutional layer
        W_conv4 = weight_variable([3, 3, 48, 64])
        b_conv4 = bias_variable([64])

        h_conv4 = tf.nn.relu(conv2d(h_conv3, W_conv4, 1) + b_conv4)

        # fifth convolutional layer
        W_conv5 = weight_variable([3, 3, 64, 64])
        b_conv5 = bias_variable([64])

        h_conv5 = tf.nn.relu(conv2d(h_conv4, W_conv5, 1) + b_conv5)

        # FCL 1
        new_shape = 1
        for s in h_conv5.get_shape().as_list()[1:]:
            new_shape *= s
        W_fc1 = weight_variable([new_shape, 1164])
        b_fc1 = bias_variable([1164])

        h_conv5_flat = tf.reshape(h_conv5, [-1, new_shape])
        h_fc1 = tf.nn.relu(tf.matmul(h_conv5_flat, W_fc1) + b_fc1)

        # keep_prob = tf.placeholder(tf.float32)
        h_fc1_drop = tf.nn.dropout(h_fc1, self.fc_dropout)

        # FCL 2
        W_fc2 = weight_variable([1164, 100])
        b_fc2 = bias_variable([100])

        h_fc2 = tf.nn.relu(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

        h_fc2_drop = tf.nn.dropout(h_fc2, self.fc_dropout)

        # FCL 3
        W_fc3 = weight_variable([100, 50])
        b_fc3 = bias_variable([50])

        h_fc3 = tf.nn.relu(tf.matmul(h_fc2_drop, W_fc3) + b_fc3)

        h_fc3_drop = tf.nn.dropout(h_fc3, self.fc_dropout)

        # FCL 3
        W_fc4 = weight_variable([50, 10])
        b_fc4 = bias_variable([10])

        h_fc4 = tf.nn.relu(tf.matmul(h_fc3_drop, W_fc4) + b_fc4)

        h_fc4_drop = tf.nn.dropout(h_fc4, self.fc_dropout)

        # Output
        W_fc5 = weight_variable([10, 1])
        b_fc5 = bias_variable([1])

        self.steering_predictions = tf.multiply(tf.atan(tf.matmul(h_fc4_drop, W_fc5) + b_fc5), 2) * std[0] + mean[0]# scale the atan outpu
        # self.steering_predictions = tf.matmul(d3, fc2) + bfc2

        # Summary statistics
        # weights = [conv1, conv2, fc1, fc2]
        # bias = [b1,b2,b3,b4]
        # activs = [h1,h2,h3]
        # pools = [pool1,pool2]
        # with tf.name_scope('weights'):
        #     map(variable_summaries, weights)
        # with tf.name_scope('bias'):
        #     map(variable_summaries, bias)
        # with tf.name_scope('ReLU'):
        #     map(variable_summaries, activs)
        # with tf.name_scope('pools'):
        #     map(variable_summaries, pools)

        self.lr = 1e-3
        self.rmse = tf.sqrt(tf.reduce_sum(tf.squared_difference(targets_normalized, self.steering_predictions)))
        tf.summary.scalar('RMSE_Loss', self.rmse)
        self.optimizer = tf.train.RMSPropOptimizer(self.lr).minimize(self.rmse)
        self.summary_op = tf.summary.merge_all()
        self.saver = tf.train.Saver(write_version=tf.train.SaverDef.V2)

    def do_epoch(self, session, sequences, labels, mode, gen=None):
        """
        batch generator will return np arrays
        """
        test_predictions = {}
        valid_predictions = {}
        if not gen:
            batch_generator = ImageGenerator(sequence_X=sequences, sequence_Y=labels, batch_size=self.batch_size)
        else:
            batch_generator = gen
        total_num_steps = batch_generator.get_total_steps()
        acc_loss = np.float128(0.0)
        generator = batch_generator.next()
        for step in range(total_num_steps):
            feed_inputs, feed_targets, input_paths = next(generator)
            assert(np.all(np.equal(feed_inputs[0], feed_inputs[1])) == False)
            # print("FEED INPUTS", feed_inputs)
            # print('FINISHGEED MAKING BACH')
            feed_dict = {self.inputs: feed_inputs, self.targets: feed_targets}
            if mode == "train":
                feed_dict.update({self.conv_dropout: self.KEEP_PROB_CONV_TRAIN, self.fc_dropout: self.KEEP_PROB_FC_TRAIN})

                preds, summary, _, loss = session.run([self.steering_predictions, self.summary_op, self.optimizer, self.rmse], feed_dict=feed_dict)
                from pdb import set_trace
                set_trace()
                self.train_writer.add_summary(summary, self.global_train_step)
                self.global_train_step += 1
            elif mode == "valid":
                feed_dict.update({self.conv_dropout: 1.0, self.fc_dropout: 1.0})
                model_predictions, summary, loss = \
                    session.run([self.steering_predictions,
                                 self.summary_op,
                                 self.rmse
                                 ],
                                feed_dict=feed_dict)
                self.valid_writer.add_summary(summary, self.global_valid_step)
                self.global_valid_step += 1
                # feed_inputs = feed_inputs.flatten()
                steering_targets = feed_targets.flatten()
                model_predictions = model_predictions.flatten()
                stats = np.stack([steering_targets, model_predictions, (steering_targets - model_predictions) ** 2])
                for i, img_path in enumerate(input_paths):
                    valid_predictions[img_path] = stats[:, i]
            elif mode == "test":
                feed_dict.update({self.conv_dropout: 1.0, self.fc_dropout: 1.0})
                model_predictions = \
                    session.run(self.steering_predictions, feed_dict=feed_dict)
                model_predictions = model_predictions.flatten()
                for i, img_path in enumerate(input_paths):
                    test_predictions[img_path] = model_predictions[i]
            if mode != "test" and step % 100 == 0:
                acc_loss += loss
                print('\r', step + 1, "/", total_num_steps, np.sqrt(acc_loss / (step + 1)))
        print()
        return (np.sqrt(acc_loss / total_num_steps), valid_predictions) if mode != "test" else (None, test_predictions)

    def predict(self, imgs, session):
        feed_dict = {self.inputs: imgs, self.conv_dropout: 1.0, self.fc_dropout: 1.0}
        model_predictions = session.run([self.steering_predictions], feed_dict=feed_dict)
        assert len(model_predictions) == len(imgs)
        model_predictions = np.array(model_predictions).flatten()
        return model_predictions

class Komada(Model):
    def __init__(self, graph, mean, std):
        self.global_train_step = 0
        self.global_valid_step = 0

        self.KEEP_PROB_TRAIN = 0.25

        self.train_writer = tf.summary.FileWriter('v4/train_summary', graph=graph)
        self.valid_writer = tf.summary.FileWriter('v4/valid_summary', graph=graph)

        self.make_model(mean, std)
        print('finished making model')
        total_parameters = 0
        for variable in tf.trainable_variables():
            # shape is an array of tf.Dimension
            shape = variable.get_shape()
            # print(shape)
            # print(len(shape))
            variable_parameters = 1
            for dim in shape:
                # print(dim)
                variable_parameters *= dim.value
            # print(variable_parameters)
            total_parameters += variable_parameters
        print(total_parameters)

    def make_model(self, mean, std):
        output_config = {}

        # inputs
        learning_rate = tf.placeholder_with_default(input=1e-4, shape=())
        self.keep_prob = tf.placeholder_with_default(input=1.0, shape=())
        aux_cost_weight = tf.placeholder_with_default(input=0.1, shape=())

        self.inputs = tf.placeholder(shape=(BATCH_SIZE, LEFT_CONTEXT + SEQ_LEN),
                                dtype=tf.string)  # pathes to png files from the central camera
        self.targets = tf.placeholder(shape=(BATCH_SIZE, SEQ_LEN, OUTPUT_DIM),
                                 dtype=tf.float32)  # seq_len x batch_size x OUTPUT_DIM

        targets_normalized = (self.targets - mean) / std

        input_images = tf.stack([tf.image.decode_png(tf.read_file(x))
                                 for x in tf.unstack(tf.reshape(self.inputs, shape=[(LEFT_CONTEXT + SEQ_LEN) * BATCH_SIZE]))])
        input_images = -1.0 + 2.0 * tf.cast(input_images, tf.float32) / 255.0
        input_images.set_shape([(LEFT_CONTEXT + SEQ_LEN) * BATCH_SIZE, HEIGHT, WIDTH, CHANNELS])
        visual_conditions_reshaped = apply_vision_simple(image=input_images, keep_prob=self.keep_prob,
                                                         batch_size=BATCH_SIZE, seq_len=SEQ_LEN)
        visual_conditions = tf.reshape(visual_conditions_reshaped, [BATCH_SIZE, SEQ_LEN, -1])
        visual_conditions = tf.nn.dropout(x=visual_conditions, keep_prob=self.keep_prob)

        rnn_inputs_with_ground_truth = (visual_conditions, targets_normalized)
        rnn_inputs_autoregressive = (visual_conditions, tf.zeros(shape=(BATCH_SIZE, SEQ_LEN, OUTPUT_DIM), dtype=tf.float32))

        internal_cell = tf.nn.rnn_cell.LSTMCell(num_units=RNN_SIZE, num_proj=RNN_PROJ)
        cell_with_ground_truth = SamplingRNNCell(num_outputs=OUTPUT_DIM, use_ground_truth=True, internal_cell=internal_cell)
        cell_autoregressive = SamplingRNNCell(num_outputs=OUTPUT_DIM, use_ground_truth=False, internal_cell=internal_cell)

        def get_initial_state(complex_state_tuple_sizes):
            flat_sizes = tf.contrib.framework.nest.flatten(complex_state_tuple_sizes)
            init_state_flat = [tf.tile(
                multiples=[BATCH_SIZE, 1],
                input=tf.get_variable("controller_initial_state_%d" % i, initializer=tf.zeros_initializer, shape=([1, s]),
                                      dtype=tf.float32))
                for i, s in enumerate(flat_sizes)]
            init_state = tf.contrib.framework.nest.pack_sequence_as(complex_state_tuple_sizes, init_state_flat)
            return init_state

        def deep_copy_initial_state(complex_state_tuple):
            flat_state = tf.contrib.framework.nest.flatten(complex_state_tuple)
            flat_copy = [tf.identity(s) for s in flat_state]
            deep_copy = tf.contrib.framework.nest.pack_sequence_as(complex_state_tuple, flat_copy)
            return deep_copy

        controller_initial_state_variables = get_initial_state(cell_autoregressive.state_size)
        self.controller_initial_state_autoregressive = deep_copy_initial_state(controller_initial_state_variables)
        controller_initial_state_gt = deep_copy_initial_state(controller_initial_state_variables)

        with tf.variable_scope("predictor"):
            out_gt, self.controller_final_state_gt = tf.nn.dynamic_rnn(cell=cell_with_ground_truth,
                                                                  inputs=rnn_inputs_with_ground_truth,
                                                                  sequence_length=[SEQ_LEN] * BATCH_SIZE,
                                                                  initial_state=controller_initial_state_gt,
                                                                  dtype=tf.float32,
                                                                  swap_memory=True, time_major=False)
        with tf.variable_scope("predictor", reuse=True):
            out_autoregressive, self.controller_final_state_autoregressive = tf.nn.dynamic_rnn(cell=cell_autoregressive,
                                                                                inputs=rnn_inputs_autoregressive,
                                                                                sequence_length=[
                                                                                SEQ_LEN] * BATCH_SIZE,
                                                                                initial_state=self.controller_initial_state_autoregressive,
                                                                                dtype=tf.float32,
                                                                                swap_memory=True,
                                                                                time_major=False)

        mse_gt = tf.reduce_mean(tf.squared_difference(out_gt, targets_normalized))
        mse_autoregressive = tf.reduce_mean(tf.squared_difference(out_autoregressive, targets_normalized))
        self.mse_autoregressive_steering = tf.reduce_mean(
            tf.squared_difference(out_autoregressive[:, :, 0], targets_normalized[:, :, 0]))
        self.steering_predictions = (out_autoregressive[:, :, 0] * std[0]) + mean[0]

        total_loss = self.mse_autoregressive_steering + aux_cost_weight * (mse_gt + mse_autoregressive)

        self.optimizer = get_optimizer(total_loss, learning_rate)

        tf.summary.scalar("MAIN_TRAIN_METRIC_rmse_autoregressive_steering", tf.sqrt(self.mse_autoregressive_steering))
        tf.summary.scalar("rmse_gt", tf.sqrt(mse_gt))
        tf.summary.scalar("rmse_autoregressive", tf.sqrt(mse_autoregressive))

        output_config['train_step'] = self.optimizer
        output_config['preds'] = self.steering_predictions
        output_config['mse_autoreg_steering'] = self.mse_autoregressive_steering
        self.summaries = tf.summary.merge_all()
        self.saver = tf.train.Saver(write_version=tf.train.SaverDef.V2)

    def do_epoch(self, session, sequences, labels, mode):
        test_predictions = {}
        valid_predictions = {}

        batch_generator = BatchGenerator(sequence=sequences, seq_len=SEQ_LEN, batch_size=BATCH_SIZE)
        total_num_steps = int(1 + (batch_generator.indices[1] - 1) / SEQ_LEN)
        controller_final_state_gt_cur, controller_final_state_autoregressive_cur = None, None
        acc_loss = np.float128(0.0)
        for step in range(total_num_steps):
            feed_inputs, feed_targets = batch_generator.next()
            feed_dict = {self.inputs: feed_inputs, self.targets: feed_targets}
            if controller_final_state_autoregressive_cur is not None:
                feed_dict.update({self.controller_initial_state_autoregressive:
                                      controller_final_state_autoregressive_cur})
            if controller_final_state_gt_cur is not None:
                feed_dict.update({self.controller_final_state_gt: controller_final_state_gt_cur})
            if mode == "train":
                feed_dict.update({self.keep_prob: self.KEEP_PROB_TRAIN})
                summary, _, loss, controller_final_state_gt_cur, controller_final_state_autoregressive_cur = \
                    session.run([self.summaries,
                                 self.optimizer,
                                 self.mse_autoregressive_steering,
                                 self.controller_final_state_gt,
                                 self.controller_final_state_autoregressive],
                                feed_dict=feed_dict)
                self.train_writer.add_summary(summary, self.global_train_step)
                self.global_train_step += 1
            elif mode == "valid":
                model_predictions, summary, loss, controller_final_state_autoregressive_cur = \
                    session.run([self.steering_predictions,
                                 self.summaries,
                                 self.mse_autoregressive_steering,
                                 self.controller_final_state_autoregressive
                                 ],
                                feed_dict=feed_dict)
                self.valid_writer.add_summary(summary, self.global_train_step)
                self.global_valid_step += 1
                feed_inputs = feed_inputs[:, LEFT_CONTEXT:].flatten()
                steering_targets = feed_targets[:, :, 0].flatten()
                model_predictions = model_predictions.flatten()
                stats = np.stack([steering_targets, model_predictions, (steering_targets - model_predictions) ** 2])
                for i, img in enumerate(feed_inputs):
                    valid_predictions[img] = stats[:, i]
            elif mode == "test":
                print("Performing test")
                model_predictions, controller_final_state_autoregressive_cur = \
                    session.run([
                        self.steering_predictions,
                        self.controller_final_state_autoregressive
                    ],
                        feed_dict=feed_dict)
                feed_inputs = feed_inputs[:, LEFT_CONTEXT:].flatten()
                model_predictions = model_predictions.flatten()
                for i, img in enumerate(feed_inputs):
                    test_predictions[img] = model_predictions[i]
            if mode != "test":
                acc_loss += loss
                print('\r', step + 1, "/", total_num_steps, np.sqrt(acc_loss / (step + 1)))
        print()
        return (np.sqrt(acc_loss / total_num_steps), valid_predictions) if mode != "test" else (None, test_predictions)


from pymongo import MongoClient
from dotenv import load_dotenv
from datetime import datetime
import os
import pymongo

load_dotenv()
MONGO_SERVER = os.getenv("MONGO_SERVER")

class GoogleFeudDB:
    def __init__(self, guild, channel):
        client = MongoClient(MONGO_SERVER)
        self.db = client.gfeuddb
        self.guild = guild
        self.channel = channel
        self.updateLastModifiedTime()

    def createSession(self):
        """
        Creates a session with guild, channel and an empty set of player scores
        """
        try:
            session = {
                "guild": self.guild,
                "channel": self.channel,
                "phrase": str(),
                "scores": dict(),
                "suggestions": dict(),
                "turns": 5,
                "last_modified": datetime.utcnow(),
            }
            # suggestions contains objects with the following shape
            # <suggestion_phrase>: Object
            #   solved: boolean
            #   score: int
            #   solvedBy: string
            #
            # scores has the following shape
            #   scores: object {
            #       id: object { score: int, display_name: string }
            #   }
            self.db.sessions.insert(session)
        except Exception as error:
            print("Failed to create a session: ", error)

    def terminateSession(self):
        """
        Deletes game session
        """
        try:
            return self.db.sessions.delete_one(
                {"guild": self.guild, "channel": self.channel}
            )
        except Exception as error:
            print("Failed terminate game session: ", error)

    def updateLastModifiedTime(self):
        """
        Adds and updates the last_modified time field for a session.
        Used to expire a game session after the expiration time has passed
        """
        try:
            return self.db.sessions.update_one(
                {"guild": self.guild, "channel": self.channel},
                {"$set": {"last_modified": datetime.utcnow()}},
            )
        except Exception as error:
            print("Failed to update last modified time: ", error)

    def getSession(self):
        """
        Returns session document contained as a Python dict using guild and channel. If it doesn't exist, return None
        """
        try:
            return self.db.sessions.find_one(
                {"guild": self.guild, "channel": self.channel}
            )
        except Exception as error:
            print("Failed to retrieve a game session: ", error)

    def getDisplayNameForUser(self, user_id):
        """
        Returns the display_name for a user with the given user_id from scores.
        """
        try:
            session = self.db.sessions.find_one(
                {"guild": self.guild, "channel": self.channel}
            )
            return session["scores"][user_id]["display_name"]
        except Exception as error:
            print("Failed to retrieve the display name: ", error)

    def updatePhrase(self, phrase):
        """
        Updates the phrase in a session.
        """
        try:
            new_phrase = {"phrase": phrase}

            result = self.db.sessions.update_one(
                {"guild": self.guild, "channel": self.channel}, {"$set": new_phrase}
            )
        except Exception as error:
            print("Failed to update phrase: ", error)

    def updateScoreForUser(self, user, score):
        """
        Increase the score for a user. Creates and sets score for user if it doesnt exist
        """
        user_id = str(user.id)
        display_name = str(user.display_name)
        try:
            if self.db.sessions.find_one({f"scores.{user_id}": {"$exists": True}}):
                new_score = {f"scores.{user_id}.score": score}
                operation = {"$inc": new_score}
            else:
                new_entry = {
                    f"scores.{user_id}": {"score": score, "display_name": display_name}
                }
                operation = {"$set": new_entry}

            result = self.db.sessions.update_one(
                {"guild": self.guild, "channel": self.channel}, operation
            )
        except Exception as error:
            print("Failed to update score for user: ", error)

    def insertSuggestions(self, phrase_suggestions):
        """
        Inserts suggestions dict into db
        @param suggestions dictionary
        """
        try:
            suggestions = {"suggestions": phrase_suggestions}
            result = self.db.sessions.update_one(
                {"guild": self.guild, "channel": self.channel}, {"$set": suggestions}
            )
        except Exception as error:
            print("Failed to insert suggestions: ", error)

    def updateSuggestionSolved(self, suggestion, user_id):
        """
        Finds and updates suggestion for user in a session using the given guild and channel values.
        https://stackoverflow.com/questions/28828825/updating-an-object-inside-an-array-with-pymongo
        https://docs.mongodb.com/manual/core/document/#dot-notation
        """
        try:
            solved_suggestion = {"$set": {f"suggestions.{suggestion}.solved": True}}

            result = self.db.sessions.update_one(
                {"guild": self.guild, "channel": self.channel}, solved_suggestion
            )

            solvedBy_suggestion = {
                "$set": {f"suggestions.{suggestion}.solvedBy": user_id}
            }

            result = self.db.sessions.update_one(
                {"guild": self.guild, "channel": self.channel}, solvedBy_suggestion
            )
        except Exception as error:
            print("Failed to update suggestion: ", error)

    def updateTurn(self):
        """
        Update turn count by decrementing turn value.
        """
        try:
            new_turn = {"turns": -1}

            result = self.db.sessions.update_one(
                {"guild": self.guild, "channel": self.channel}, {"$inc": new_turn}
            )
        except Exception as error:
            print("Failed update the turns for this session: ", error)

    def getGoogleSearchPhrase(self):
        """
        Returns a random Google search phrase string.
        Connect to MongoDB server to add/remove phrases.
        """
        try:
            search_phrase = self.db.searchphrases.aggregate([{"$sample": {"size": 1}}])
            # Aggregate returns list of objects. We only need the first random sample
            for phrase in search_phrase:
                return phrase["phrase"]

        except Exception as error:
            print("Failed to fetch search phrase: ", error)

    def checkIfUserIsAdmin(self, discord_author):
        """
        Returns user contained as a Python dict using Discord author's id. If it doesn't exist, return None
        """
        try:
            return self.db.roles.find_one({"user_id": discord_author.id})
        except Exception as error:
            print("Failed to retrieve admin user: ", error)

    def addGoogleSearchPhrase(self, phrase):
        """
        ADMIN: Adds a Google search phrase.
        """
        try:
            trimmed_phrase = phrase.strip()
            self.db.searchphrases.insert({"phrase": phrase})
        except Exception as error:
            print("Failed to add a search phrase: ", error)

    def add_contribution(self, phrase, suggestions, discord_author):
        try:
            trimmed_phrase = phrase.strip()
            # Shape of a contribution document
            self.db.contributions.insert(
                {
                    "user_id": str(discord_author.id),
                    "phrase": phrase,
                    "suggestions": suggestions,
                    "submitted_on": datetime.utcnow(),
                }
            )
            if self.db.contributors.find_one({"user_id": str(discord_author.id)}):
                result = self.db.contributors.update_one(
                    {"user_id": str(discord_author.id)},
                    {
                        "$inc": {
                            "num_of_contributions_today": 1,
                            "num_of_contributions": 1,
                        }
                    },
                )
            else:
                # Shape of a contributor document
                self.db.contributors.insert(
                    {
                        "user_id": str(discord_author.id),
                        "num_of_contributions": 1,
                        "num_of_approved_contributions": 0,
                        "num_of_contributions_today": 1,
                        "daily_limit": 10,
                    }
                )
        except Exception as error:
            print("Failed to add a contribution: ", error)

    def get_oldest_contribution(self):
        """
        Returns the oldest submitted contribution from our collection. Returns None if the collection is empty
        """
        try:
            return self.db.contributions.find().sort("submitted_on", 1).limit(1)[0]
        except Exception as error:
            print("Failed to get oldest contribution: ", error)

    def delete_contribution(self, phrase):
        """
        Deletes a contribution based on its phrase
        """
        try:
            return self.db.contributions.delete_one({"phrase": phrase})
        except Exception as error:
            print("Failed to delete contribution: ", error)

    def increment_approved_contribution(self, user_id):
        """
        Credit a user for their contribution and increase their contributions approved tally
        """
        try:
            return self.db.contributors.update_one(
                {"user_id": str(user_id)},
                {"$inc": {"num_of_approved_contributions": 1}},
            )
        except Exception as error:
            print("Failed to increased number of approved contributions: ", error)

    def get_contributor(self, discord_author):
        try:
            return self.db.contributors.find_one({"user_id": str(discord_author.id)})
        except Exception as error:
            print("Failed to get contributor: ", error)

    def check_if_phrase_is_in_contributions(self, phrase):
        try:
            trimmed_phrase = phrase.strip()
            return self.db.contributions.find_one({"phrase": phrase})
        except Exception as error:
            print("Failed to look for the phrase in contributions: ", error)

    def checkIfSearchPhraseExists(self, phrase):
        """
        ADMIN: Checks if the given phrase already exists
        """
        try:
            trimmed_phrase = phrase.strip()
            return self.db.searchphrases.find_one({"phrase": phrase})
        except Exception as error:
            print("Failed to look for the given phrase: ", error)

    def updateLeaderboard(self, user_id: str) -> pymongo.UpdateOne:
        try:
            return self.db.leaderboard.update_one(
                {"user_id": user_id}, {"$inc": {"wins": 1}}, upsert=True
            )
        except Exception as error:
            print("Failed to update leaderboard for user", user_id, error)

    def getLeaderboard(self, user_ids: list[str]) -> pymongo.CursorType:
        try:
            return self.db.leaderboard.find({"user_id": {"$in": user_ids}})
        except Exception as error:
            print("Failed to get leaderboard for users", user_ids, error)


from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import os

from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.inventory.host import Host
from ansible.module_utils._text import to_bytes
from ansible.plugins.loader import vars_loader
from ansible.utils.collection_loader import AnsibleCollectionRef
from ansible.utils.display import Display
from ansible.utils.vars import combine_vars

display = Display()

def get_plugin_vars(loader, plugin, path, entities):

    data = {}
    try:
        data = plugin.get_vars(loader, path, entities)
    except AttributeError:
        try:
            for entity in entities:
                if isinstance(entity, Host):
                    data.update(plugin.get_host_vars(entity.name))
                else:
                    data.update(plugin.get_group_vars(entity.name))
        except AttributeError:
            if hasattr(plugin, 'run'):
                raise AnsibleError("Cannot use v1 type vars plugin %s from %s" % (plugin._load_name, plugin._original_path))
            else:
                raise AnsibleError("Invalid vars plugin %s from %s" % (plugin._load_name, plugin._original_path))
    return data

def get_vars_from_path(loader, path, entities, stage):

    data = {}

    vars_plugin_list = list(vars_loader.all())
    for plugin_name in C.VARIABLE_PLUGINS_ENABLED:
        if AnsibleCollectionRef.is_valid_fqcr(plugin_name):
            vars_plugin = vars_loader.get(plugin_name)
            if vars_plugin is None:
                # Error if there's no play directory or the name is wrong?
                continue
            if vars_plugin not in vars_plugin_list:
                vars_plugin_list.append(vars_plugin)

    for plugin in vars_plugin_list:
        if plugin._load_name not in C.VARIABLE_PLUGINS_ENABLED and getattr(plugin, 'REQUIRES_WHITELIST', False):
            # 2.x plugins shipped with ansible should require whitelisting, older or non shipped should load automatically
            continue

        has_stage = hasattr(plugin, 'get_option') and plugin.has_option('stage')

        # if a plugin-specific setting has not been provided, use the global setting
        # older/non shipped plugins that don't support the plugin-specific setting should also use the global setting
        use_global = (has_stage and plugin.get_option('stage') is None) or not has_stage

        if use_global:
            if C.RUN_VARS_PLUGINS == 'demand' and stage == 'inventory':
                continue
            elif C.RUN_VARS_PLUGINS == 'start' and stage == 'task':
                continue
        elif has_stage and plugin.get_option('stage') not in ('all', stage):
            continue

        data = combine_vars(data, get_plugin_vars(loader, plugin, path, entities))

    return data

def get_vars_from_inventory_sources(loader, sources, entities, stage):

    data = {}
    for path in sources:

        if path is None:
            continue
        if ',' in path and not os.path.exists(path):  # skip host lists
            continue
        elif not os.path.isdir(to_bytes(path)):
            # always pass the directory of the inventory source file
            path = os.path.dirname(path)

        data = combine_vars(data, get_vars_from_path(loader, path, entities, stage))

    return data


import cv2
import os
import argparse
import sys
import shutil
from tqdm import tqdm

parser = argparse.ArgumentParser(description='Convert video to videos of smaller length')
parser.add_argument('-i', type=str, help='input video path')
parser.add_argument('-o', type=str, help='output FOLDER path')
parser.add_argument('-f', type=str, help='output videos format (such as mp4/avi)')
parser.add_argument('-p', type=int, help='small videos of how many SECONDS')
parser.add_argument('-s', type=tuple, default=(0, 0), help='[OPTIONAL] resize output videos frames')

args = parser.parse_args()

def main(inpVideoPath, outFolderPath, outFormat, seconds, resize):
    print(inpVideoPath)
    cap = cv2.VideoCapture(inpVideoPath)
    if not cap.isOpened():
        print("could not open :", inpVideoPath)
        sys.exit()

    numFrames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = int(cap.get(cv2.CAP_PROP_FPS))

    NUM_OUTPUT_VIDEOS = int(numFrames / (fps * seconds))
    FRAMES_PER_VIDEO = fps * seconds
    OUTPUT_VIDEO_SIZE = resize if resize[0] != 0 else (width, height)
    print('[video loaded succesfully]')

    if os.path.exists(outFolderPath):
        shutil.rmtree(outFolderPath)

    outs = {}

    os.mkdir(outFolderPath)
    print("NUM_OUTPUT_VIDEOS", NUM_OUTPUT_VIDEOS)
    for ii in range(NUM_OUTPUT_VIDEOS):
        # os.mkdir( os.path.join(outFolderPath,str(i)) )
        outs[ii] = cv2.VideoWriter(os.path.join(outFolderPath, str(ii) + '.' + outFormat),
                                   cv2.VideoWriter_fourcc(*'DIVX'), fps, frameSize=OUTPUT_VIDEO_SIZE)
    print('[output folder & subfolders created]')

    j = 0

    print('[saving videos]')
    for i in tqdm(range(numFrames)):

        if i % FRAMES_PER_VIDEO == 0 and i != 0:
            outs[j].release()
            j += 1
            if j == NUM_OUTPUT_VIDEOS:
                j -= 1

        ret, frame = cap.read()
        frame = cv2.resize(frame, OUTPUT_VIDEO_SIZE)

        outs[j].write(frame)

    print('[done!]')

if __name__ == '__main__':
    main(args.i, args.o, args.f, args.p, args.s)


import sys
import numpy as np

from typing import List, Callable, Optional, Tuple, Sequence
from collections import UserDict
from EmbeddingDictionary import EntryKey, EntryValue, EmbeddingDictionary
from Jsonifiable import JsonEncodable

class WordSimilarity(JsonEncodable):
    def __init__(self, word: str, similarityScore: float):
        if not isinstance(word, str) or not isinstance(similarityScore, float):
            raise TypeError('WordSimilarity expects (str, float), but got (%s, %s)' % \
                            (type(word), type(similarityScore)))
        self._word = word
        self._similarityScore = similarityScore

    @property
    def word(self):
        return self._word

    @property
    def similarity(self):
        return self._similarityScore * 100

    def __str__(self):
        return "\"%s\": %.2f%%" % (self.word, self.similarity)

    def __repr__(self):
        return "WordSimilarity(%s, %.2f)" % (self.word, self._similarityScore)

    def __lt__(self, other):
        return self.similarity < other.similarity

    def jsonDefault(self):
        return dict(word=self.word, similarityScore=self.similarity)

class WordSimilarityList(np.ndarray, JsonEncodable):

    def __new__(cls, inputArray):
        obj = np.asarray(inputArray).view(cls)
        return obj

    def sortBySimilarity(self, reverse=False):
        if reverse:
            self[::-1].sort()
        else:
            self.sort()

        return self

    def stringify(self, strFormat: str, start: Optional[int] = None, end: Optional[int] = None):
        """
        stringify the list in given format and range [start, end)
        :param (str) strFormat: format to print each element
        :param (int) start: start index of element to be printed (inclusive)
        :param (int) end: end index of element to be printed (exclusive)
        :return (str): stringified list
        """
        start = 0 if start is None else start
        end = len(self) if end is None else end
        ret = ""
        for i in range(start, start + end):
            if not isinstance(self[i], WordSimilarity):
                raise TypeError('WordSimilarityList expects element type WordSimilarity, '
                                'but got %s' % type(self[i]))
            ret += strFormat % (i+1, self[i])
        ret = ret[:-1]  # ignore the additional ending format
        return ret

    def __str__(self):
        return self.stringify("%d. %s\n")

    def jsonDefault(self):
        return self.tolist()

class CategorySimilarityDict(UserDict, JsonEncodable):
    def stringify(self, strFormat):
        ret = ""
        for cat, simList in self.items():
            ret += strFormat % (cat, simList.stringify("\t%d. %s\n"))

        return ret[:-1]

    def __str__(self):
        return self.stringify("%s:\n%s\n")

    def __repr__(self):
        return "CategorySimilarityDict(%s)" % super().__repr__()

    def jsonDefault(self):
        return self.data

class SimilarityDictionary(object):
    def __init__(self, embedDict: EmbeddingDictionary,
                 similarityFunction: Callable[[Sequence, Sequence], float]):
        """
        :param embedDict: a reference to initialized an EmbeddingDictionary object
        :param similarityFunction: function to compute similarity with
        """
        self._embedDict = embedDict
        self._simFunc = similarityFunction
        self._data = dict()

    def __contains__(self, item: EntryKey):
        if not isinstance(item, EntryKey):
            raise TypeError('SimilarityDictionary expects type EntryKey as key, '
                            'but got %s' % type(item))
        if item.isEmpty() or item.word is None:
            raise KeyError("EntryKey.word must be set!")
        if item.category is None:
            return item.word in self._data
        return item.word in self._data and item.category in self._data[item.word]

    def __str__(self):
        ret = ""
        for word in self._data.keys():
            ret += "%s:\n" % word
            for cat, simList in self._data[word].items():
                ret += "\t%s:\n%s\n" % (cat, simList.stringify('\t\t%d. %s\n'))
            # ret += "%s:\n%s\n" % (key, self.data[key].stringify('\t%d. %s\n'))
        return ret[:-1]

    @property
    def embedDict(self):
        return self._embedDict

    @embedDict.setter
    def embedDict(self, newDict: EmbeddingDictionary):
        if self._embedDict is newDict:
            return
        if not isinstance(newDict, EmbeddingDictionary):
            raise TypeError("expects ")
        print("[INFO] SimilarityDictionary dictionary changed, clearing similarity data",
              file=sys.stderr)
        self._data.clear()      # clears data as new dict is used and old data should be nullified
        self._embedDict = newDict

    @property
    def similarityFunction(self):
        return self._simFunc

    @similarityFunction.setter
    def similarityFunction(self, newSimFunc):
        if self._simFunc is newSimFunc:
            return
        print("[INFO] SimilarityDictionary similarity function changed, "
              "clearing similarity data", file=sys.stderr)
        self._data.clear()
        self._simFunc = newSimFunc

    def similarityBetweenWords(self, key1: EntryKey, key2: EntryKey) -> float:
        if not key1.isFull() or not key2.isFull():
            raise ValueError("EntryKeys must be full (both word and category provided)")
        return self._simFunc(self.embedDict[key1].embedding, self.embedDict[key2].embedding)

    def similarityInCategory(self, key: EntryKey, forceUpdate=False) -> WordSimilarityList:
        if not key.isFull():
            raise ValueError("EntryKey must be fully set!")
        # if the similarity for this word in this category is not computed,
        # compute
        if forceUpdate or key not in self:
            val: EntryValue = self.embedDict[key]
            otherVals: List[Tuple[str, EntryValue]] = \
                [(w, v) for w, v in self.embedDict.category[key.category].items()
                 if w != key.word]

            if key.word not in self._data:
                self._data[key.word] = CategorySimilarityDict()
            self._data[key.word][key.category] = WordSimilarityList(
                [WordSimilarity(w, self._simFunc(val.embedding, v.embedding))
                 for w, v in otherVals]
            )
        return self._data[key.word][key.category]

    def similarity(self, key: EntryKey, forceUpdate=False):
        if key.isEmpty() or key.word is None:
            raise ValueError("EntryKey.word must be set!")
        simsByCat = CategorySimilarityDict()
        if key.category is None:
            for cat in self.embedDict[key].keys():
                simsByCat[cat] = self.similarityInCategory(EntryKey(key.word, cat), forceUpdate)
        else:
            simsByCat[key.category] = self.similarityInCategory(key, forceUpdate)
        return simsByCat

    def rankedSimilarity(self, key: EntryKey, top: Optional[int] = None,
                         bottom: Optional[int] = None, forceUpdate=False):
        if key.isEmpty() or key.word is None:
            raise ValueError("EntryKey.word must be set!")
        simsByCat = self.similarity(key, forceUpdate)
        if top is not None and bottom is not None:
            raise ValueError("parameters top and bottom are mutually exclusive")
        for cat in simsByCat.keys():
            if top:
                simsByCat[cat] = simsByCat[cat].sortBySimilarity(reverse=True)[:top]
            elif bottom:
                simsByCat[cat] = simsByCat[cat].sortBySimilarity(reverse=True)[bottom:]
            else:
                simsByCat[cat] = simsByCat[cat].sortBySimilarity(reverse=True)

        return simsByCat


from solver.lp_tools import LpProblem, DecisionSeries, DecisionMatrix
from solver.fpl_tools import Projections, Squad
from definitions import root_dir
import numpy as np
from typing import List, Callable, Dict, Iterable
import pandas as pd
import pulp

class Solver:
    """
    Framework for Fantasy Premier League linear optimisation program
    ================================================================
    Credit to @sertalpbilal for inspiration
    """
    def __init__(
            self,
            horizon: int = 8,
            bench_weights: List[List[float]] = None,
            vc_weight: float = 0.05,
            penalties: Dict[Callable, float] = None,
            budget_decay_rate: float = 0,
            transfer_pattern: List[int] = None,
            million_value=0.45,
            exclude_everton: bool = False,
    ):
        """
        :param horizon: Number of future GWs included in optimisation, max=8
        :param bench_weights: [bench_gk_weight, 1st_bench_weight, 2nd_bench_weight, 3rd_bench_weight]
        :param vc_weight: Extra weight of vice captain in objective function
        :param penalties: Dict of penalty functions taking self, current objective and parameter
        :param budget_decay_rate: Linear decay parameter; budget reduces by budget_decay_rate each future GW
        :param transfer_pattern: Manual override for transfer logic, nth element is transfers in n GWs from now
        :param million_value: User-defined value of £1 million in FPL, used to factor in price changes. "solve" solves.
        :param exclude_everton: If True, no Everton players are included in solutions
        """
        self.horizon = horizon
        if bench_weights is None:
            self.bench_weights = np.array([
                [0.001, 0.15,   0.01,   0.001],
                [0.01,  0.22,   0.03,   0.004],
                [0.02,  0.26,   0.045,  0.006],
                [0.03,  0.36,   0.075,  0.016],
                [0.035, 0.40,   0.095,  0.02],
                [0.04,  0.43,   0.12,   0.03],
                [0.043, 0.46,   0.14,   0.035],
                [0.045, 0.48,   0.15,   0.04]
            ][:horizon])
        else:
            self.bench_weights = bench_weights
        self.vc_weight = vc_weight
        self.penalties = penalties
        self.budget_decay_rate = budget_decay_rate
        self.transfer_pattern = transfer_pattern
        if million_value == 'solve':
            # Investigate value of 1 million in current FPL game state
            pass
        else:
            pass
        self.million_value = million_value
        self.exclude_everton = exclude_everton

        self.rolls = None
        self.force_chips = None

    def solve(
            self,
            projections: Projections,
            initial_squad: Squad,
            next_gw: int = None,
            force_chips: Dict[int, str] = None,
            force_players: Dict[str, list] = None,
            force_transfers: Dict[int, dict] = None,
            price_changes: Dict[str, Iterable] = None,
            time_limit: float = None,
            optimizer: type = pulp.GUROBI,
            message: bool = True
    ):
        """
        Method for solving specific instance of FPL problem
        :param projections: DataFrame containing projections, such as from get_fplreview output
        :param next_gw: Next GW
        :param initial_squad: Squad object with data for current itb, fts and active chip
        :param force_chips: {gw_a: wildcard, gw_b: bench_boost, gw_c: triple_captain, gw_d: free_hit} forces wildcard
            in gw_a, ...m free hit in gw_d.
        :param force_players: {'include': [{players to include}], 'exclude': [{players to exclude}]}
        :param force_transfers: {gw_a: {'in': [{player list 1}], 'out': [{player list 2}]}, gw_b: ...} forces players in
            player list 1 to be transferred in for players in player list 2 in gw_a
        :param price_changes:
        :param time_limit: Set time limit for optimisation (in seconds)
        :param optimizer: Which optimiser to use
        :param message: if False, suppress optimisation output:
        :return: class Solution containing transfer and team selection data for optimal solution
        """
        if next_gw is None:
            next_gw = sorted([int(column.split('_')[0]) for column in projections.columns if column.endswith('Pts')])[0]
        # Set up useful references
        initial_players = initial_squad.players
        initial_itb = initial_squad.itb
        initial_fts = initial_squad.fts
        active_chip = initial_squad.active_chip
        players = projections.index
        positions = ('G', 'D', 'M', 'F')
        teams = projections['Team'].unique()
        gw_interval = list(range(next_gw, next_gw + self.horizon))

        # Initialise optimisation model
        prob = LpProblem('FPL_transfer_optimisation')

        # Initialise decision variables
        default_args = {'index': players, 'columns': gw_interval, 'column_type': 'gw', 'model': prob}
        lineup = DecisionMatrix.lp_variable('lineup', **default_args)
        bench_gk = DecisionMatrix.lp_variable('bench_gk', **default_args)
        bench_1 = DecisionMatrix.lp_variable('bench_1', **default_args)
        bench_2 = DecisionMatrix.lp_variable('bench_2', **default_args)
        bench_3 = DecisionMatrix.lp_variable('bench_3', **default_args)
        squad = DecisionMatrix.lp_variable('squad', **default_args)
        prob += squad == lineup + bench_gk + bench_1 + bench_2 + bench_3
        squad[next_gw - 1] = pd.Series(squad.index).isin(initial_players).astype(int)
        captain = DecisionMatrix.lp_variable('captain', **default_args)
        vice_captain = DecisionMatrix.lp_variable('vice_captain', **default_args)
        transfer_in = DecisionMatrix.lp_variable('transfer_in', **default_args)
        transfer_out = DecisionMatrix.lp_variable('transfer_out', **default_args)
        itb = DecisionSeries(data=[initial_itb], index=[next_gw - 1], model=prob)
        # itb is previous GW's itb + revenue from outgoing players + cost of incoming players
        for i, gw in enumerate(gw_interval):
            itb[gw] = itb[gw - 1] + (transfer_out[gw] * projections['SV']).sum() - \
                      (transfer_in[gw] * projections['BV']).sum() - self.budget_decay_rate

        # Add problem constraints to optimisation model
        prob += squad == squad.lag(1) + transfer_in - transfer_out  # New squad is previous squad plus transfers
        prob += squad.drop(next_gw - 1, axis=1) <= 1  # Each player can only appear in the squad once
        prob += lineup.sum() == 11  # Lineup contains 11 players
        prob += bench_gk.sum() == 1     # There is 1 bench GK;
        prob += bench_1.sum() == 1  # 1 1st bench slot;
        prob += bench_2.sum() == 1  # 1 2nd bench slot;
        prob += bench_3.sum() == 1  # 1 3rd bench slot;
        prob += captain.sum() == 1  # 1 Captain;
        prob += transfer_out.sum() == transfer_in.sum()     # Transfers in must be same as transfers out

        prob += vice_captain.sum() == 1     # 1 vice-captain
        prob += captain <= lineup   # Captain must be in lineup
        prob += vice_captain <= lineup  # Vice-captain must be in lineup
        prob += captain + vice_captain <= 1     # Captain and vice-captain must be different players
        for position, limit in zip(positions, (2, 5, 5, 3)):
            prob += squad[projections['Pos'] == position].sum() == limit    # Set squad position structure
        for team in teams:
            prob += squad[projections['Team'] == team].sum() <= 3   # No more than 3 players from each team
        if self.exclude_everton:
            prob += squad[projections['Team'] == 'Everton'].sum() == 0  # Option to exclude Everton players
        prob += bench_gk <= (projections['Pos'] == 'G')     # Bench GK must be a goalkeeper
        prob += (lineup * (projections['Pos'] == 'G')).sum() == 1   # There must be 1 goalkeeper in lineup
        prob += (lineup * (projections['Pos'] == 'D')).sum() >= 3   # There must be at least 3 defenders in lineup
        prob += itb[[False] + [True] * self.horizon] >= 0   # The itb amount must be non-negative for future GWs

        # Set up transfer logic
        transfer_args = {'index': gw_interval, 'column_type': 'gw', 'model': prob, 'cat': 'Integer'}
        aux = DecisionSeries.lp_variable('aux', **transfer_args)
        free_transfers = DecisionSeries(data=[initial_fts], index=[next_gw - 1], model=prob) + DecisionSeries.\
            lp_variable('free_transfers', **transfer_args)
        penalised_transfers = DecisionSeries.lp_variable('penalised_transfers', **transfer_args)
        transfer_counts = transfer_in.sum()
        frees_minus_transfers = free_transfers.lag(1) - transfer_counts
        lower_bound = aux * 15 - 14
        upper_bound = aux * 2
        if initial_fts > 1:
            prob += transfer_counts[next_gw] >= 1
        prob += frees_minus_transfers >= lower_bound
        prob += frees_minus_transfers <= upper_bound
        prob += free_transfers == aux + 1
        # penalised_transfers is max(transfers - frees, 0)
        prob += penalised_transfers >= -frees_minus_transfers
        prob += penalised_transfers >= 0

        ev_values = projections[[f'{gw}_Pts' for gw in gw_interval]]    # Restructure projections data for easier
        ev_values.columns = gw_interval                                 # manipulation
        objective = ((lineup + captain) * ev_values).sum()  # Objective function is sum of lineup and captain pts
        objective += (vice_captain * self.vc_weight * ev_values).sum()  # Add vice-captain weight
        for loc, bench_slot in enumerate((bench_gk, bench_1, bench_2, bench_3)):
            objective += (bench_slot * ev_values).sum() * self.bench_weights[:, loc]    # Add bench weights to objective
        if force_transfers is None:
            objective -= penalised_transfers * 4    # Take away 4 points from each hit taken
            if force_chips is not None:
                self.force_chips = force_chips
                for gw in force_chips:
                    if force_chips[gw] == 'wildcard':
                        objective[gw] += penalised_transfers[gw] * 4    # Remove penalised points in wildcard week

        if force_players is not None:
            for player in force_players['include']:
                prob += squad.T[player].drop(next_gw - 1) == 1
            for player in force_players['exclude']:
                prob += squad.T[player].drop(next_gw - 1) == 0
            if 'include_for_gw' in force_players:
                for gw in force_players['include_for_gw']:
                    try:
                        prob += squad[force_players['include_for_gw'][gw], gw] == 1
                    except ValueError:
                        pass
            if 'exclude_for_gw' in force_players:
                for gw in force_players['exclude_for_gw']:
                    try:
                        prob += squad[force_players['exclude_for_gw'][gw], gw] == 0
                    except ValueError:
                        pass
        self.rolls = frees_minus_transfers + penalised_transfers
        prob += self.rolls <= 2

        if self.penalties is not None:
            if time_decay in self.penalties:
                self.penalties[time_decay] = self.penalties.pop(time_decay)     # Apply time decay after other penalties
            for penalty, parameter in self.penalties.items():
                objective = penalty(objective, self, parameter)  # Apply external penalty functions

        # Apply price change EV
        if price_changes is not None:
            gws_remaining = 38 - next_gw + 1
            for player in price_changes['rise']:
                objective[next_gw] += self.million_value / 30 * squad[player, next_gw] * gws_remaining
            for player in price_changes['drop']:
                objective[next_gw] -= self.million_value / 10 * squad[player, next_gw] * gws_remaining

        prob.model += objective.sum()
        prob.solve(time_limit=time_limit, optimizer=optimizer, message=message)

        return Solution(lineup, bench_gk, bench_1, bench_2, bench_3, captain, vice_captain, objective, transfer_in,
                        transfer_out, itb, projections, free_transfers, penalised_transfers, force_chips)

class ObjectivePenalty:     # TODO
    def __init__(self, penalty_func, parameter):
        pass

    def apply(self, objective):
        pass

def ft_penalty(objective, solver, parameter):
    if parameter == 0:  # Unsolved glitch occurs when ft_penalty = 0
        parameter = 0.01
    if solver.force_chips is not None:
        for gw in solver.force_chips:
            if solver.force_chips[gw] == 'wildcard':
                solver.rolls[gw] = 0
    return objective + solver.rolls * parameter

def time_decay(objective, solver, parameter):
    return objective * np.array([parameter**i for i in range(solver.horizon)])

class Solution:
    def __init__(self, lineup, bench_gk, bench_1, bench_2, bench_3, captain, vice_captain, objective,
                 transfer_in, transfer_out, itb, projections, free_transfers, penalised_transfers, chips):
        self.data = projections    # Get current EV projections
        self.lineup = lineup
        self.bench_gk = bench_gk
        self.bench_1 = bench_1
        self.bench_2 = bench_2
        self.bench_3 = bench_3
        self.captain = captain
        self.vice_captain = vice_captain
        self.transfer_in = transfer_in
        self.transfer_out = transfer_out
        self.itb = itb
        self.objective = objective
        self.free_transfers = free_transfers
        self.penalised_transfers = penalised_transfers
        self.chips = chips
        self.bench_weights = None

    def __str__(self):
        output = ''
        lineup, bench_gk, bench_1, bench_2, bench_3, captain, vice_captain, transfer_in, transfer_out = \
            [variable.get_value() for variable in (
                self.lineup, self.bench_gk, self.bench_1, self.bench_2, self.bench_3, self.captain, self.vice_captain,
                self.transfer_in, self.transfer_out)]
        for gw in self.lineup.columns:
            players_in = [self.data.loc[i, 'Name'] for i in transfer_in[gw][pd.Series(transfer_in[gw]) >= 0.5].index]
            players_out = [self.data.loc[i, 'Name'] for i in transfer_in[gw][pd.Series(transfer_out[gw]) >= 0.5].index]
            players_in, players_out = [player for player in players_in if player not in players_out], \
                                      [player for player in players_out if player not in players_in]
            output += '-' * 60 + '\n' + 'Transfer out: ' + ', '.join(players_out) + '\n'
            output += 'Transfer in: ' + ', '.join(players_in) + '\n' + '-' * 60 + '\n'
            gw_lineup = lineup[gw][pd.Series(lineup[gw]) >= 0.5].index
            for position in ('G', 'D', 'M', 'F'):
                pos_players = [self.data.loc[i, 'Name'] + ' (C)' * int(captain[i, gw]) + ' (VC)' *
                               int(vice_captain[i, gw]) for i in sorted(gw_lineup, key=lambda i: self.data.loc[
                                i, f'{gw}_Pts'], reverse=True) if self.data.loc[i, 'Pos'] == position]
                output += '  '.join(pos_players).center(60, ' ') + '\n'
            gw_bench_gk = self.data.loc[bench_gk[gw][pd.Series(bench_gk[gw]) >= 0.5].index[0], 'Name']
            gw_bench_1 = self.data.loc[bench_1[gw][pd.Series(bench_1[gw]) >= 0.5].index[0], 'Name']
            gw_bench_2 = self.data.loc[bench_2[gw][pd.Series(bench_2[gw]) >= 0.5].index[0], 'Name']
            gw_bench_3 = self.data.loc[bench_3[gw][pd.Series(bench_3[gw]) >= 0.5].index[0], 'Name']
            bench = [gw_bench_gk, gw_bench_1, gw_bench_2, gw_bench_3]
            output += '-' * 60 + '\n' + '  '.join(bench).center(60, ' ') + '\n' + '=' * 60 + '\n'
            output += f'GW{gw} Objective: {pulp.value(self.objective[gw])}' + '\n' * 3
        return output[:-3]

    def eval(self, gws_from_now=0, bench_weights=None, vc_weight=0):
        if bench_weights is None:
            bench_weights = np.array([0.001, 0.15, 0.01, 0.001])
        gw = self.lineup.columns[gws_from_now]
        ev_sum = 0
        for variable in (self.lineup, self.captain):
            ev_sum += (variable[gw].get_value() * self.data[f'{gw}_Pts']).sum()
        for bench_slot, weight in zip([self.bench_gk, self.bench_1, self.bench_2, self.bench_3], bench_weights):
            ev_sum += (bench_slot[gw].get_value() * self.data[f'{gw}_Pts']).sum() * weight
        ev_sum += (self.vice_captain[gw].get_value() * self.data[f'{gw}_Pts']).sum() * vc_weight

        penalised_transfers = self.penalised_transfers.get_value()
        if self.chips is not None:
            for gw in self.chips:
                if self.chips[gw] == 'wildcard':
                    penalised_transfers[gw] = 0
        ev_sum -= penalised_transfers.sum() * 4

        return ev_sum

def main():
    gw = 7
    ev = Projections.get_fplreview(2021, gw)
    my_squad = Squad.from_id(1433, gw - 1)
    print(my_squad)

    solver = Solver(
        horizon=6,
        penalties={
            ft_penalty: 1.2,
            time_decay: 0.84
        },
        million_value=0.45
    )
    solution = solver.solve(
        projections=ev,
        initial_squad=my_squad,
        force_players={'include': [], 'exclude': [ev.get_player('Alonso')], 'include_for_gw': {}, 'exclude_for_gw': {}},
        force_chips={8: 'wildcard'},
        price_changes={'rise': [ev.get_player('Rudiger')], 'drop': [ev.get_player('Shaw')]}
    )
    print(solution)

if __name__ == '__main__':
    main()


import vectorbt as vbt

import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import pytz
from dateutil.parser import parse
import ipywidgets as widgets
from copy import deepcopy
from tqdm import tqdm
import imageio
from IPython import display
import plotly.graph_objects as go
import itertools
import dateparser
import gc

# Enter your parameters here
seed = 42
symbol = 'BTC-USD'
metric = 'total_return'

start_date = datetime(2018, 1, 1, tzinfo=pytz.utc)  # time period for analysis, must be timezone-aware
end_date = datetime(2020, 1, 1, tzinfo=pytz.utc)
time_buffer = timedelta(days=100)  # buffer before to pre-calculate SMA/EMA, best to set to max window
freq = '1D'

vbt.settings.portfolio['init_cash'] = 100.  # 100$
vbt.settings.portfolio['fees'] = 0.0025  # 0.25%
vbt.settings.portfolio['slippage'] = 0.0025  # 0.25%

# Download data with time buffer
cols = ['Open', 'High', 'Low', 'Close', 'Volume']
ohlcv_wbuf = vbt.YFData.download(symbol, start=start_date-time_buffer, end=end_date).get(cols)

ohlcv_wbuf = ohlcv_wbuf.astype(np.float64)

# print(ohlcv_wbuf.shape)
# print(ohlcv_wbuf.columns)

# Create a copy of data without time buffer
wobuf_mask = (ohlcv_wbuf.index >= start_date) & (ohlcv_wbuf.index <= end_date) # mask without buffer

ohlcv = ohlcv_wbuf.loc[wobuf_mask, :]

# print(ohlcv.shape)

# Plot the OHLC data
# ohlcv_wbuf.vbt.ohlcv.plot().show_svg()
# remove show_svg() to display interactive chart!
ohlcv_wbuf.vbt.ohlcv.plot().show()

## SINGLE WINDOW COMBINATION

fast_window = 30
slow_window = 80

# Pre-calculate running windows on data with time buffer
fast_ma = vbt.MA.run(ohlcv_wbuf['Open'], fast_window)
slow_ma = vbt.MA.run(ohlcv_wbuf['Open'], slow_window)

print(fast_ma.ma.shape)
print(slow_ma.ma.shape)

# Remove time buffer
fast_ma = fast_ma[wobuf_mask]
slow_ma = slow_ma[wobuf_mask]

# there should be no nans after removing time buffer
assert(~fast_ma.ma.isnull().any())
assert(~slow_ma.ma.isnull().any())

print(fast_ma.ma.shape)
print(slow_ma.ma.shape)

# Generate crossover signals
dmac_entries = fast_ma.ma_crossed_above(slow_ma)
dmac_exits = fast_ma.ma_crossed_below(slow_ma)

fig = ohlcv['Open'].vbt.plot(trace_kwargs=dict(name='Price'))
fig = fast_ma.ma.vbt.plot(trace_kwargs=dict(name='Fast MA'), fig=fig)
fig = slow_ma.ma.vbt.plot(trace_kwargs=dict(name='Slow MA'), fig=fig)
fig = dmac_entries.vbt.signals.plot_as_entry_markers(ohlcv['Open'], fig=fig)
fig = dmac_exits.vbt.signals.plot_as_exit_markers(ohlcv['Open'], fig=fig)

fig.show_svg()

# Signal stats
print(dmac_entries.vbt.signals.stats(settings=dict(other=dmac_exits)))

# Plot signals
fig = dmac_entries.vbt.signals.plot(trace_kwargs=dict(name='Entries'))
dmac_exits.vbt.signals.plot(trace_kwargs=dict(name='Exits'), fig=fig).show_svg()

# Build partfolio, which internally calculates the equity curve

# Volume is set to np.inf by default to buy/sell everything
# You don't have to pass freq here because our data is already perfectly time-indexed
dmac_pf = vbt.Portfolio.from_signals(ohlcv['Close'], dmac_entries, dmac_exits)

# Print stats
print(dmac_pf.stats())

# Plot trades
print(dmac_pf.trades.records)
dmac_pf.trades.plot().show_svg()

# Now build portfolio for a "Hold" strategy
# Here we buy once at the beginning and sell at the end
hold_entries = pd.Series.vbt.signals.empty_like(dmac_entries)
hold_entries.iloc[0] = True
hold_exits = pd.Series.vbt.signals.empty_like(hold_entries)
hold_exits.iloc[-1] = True
hold_pf = vbt.Portfolio.from_signals(ohlcv['Close'], hold_entries, hold_exits)

# Equity
fig = dmac_pf.value().vbt.plot(trace_kwargs=dict(name='Value (DMAC)'))
hold_pf.value().vbt.plot(trace_kwargs=dict(name='Value (Hold)'), fig=fig).show_svg()

# Now we will implement an interactive window slider to easily compare windows by their performance.

min_window = 2
max_window = 100

perf_metrics = ['total_return', 'positions.win_rate', 'positions.expectancy', 'max_drawdown']
perf_metric_names = ['Total return', 'Win rate', 'Expectancy', 'Max drawdown']

windows_slider = widgets.IntRangeSlider(
    value=[fast_window, slow_window],
    min=min_window,
    max=max_window,
    step=1,
    layout=dict(width='500px'),
    continuous_update=True
)
dmac_fig = None
dmac_img = widgets.Image(
    format='png',
    width=vbt.settings['plotting']['layout']['width'],
    height=vbt.settings['plotting']['layout']['height']
)
metrics_html = widgets.HTML()

def on_value_change(value):
    global dmac_fig

    # Calculate portfolio
    fast_window, slow_window = value['new']
    fast_ma = vbt.MA.run(ohlcv_wbuf['Open'], fast_window)
    slow_ma = vbt.MA.run(ohlcv_wbuf['Open'], slow_window)
    fast_ma = fast_ma[wobuf_mask]
    slow_ma = slow_ma[wobuf_mask]
    dmac_entries = fast_ma.ma_crossed_above(slow_ma)
    dmac_exits = fast_ma.ma_crossed_below(slow_ma)
    dmac_pf = vbt.Portfolio.from_signals(ohlcv['Close'], dmac_entries, dmac_exits)

    # Update figure
    if dmac_fig is None:
        dmac_fig = ohlcv['Open'].vbt.plot(trace_kwargs=dict(name='Price'))
        fast_ma.ma.vbt.plot(trace_kwargs=dict(name='Fast MA'), fig=dmac_fig)
        slow_ma.ma.vbt.plot(trace_kwargs=dict(name='Slow MA'), fig=dmac_fig)
        dmac_entries.vbt.signals.plot_as_entry_markers(ohlcv['Open'], fig=dmac_fig)
        dmac_exits.vbt.signals.plot_as_exit_markers(ohlcv['Open'], fig=dmac_fig)
    else:
        with dmac_fig.batch_update():
            dmac_fig.data[1].y = fast_ma.ma
            dmac_fig.data[2].y = slow_ma.ma
            dmac_fig.data[3].x = ohlcv['Open'].index[dmac_entries]
            dmac_fig.data[3].y = ohlcv['Open'][dmac_entries]
            dmac_fig.data[4].x = ohlcv['Open'].index[dmac_exits]
            dmac_fig.data[4].y = ohlcv['Open'][dmac_exits]
    dmac_img.value = dmac_fig.to_image(format="png")

    # Update metrics table
    sr = pd.Series([dmac_pf.deep_getattr(m) for m in perf_metrics],
                   index=perf_metric_names, name='Performance')
    metrics_html.value = sr.to_frame().style.set_properties(**{'text-align': 'right'}).render()

windows_slider.observe(on_value_change, names='value')
on_value_change({'new': windows_slider.value})

dashboard = widgets.VBox([
    widgets.HBox([widgets.Label('Fast and slow window:'), windows_slider]),
    dmac_img,
    metrics_html
])
dashboard

dashboard.close() # after using, release memory and notebook metadata

gc.collect()


from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage, InvalidPage
from django.db.models import Sum

from .models import User, Transaction, Profile

import calendar

monthsConverter = {
    month: index for index, month in enumerate(calendar.month_abbr) if month
}

# Helper to paginate query sets passed from views.py
def paginate(queryset, page, per_page):
    paginator = Paginator(queryset, per_page)

    try:
        transactions = paginator.page(page)
    except PageNotAnInteger:
        transactions = paginator.page(1)
    except EmptyPage:
        transactions = paginator.page(paginator.num_pages)

    return transactions

def get_profile(user):
    try:
        profile = Profile.objects.get(user=user)
    except ObjectDoesNotExist:
        return 404

    return profile

def get_transactions(user):
    user_profile = get_profile(user)
    return user_profile.transactions.all()

def order_transactions_by(field, transactions_list):
    return transactions_list.order_by(f"-{field}")

def transactions_in(category, transactions_list):
    category = category.upper()
    return transactions_list.filter(category=category)

def transactions_in_month(month, transactions_list):
    monthNumber = monthsConverter[month]
    return transactions_list.filter(timestamp__month=monthNumber)

def sum_amounts_in_a_year(transactions_list):
    return transactions_list.aggregate(Sum("amount"))["amount__sum"]


from PyQt4 import QtGui
from PyQt4.QtCore import pyqtSlot

from views.core import centraltabwidget
from db.windows.wellplot.wellplotdata.wellplotdata import WellPlotData
from gui.wellplot.toolbar import logsettingstoolbar
from gui.signals.wellplotsignals import WellPlotSignals
from gui.wellplot.wellplotdialog.uiwellplotpg import UIWellPlotPG
from gui.wellplot.wellplotdialog.headerviewer import HeaderViewer
from gui.wellplot.model.wellplotmodelaccess import WellPlotModelAccess

import logging
from gui.wellplot.wellplotdialog.trackviewer import TrackViewer

from gui.wellplot.overview.overviewwidget import OverviewWidget
from gui.util.qt.widgetutils import WidgetUtils

logger = logging.getLogger('console')

class WellPlotPG(UIWellPlotPG):
    '''Main well plot class, extends  UIWellPlotPG'''

    def __init__(self, logs, well, template, wellPlotData = None, logSet = None, parent = None):
        '''Logs are a selected logs that are plotting
        All logs can be accessed via '''
        super(WellPlotPG, self).__init__(parent)
        if (logs is not None) and (well is not None) and (template is not None):

            self._well = well
            self._logSet = logSet
            self._logs = logs
            self._template = template
            self.trackViewer = None
            self.headerViewer = None
            self.overviewWidget = None
            self.canvas = None
            self.depthPlot = None
            self.headerPlot = None
            self.centralWidget = centraltabwidget.CentralTabWidget()
            #id(self) returns the 'hash' of this object
            self.uid = (self.centralWidget.count(), id(self))
            self.wellPlotSignals = WellPlotSignals()
            self.setupUI()
            self.createTabView()
            self.connectSlots()
            if wellPlotData is None:
                self.wellPlotData = self.generateInitialData()
            else:
                self.wellPlotData = wellPlotData

            self.plotMultiLogs(self.wellPlotData)

            self.createToolWidget()
        else:
            logger.error("Can't open well plot, input data is None: logs:{0}, well:{1}, template:{2}".format(logs, well, template))

    def generateInitialData(self):
        wellPlotModelAccess = WellPlotModelAccess()
        wellPlotData = wellPlotModelAccess.createWellPlotData(self._logs, self.uid, self._well, self._template, self._logSet)
        return wellPlotData

    def plotMultiLogs(self, wellPlotData):
        if (wellPlotData != None):
            self.clearWidgets(wellPlotData)

            self.mainWidget.setLogPlotData(wellPlotData)
            self.trackViewer = TrackViewer(wellPlotData, parent = self.scrollArea)
            self.scrollArea.setWidget(self.trackViewer)
            self.headerViewer = HeaderViewer(wellPlotData = wellPlotData, logTracks = self.trackViewer.allTracks)
            self.headerScrollArea.setWidget(self.headerViewer)

            self.setSplitterStretch()
            #create overview here so is recreated on redraw signal
            self.createOverviewWidget()

    def clearWidgets(self, wellPlotData):
        '''delete everything if existing'''
        if self.trackViewer is not None:
            WidgetUtils.removeWidgets(self.scrollArea.layout())
            self.trackViewer = None
            self.wellPlotData = wellPlotData

        if self.headerViewer is not None:
            WidgetUtils.removeWidgets(self.headerScrollArea.layout())
            self.headerViewer = None

        if (self.overviewWidget is not None) and (self.scaleWidgetLayout is not None):
            WidgetUtils.removeWidgets(self.scaleWidgetLayout)
            self.overviewWidget = None

    def setSplitterStretch(self):
        #totalWidth = self.trackViewer.getMinWidgetWidth()
        trackHeight = self.trackViewer.geometry().height()
        headerHeight = self.headerViewer.geometry().height()
        #self.trackViewer.setMinimumSize(totalWidth, trackHeight)
        if headerHeight > 0:
            ratio = int((trackHeight+headerHeight)/headerHeight)
        else:
            ratio = 10
        logger.debug("trackHeight:{0} headerHeight:{1} ratio:{2}".format(trackHeight,headerHeight, ratio))
        self.splitter.setStretchFactor(1, ratio)

    def connectSlots(self):
        logger.debug(">>connectSlots")
        self.wellPlotSignals.logPlotSettingsModified.connect(self.replotLogs)

    @pyqtSlot(WellPlotData)
    def replotLogs(self, wellPlotData):
        #check uid's before accessing them, where uid is a (number widgets in central widget, id) tuple
        logger.debug("--replotLogs() len(self.uid):{0}, len(logPlotData.uid):{1}".format(len(self.uid), len(wellPlotData.uid)))
        if (len(self.uid) == 2) and (len(wellPlotData.uid) == 2):
            #ensure this object is associated with the plot object
            if self.uid[0] == wellPlotData.uid[0] and self.uid[1] == wellPlotData.uid[1]:
                logger.debug("--replotLogs() match found uid: "+str(self.uid[0]))
                self.plotMultiLogs(wellPlotData)

    def spacePlots(self, bottomLayout):
        rightSpacer = QtGui.QWidget()
        rightSpacer.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        #topLayout.addWidget(rightSpacer)
        bottomLayout.addWidget(rightSpacer)
        #self.topWidget.setLayout(topLayout)
        self.dataWidget.setLayout(bottomLayout)

    def createTabView(self):
        self.mainWidget.setData(self.uid)
        self.centralWidget.addTab(self.mainWidget, "Well plot "+str(self.uid[0]))

    def createToolWidget(self):
        logger.debug(">>createToolWidget()")
        if len(self._logs) > 0:
            toolbar = logsettingstoolbar.LogSettingsToolbar()
            #toolbar.setData(self._well, self._logSet, self.canvas, self.depthPlot, self.headerPlot)
            toolbar.emitShowToolbarSignal()
            logger.debug("<<createToolWidget() toolbar created")

    def createOverviewWidget(self):
        #TODO find longest GR track and use as default
        #for logTrackData in self.wellPlotData.log_track_datas:
        #   logTrackData.getLogs()
        #also for Active logs raises index error
        logTrackDatas= self.wellPlotData.getLogTrackDatas()
        if len(logTrackDatas) > 0:
            logTrackData = logTrackDatas[0]
            logger.debug("--createOverviewWidget() self.wellPlotData.uid:{0}".format(self.wellPlotData.uid))
            self.overviewWidget = OverviewWidget(self.wellPlotData, self.trackViewer, wellPlotPGSplitter = self.splitter)
            #revise these 3 lines to log want to plot and data range
            log = logTrackData.getLogs()[0]
            yData = log.z_measure_data
            xData = log.log_data

            self.overviewWidget.generatePlot(logTrackData, log, xData, yData)
            self.scaleWidgetLayout.addWidget(self.overviewWidget)
            #scaleWidget.primaryDomainWidget = self.trackViewer.primaryDomainTrackWidget
        else:
            logger.debug("No log track data, cannot create scale widget, ")


import cv2
vidcap = cv2.VideoCapture('C:\\Users\\Admin\\Pictures\\Camera Roll\\WIN_20190621_20_18_04_Pro.mp4')
def getFrame(sec):
    vidcap.set(cv2.CAP_PROP_POS_MSEC,sec*1000)
    print(vidcap.get(cv2.CAP_PROP_POS_MSEC))
    hasFrames,image = vidcap.read()
    if hasFrames:
        cv2.imwrite("image_3_"+str(count)+".jpg", image)     # save frame as JPG file
    return hasFrames
sec = 0
frameRate = 100.00 #//it will capture image in each 0.5 second
count=1
success = getFrame(sec)
while success:
    count = count + 1
    sec = sec + frameRate
    sec = round(sec, 2)
    print("value of sec: ",sec)
    success = getFrame(sec)
    print("value of success: ",success)


import requests
import logging
from operator import itemgetter

logger = logging.getLogger(__name__)

class Graphite(object):
    def __init__(self, host):
        self.endpoint = host + "/render"

    def _get_json(self, target, params={}):
        payload = {
            'target': target,
            'format': 'json',
        }
        payload.update(params)
        logger.debug(payload)
        r = requests.get(self.endpoint, params=payload)
        r.raise_for_status()
        return r.json()

    def get_stats(self, client_name, query='*.*', params={}):
        prefix = "nagios.%s." % client_name
        target = prefix + query
        metrics = self._get_json(target, params)
        for m in metrics:
            m[u'target'] = m[u'target'].replace(prefix, "", 1)
        return metrics