'''
Created on 28 Mar 2013

@author: giles
'''

from django.contrib.auth.models import User, Group, Permission

perms_committee = ['user_create',
                   'user_view_active',
                   'user_edit_self',
                   'user_edit_all',
                   'user_fdel_all',
                   'group_create_edit',
                   'ticket_create',
                   'ticket_view_all',
                   'ticket_view_deld',
                   'ticket_comment',
                   'ticket_comment_del',
                   'ticket_comment_del_self',
                   'ticket_comment_edit_all',
                   'ticket_close',
                   'ticket_fdel_norm',
                   'file_view_all',
                   'file_view_deld',
                   'file_create_small',
                   'file_create_large',
                   'file_edit_self',
                   'file_edit_all',
                   'file_fdel_self',
                   'file_fdel_all',
                   'file_rdel_all',
                   'file_edit_icons',
                   'punt_create',
                   'punt_view_current',
                   'punt_view_all',
                   'punt_edit',
                   'punt_add_ticket',
                   'punt_view_maint',
                   'punt_decomission',
                   'punt_fdel',
                   'accounts_view_all',
                   'accounts_summarise',
                   ]

perms_treasurer = ['accountitem_create',
                   'accountitem_edit',
                   'accounts_view_all',
                   'accounts_view_deld',
                   'accountitem_fdel',
                   'accounts_summarise',
                   'account_create',
                   'account_edit',
                   ]

perms_members = [  'user_view_active',
                   'user_edit_self',
                   'ticket_create',
                   'ticket_view_all',
                   'ticket_comment',
                   'ticket_comment_del_self',
                   'file_view_all',
                   'file_create_small',
                   'file_edit_self',
                   'file_fdel_self',
                   'punt_view_current',
                   'punt_view_all',
                   'punt_add_ticket',
                   'punt_view_maint',
                   ]

perms_exmembers = ['user_edit_self',
                   'ticket_view_all',
                   'file_edit_self',
                   'file_fdel_self',
                   'punt_view_current',
                   ]

perms_showoff = [  'ticket_view_all',
                   'punt_view_current',
                 ]

permissions = {'user_create'        :'auth.add_user,members.add_member',
               'user_view_active'   :'members.view_active',
               'user_edit_self'     :'members.edit_self',
               'user_edit_all'      :'members.edit_all,auth.change_user',
               'user_fdel_all'      :'members.fdel_all',
               'user_rdel_all'      :'members.delete_all',
               'group_create_edit'  :'auth.add_group,auth.change_group,auth.delete_group',
               'ticket_create'      :'ticket.add_ticket',
               'ticket_view_all'    :'ticket.view_all',
               'ticket_view_deld'   :'ticket.view_deleted',
               'ticket_comment'     :'comments.create_ticket',
               'ticket_comment_del' :'comments.delete_ticket',
               'ticket_comment_del_self'    :'comments.delete_ticket_self',
               'ticket_comment_edit_self'   :'comments.edit_ticket_self',
               'ticket_comment_edit_all'    :'comments.edit_ticket_all',
               'ticket_close'       :'ticket.close',
               'ticket_fdel_norm'   :'ticket.fdel_norm',
               'ticket_fdel_acc'    :'ticket.fdel_acc',
               'ticket_rdel_norm'   :'ticket.delete_norm',
#               'rota_bailing_view'  :'events.rota_bailing_view',
#               'rota_bailing_edit'  :'events.rota_bailing_edit',
#                'event_create'        :'events.event_create',
#                'event_edit'        :'events.event_edit',
#                'event_comment'    :'events.event_comment',
               'file_view_all'      :'files.view_file_all',
               'file_view_deld'     :'files.view_file_deleted',
               'file_create_small'  :'files.create_file_small',
               'file_create_large'  :'files.create_file_large',
               'file_edit_self'     :'files.edit_file_self',
               'file_edit_all'      :'files.edit_file_all',
               'file_fdel_self'     :'files.fdel_file_self',
               'file_fdel_all'      :'files.fdel_file_all',
               'file_rdel_all'      :'files.rdel_file_all',
               'file_edit_icons'    :'files.add_icon,files.change_icon,files.delete_icon',
               'punt_create'        :'punts.punt_create',
               'punt_view_current'  :'punts.punt_view_current',
               'punt_view_all'      :'punts.punt_view_all',
               'punt_view_maint'    :'punts.punt_view_maintenance',
               'punt_edit'          :'punts.punt_edit',
               'punt_add_ticket'    :'punts.punt_add_ticket',
               'punt_decomission'   :'punts.punt_decomission',
               'punt_fdel'          :'punts.punt_fdel',
               'accountitem_create' :'accounts.accountitem_create',
               'accountitem_edit'   :'accounts.accountitem_edit',
               'accounts_view_all'  :'accounts.view_all',
               'accounts_view_deld' :'accounts.view_deleted',
               'accountitem_fdel'   :'accounts.accountitem_fdel',
               'accounts_summarise' :'accounts.summarise',
               'account_create'     :'accounts.account_create',
               'account_edit'       :'accounts.account_edit',
               }

def check_makeGroups():
    try:
        com = Group.objects.get(name='committee')
    except:
        com = Group(name='committee')
        com.save()
    set_group_permissions(com,perms_committee)
    #TODO Add rest

def set_group_permissions(gin,gperms):
    for p in gperms:
        if p in permissions:
            pl = permissions[p].split(',')
            for pa in pl:
                parts = pa.split('.')
                #TODO: Add content type finder and permission sorter


import uuid
import googleapiclient.discovery
from google.cloud import runtimeconfig

def cloud_fn_stop_all_servers(event, context):
    """
    Simply stops all servers in the project. This is meant to run periodically to prevent servers from running
    constantly
    :param event: No data is passed to this function
    :param context: No data is passed to this function
    :return:
    """
    runtimeconfig_client = runtimeconfig.Client()
    myconfig = runtimeconfig_client.config('myconfig')
    project = myconfig.get_variable('project').value.decode("utf-8")
    zone = myconfig.get_variable('zone').value.decode("utf-8")

    compute = googleapiclient.discovery.build('compute', 'v1')
    result = compute.instances().list(project=project, zone=zone).execute()
    if 'items' in result:
        for vm_instance in result['items']:
            compute.instances().stop(project=project, zone=zone, instance=vm_instance["name"]).execute()

def cloud_fn_my_cloud_function(event, context):
    """
    This is your function
    Args:
         event (dict):  The dictionary with data specific to this type of
         event. The `data` field contains the PubsubMessage message. The
         `attributes` field will contain custom attributes if there are any.
         context (google.cloud.functions.Context): The Cloud Functions event
         metadata. The `event_id` field contains the Pub/Sub message ID. The
         `timestamp` field contains the publish time.
    Returns:
        A success status
    """

    action = event['attributes']['action'] if 'action' in event['attributes'] else None

    if not action:
        print(f'No action provided in cloud_fn_manage_server for published message.')
        return

    if action == "build":
        runtimeconfig_client = runtimeconfig.Client()
        myconfig = runtimeconfig_client.config('cybergym')
        project = myconfig.get_variable('project').value.decode("utf-8")
        zone = myconfig.get_variable('zone').value.decode("utf-8")

        server_name = f"auto_server-{uuid.uuid4()}"
        compute = googleapiclient.discovery.build('compute', 'v1')
        image_response = compute.images().getFromFamily(project="debian-cloud", family="debian-9").execute()
        source_disk_image = image_response["selfLink"]
        config = {
            "name": server_name,
            "machineType": f"projects/{project}/zones/{zone}/machineTypes/e2-micro",
            "disks": [
                {
                    "boot": True,
                    "autoDelete": True,
                    "initializeParams": {
                        "sourceImage": source_disk_image,
                    }
                }
            ],
            "networkInterfaces": [{
                "network": "global/networks/default",
                "accessConfigs": [
                    {"type": "ONE_TO_ONE_NAT", "name": "External NAT"}
                ]
            }],
        }
        print("Continue coding to deploy the server")
    elif action == "bucket":
        print("Replace this with a function to create the cloud bucket.")


class Human:
    def __init__(self, first_name, last_name, age, height, weight, gender):
        # these are the attributes of the class
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.height = height
        self.gender = gender
        self.weight = weight

# methods are actions
    def say_name(self):
        print(f'My name is {self.first_name} {self.last_name}')

    def lose_weight(self, amount):
        # current weight minus current weight
        self.weight -= amount

tom = Human('tom', 'harris', 33, 6, "male")
kyle = Human('kyle', 'arris', 43, 6, "male")

tom.say_name()
kyle.say_name()

print(tom.weight)
tom.lose_weight(30)


# 성적이 낮은 순서로 학생 출력하기
# my
n = int(input())

array = []
for i in range(n):
    array.append(tuple(map(str, input().split())))

def setting(data):
    return data[1]

array = sorted(array, key=setting)

print(array)

# sol
n = int(input())

array = []
for i in range(n):
    input_data = input().split()
    array.append((input_data[0], int(input_data[1])))

array = sorted(array, key=lambda student: student[1])

print(array)


from turtle import Turtle, Screen

STARTING_POSITION = (0, -280)
MOVE_DISTANCE = 10
FINISH_LINE_Y = 280

class Player(Turtle):
    def __init__(self):
        super().__init__()
        self.penup()
        self.color("green")
        self.shape('turtle')
        self.speed(3)
        self.reset_player()
        self.setheading(90)

    def move(self):
        self.forward(MOVE_DISTANCE)

    def check_is_hit(self, other):                              # Check to see if our coordinates are within range to define a "hit"
        (turtle_x, turtle_y) = self.pos()
        (other_x, other_y) = other.pos()

        if other_x >= turtle_x - 25 and other_x <= + 25 and other_y >= turtle_y - 15 and other_y <= turtle_y + 15:
            return True
        return False

    def finish(self):
        if self.ycor() >= FINISH_LINE_Y:
            return True
        return False

    def reset_player(self):
        self.goto(STARTING_POSITION)


"""
You need to run this file in order to create the grid_exploration_amounts files for plotting.
Requires that you store states during training.
"""

import math
import pickle
import matplotlib.pyplot as plt
import numpy as np

from pathlib import Path

def discretize_state_list(l, square_size=0.5):
    """
    List of states, "radius"
    """
    discrete_tuples = []
    for state in l:
        t = tuple(math.floor(f/square_size) for f in state)
        discrete_tuples.append(t)
    return discrete_tuples

def test_discretize_example():
    l = [
        [0.1, 0.9],
        [-0.1, 0.1],
        [1.8, -1.1],
    ]
    discretized = discretize_state_list(l, 1.0)
    assert discretized == [
        (0, 0),
        (-1,0),
        (1,-2)
    ]

def add_discretization_to_set(s, l, square_size=0.5):
    discretized = discretize_state_list(l, square_size=square_size)
    s.update(discretized)
    return s

def trim_list_of_episodes(list_of_episodes):
    los = []
    for l in list_of_episodes:
        trimmed_states = [s[0:2] for s in l]
        los.append(trimmed_states)
    return los

def episodes_to_exploration_amounts(list_of_episodes, square_size=0.5):
    """
    list of episodes is a list of lists of states.
    Figure out how much was explored at each step.
    """
    list_of_episodes = trim_list_of_episodes(list_of_episodes)
    visitation_set = set()
    exploration_per_episode = []
    for episode in list_of_episodes:
        add_discretization_to_set(visitation_set, episode, square_size=square_size)
        exploration_per_episode.append(len(visitation_set))

    return exploration_per_episode

def write_grid_exploration_pkl(input_filename, output_filename):
    with open(input_filename, "rb") as f:
        list_of_episodes = pickle.load(f)['states']

    expl_per_ep = episodes_to_exploration_amounts(list_of_episodes, square_size=0.5)

    with open(output_filename, "wb") as f:
        pickle.dump(expl_per_ep, f)

def do_for_all_files_in_run(run_directory):
    print(f'doing for {run_directory}')
    if isinstance(run_directory, str):
        run_directory = Path(run_directory)

    states_directory = run_directory / "stored_states"
    if not states_directory.exists():
        print(f"no states directory for {str(run_directory)}, continuing")
        return

    grid_directory = run_directory / "grid_exploration_amounts"
    grid_directory.mkdir(exist_ok=True)

    for filepath in states_directory.iterdir():
        filename = filepath.name
        gridpath = grid_directory / filename
        write_grid_exploration_pkl(str(filepath), str(gridpath))

    print(f'done for {run_directory}')

def do_for_all_runs_in_experiment(exp_directory):
    print(f'doing for {exp_directory}')
    if isinstance(exp_directory, str):
        exp_directory = Path(exp_directory)

    for directory in exp_directory.iterdir():
        if not directory.is_dir():
            continue
        try:
            do_for_all_files_in_run(directory)
        except:
            print(f"failed for {str(directory)}")

def do_for_pickle_file(filename):
    with open(filename, "rb") as f:
        list_of_episodes = pickle.load(f)['states']

    expl_per_ep = episodes_to_exploration_amounts(list_of_episodes, square_size=0.5)
    plt.plot(range(len(expl_per_ep)), np.array(expl_per_ep) / 400)
    plt.show()
    plt.close()

def main():
    do_for_all_runs_in_experiment("./remote_plots/slurm_plots/point_maze/ddpg/no_knownness/fixed_policy")

def tests():
    test_discretize_example()

if __name__ == "__main__":
    # tests()
    main()


from typing import Tuple, List, Union
import struct
import numpy as np
import tensorflow as tf

from aimet_common.winnow.mask import Mask
from aimet_common.winnow.winnow_utils import get_zero_positions_in_binary_mask
from aimet_common.utils import AimetLogger
from aimet_tensorflow.common.operation import Op
from aimet_tensorflow.common.product import Product
from aimet_tensorflow.utils.op.conv import WeightTensorUtils, BiasUtils
from aimet_tensorflow.utils.op.fusedbatchnorm import BNUtils

logger = AimetLogger.get_area_logger(AimetLogger.LogAreas.Winnow)

# pylint: disable=too-many-locals
def reduce_conv2d(sess: tf.compat.v1.Session,
                  op_tensor_tuple: Tuple[Op, List[tf.Tensor]], op_mask) -> (str, tf.Operation, tf.Operation):
    """
    Conv2D module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    :param op_mask: Mask containing information on input and output channels to winnow
    """

    tf_op = op_tensor_tuple[0].get_module()
    padding = str(tf_op.get_attr("padding"), "utf-8")
    strides = tf_op.get_attr("strides")
    # Remove last part of conv op's name, or else we end up with names like Conv2D/Conv2D/Conv2D... if the same conv op
    # is reduced multiple times
    last_slash = op_tensor_tuple[0].dotted_name.rfind('/')
    name = "reduced_" + op_tensor_tuple[0].dotted_name[:last_slash]
    kernel_product = op_tensor_tuple[0].get_param_product('kernel')
    kernel_size = kernel_product.shape.as_list()[:2]

    # Depthwise Conv2d always has output dim of 1.  Only slice out input channel dimensions.
    if op_tensor_tuple[0].type == 'DepthwiseConv2dNative':
        # Check to make sure input channel and output channel sizes are identical
        # Format is expected to be NHWC, so channels is the last index in shape array
        if tf_op.inputs[0].shape.as_list()[-1] != tf_op.outputs[0].shape.as_list()[-1]:
            raise NotImplementedError('Reducing depthwise conv2d with differing input and output channel sizes not '
                                      'supported')
        output_dim = None
    else:
        output_dim = 3

    reduced_weights = _get_reduced_params(sess=sess,
                                          product=kernel_product,
                                          mask=op_mask,
                                          input_dim=2,
                                          output_dim=output_dim)
    reduced_weights_init = tf.compat.v1.constant_initializer(reduced_weights, verify_shape=True)
    bias_product = op_tensor_tuple[0].get_param_product('bias')
    reduced_bias = None
    if bias_product:
        use_bias = True
        reduced_bias = _get_reduced_params(sess=sess,
                                           product=bias_product,
                                           mask=op_mask,
                                           input_dim=None,
                                           output_dim=0)
        reduced_bias_init = tf.compat.v1.constant_initializer(reduced_bias, verify_shape=True)
    else:
        use_bias = False
        reduced_bias_init = 'zeros'

    output_ch_masks = op_mask.output_channel_masks
    output_ch_indices_to_reduce = get_zero_positions_in_binary_mask(output_ch_masks[0])
    filters = len(output_ch_masks[0]) - len(output_ch_indices_to_reduce)

    # Check for regularization in the kernel
    kernel_tensor = kernel_product.tensor_dict[kernel_product.consumers[0]]
    kernel_regularizer = _get_kernel_regularizer(kernel_tensor)

    if op_tensor_tuple[0].type == 'DepthwiseConv2dNative':
        new_tensor = tf.keras.layers.DepthwiseConv2D(kernel_size=kernel_size,
                                                     strides=strides[1:3],
                                                     padding=padding,
                                                     use_bias=use_bias,
                                                     bias_initializer=reduced_bias_init,
                                                     kernel_initializer=reduced_weights_init,
                                                     kernel_regularizer=kernel_regularizer,
                                                     name=name)(op_tensor_tuple[1][0])
    else:
        new_tensor = tf.keras.layers.Conv2D(filters=filters,
                                            kernel_size=kernel_size,
                                            strides=strides[1:3],
                                            padding=padding,
                                            use_bias=use_bias,
                                            bias_initializer=reduced_bias_init,
                                            kernel_initializer=reduced_weights_init,
                                            kernel_regularizer=kernel_regularizer,
                                            name=name)(op_tensor_tuple[1][0])
    module = new_tensor.op
    if use_bias:
        module = module.inputs[0].op
    WeightTensorUtils.update_tensor_for_op(sess, module, reduced_weights)
    if use_bias:
        BiasUtils.update_bias_for_op(sess, module, reduced_bias)

    return name, new_tensor.op, module

def reduce_maxpool(sess: tf.compat.v1.Session,
                   op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Maxpool module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    tf_op = op_tensor_tuple[0].get_module()
    padding = str(tf_op.get_attr("padding"), "utf-8")
    strides = tf_op.get_attr("strides")[1:3]
    ksize = tf_op.get_attr("ksize")[1:3]
    name = "reduced_" + op_tensor_tuple[0].dotted_name

    # Hardcoding [:-8] below to remove '/MaxPool' from the dotted name
    # Otherwise this leads to a doubly nested op
    new_tensor = tf.keras.layers.MaxPool2D(pool_size=ksize,
                                           strides=strides,
                                           padding=padding,
                                           name=name[:-8])(op_tensor_tuple[1][0])
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_avgpool(sess: tf.compat.v1.Session,
                   op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Avgpool module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    tf_op = op_tensor_tuple[0].get_module()
    padding = str(tf_op.get_attr("padding"), "utf-8")
    strides = tf_op.get_attr("strides")[1:3]
    ksize = tf_op.get_attr("ksize")[1:3]
    name = "reduced_" + op_tensor_tuple[0].dotted_name

    # Hardcoding [:-8] below to remove '/AvgPool' from the dotted name
    # Otherwise this leads to a doubly nested op
    new_tensor = tf.keras.layers.AvgPool2D(pool_size=ksize,
                                           strides=strides,
                                           padding=padding,
                                           name=name[:-8])(op_tensor_tuple[1][0])
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_batchnorm(sess: tf.compat.v1.Session,
                     op_tensor_tuple: Tuple[Op, List[tf.Tensor]], op_mask) -> (str, tf.Operation, tf.Operation):
    """
    Fused and non fused batchnorm module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    :param op_mask: Mask containing information on input and output channels to winnow
    """

    beta_product = op_tensor_tuple[0].get_param_product('beta')
    if beta_product:
        use_beta = True
        reduced_beta_init = tf.compat.v1.constant_initializer(_get_reduced_params(sess=sess,
                                                                                product=beta_product,
                                                                                mask=op_mask,
                                                                                input_dim=0,
                                                                                output_dim=None),
                                                              verify_shape=True)
    else:
        use_beta = False
        reduced_beta_init = 'zeros'

    gamma_product = op_tensor_tuple[0].get_param_product('gamma')
    if gamma_product:
        use_gamma = True
        reduced_gamma_init = tf.compat.v1.constant_initializer(_get_reduced_params(sess=sess,
                                                                                product=gamma_product,
                                                                                mask=op_mask,
                                                                                input_dim=0,
                                                                                output_dim=None),
                                                               verify_shape=True)
    else:
        use_gamma = False
        reduced_gamma_init = 'ones'

    moving_mean_product = op_tensor_tuple[0].get_param_product('moving_mean')
    reduced_mov_mean_init = tf.compat.v1.constant_initializer(_get_reduced_params(sess=sess,
                                                                                product=moving_mean_product,
                                                                                mask=op_mask,
                                                                                input_dim=0,
                                                                                output_dim=None),
                                                              verify_shape=True)
    moving_variance_product = op_tensor_tuple[0].get_param_product('moving_variance')
    reduced_mov_variance_init = tf.compat.v1.constant_initializer(_get_reduced_params(sess=sess,
                                                                                product=moving_variance_product,
                                                                                mask=op_mask,
                                                                                input_dim=0,
                                                                                output_dim=None),
                                                                  verify_shape=True)

    name = "reduced_" + op_tensor_tuple[0].dotted_name
    # Get training attribute
    # This will either be True, False, or a string representing a training_placeholder the original BN was using
    training = BNUtils.get_training(op_tensor_tuple[0].get_module())
    assert training is not None
    is_fused = op_tensor_tuple[0].type == 'FusedBatchNormV3'
    epsilon = BNUtils.get_epsilon(op_tensor_tuple[0].get_module())
    momentum = BNUtils.get_momentum(op_tensor_tuple[0].get_module())
    if momentum is not None:
        new_tensor = tf.keras.layers.BatchNormalization(center=use_beta,
                                                        scale=use_gamma,
                                                        epsilon=epsilon,
                                                        momentum=momentum,
                                                        beta_initializer=reduced_beta_init,
                                                        gamma_initializer=reduced_gamma_init,
                                                        moving_mean_initializer=reduced_mov_mean_init,
                                                        moving_variance_initializer=reduced_mov_variance_init,
                                                        fused=is_fused,
                                                        name=name)(op_tensor_tuple[1][0], training=training)
    else:
        new_tensor = tf.keras.layers.BatchNormalization(center=use_beta,
                                                        scale=use_gamma,
                                                        epsilon=epsilon,
                                                        beta_initializer=reduced_beta_init,
                                                        gamma_initializer=reduced_gamma_init,
                                                        moving_mean_initializer=reduced_mov_mean_init,
                                                        moving_variance_initializer=reduced_mov_variance_init,
                                                        fused=is_fused,
                                                        name=name)(op_tensor_tuple[1][0], training=training)
    module = new_tensor.op.inputs[0].op

    return name, new_tensor.op, module

def reduce_relu(sess: tf.compat.v1.Session,
                op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Relu module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    # In TF 1.7, no ReLU exists in tf.python.keras.layers API
    # need to use lower level tf.nn API here
    # pylint: disable=no-member
    name = "reduced_" + op_tensor_tuple[0].dotted_name
    if op_tensor_tuple[0].type == 'Relu6':
        new_tensor = tf.nn.relu6(op_tensor_tuple[1][0],
                                 name=name)
    else:
        new_tensor = tf.nn.relu(op_tensor_tuple[1][0],
                                name=name)
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_tanh(sess: tf.compat.v1.Session,
                op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Tanh module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    name = "reduced_" + op_tensor_tuple[0].dotted_name
    new_tensor = tf.nn.tanh(op_tensor_tuple[1][0],
                            name=name)
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_dropout(_, op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _op_mask) -> (str, tf.Operation, tf.Operation):
    """
    Dropout module reducer
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    :param _op_mask: unused parameter
    """

    name = "reduced_" + op_tensor_tuple[0].dotted_name
    # Get rate tensor
    cast_op = op_tensor_tuple[0].get_module().inputs[1].op
    assert cast_op.type == 'Cast'
    greater_equal_op = cast_op.inputs[0].op
    assert greater_equal_op.type == 'GreaterEqual'
    rate_tensor = greater_equal_op.inputs[1]
    rate = rate_tensor.op.get_attr('value').float_val[0]
    if op_tensor_tuple[0].pattern_type == 'Dropout_with_training_tensor':
        new_tensor = tf.keras.layers.Dropout(rate, name=name)(op_tensor_tuple[1][0])
    else:
        new_tensor = tf.keras.layers.Dropout(rate, name=name)(op_tensor_tuple[1][0], training=True)
    module = new_tensor.op

    return name, new_tensor.op, module

def reduce_identity(sess: tf.compat.v1.Session,
                    op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Identity module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    name = "reduced_" + op_tensor_tuple[0].dotted_name
    new_tensor = tf.identity(op_tensor_tuple[1][0],
                             name=name)
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_pad(sess: tf.compat.v1.Session,
               op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Pad module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    name = "reduced_" + op_tensor_tuple[0].dotted_name
    pad_op = op_tensor_tuple[0].get_module()

    # Get padding tensor dimensions
    # Padding dimension information is captured in an input tensor to the pad op, index 1 of pad op inputs
    # Dimensions of this tensor are always (N, 2), where N is the dimensionality of the input tensor coming into pad.
    # The value of padding[N][0] gives the amount to pad in dimension N prior to the contents of the input to pad, while
    # padding[N][1] gives the amount to pad in dimension N after the contents of the input.
    # Currently we do not support reducing a pad op that modifies the channel dimension, which is the last dimension,
    # indexed by -1 below.  So check to make sure that indices [-1][0] and [-1][1] remain 0 (no padding).
    padding_tensor_eval = sess.run(pad_op.inputs[1])
    if padding_tensor_eval[-1][0] != 0 or padding_tensor_eval[-1][1] != 0:
        raise NotImplementedError("Attempting to reduce pad operation that modifies channel size, not supported.")
    new_padding_tensor = tf.constant(padding_tensor_eval)       # No need to actually modify padding tensor

    # Get constant value for padding
    # If pad op takes a non default constant value (default = 0), it appears as a third input tensor to pad op, index 2
    const_val = 0
    if len(pad_op.inputs) > 2:
        const_val = sess.run(pad_op.inputs[2])

    # Get mode
    # Mode can be 'CONSTANT', 'SYMMETRIC', or 'REFLECT'.  'CONSTANT' is default, and will not appear as a mode attribute
    # if it is the case.
    try:
        mode = pad_op.get_attr('mode')
        mode = mode.decode('utf-8')
    except ValueError:
        mode = 'CONSTANT'

    new_tensor = tf.pad(op_tensor_tuple[1][0],
                        new_padding_tensor,
                        constant_values=const_val,
                        mode=mode,
                        name=name)
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_min_max(sess: tf.compat.v1.Session,
                   op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Minimum and maximum module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    name = "reduced_" + op_tensor_tuple[0].dotted_name
    rate = op_tensor_tuple[0].get_module().inputs[1].op.get_attr('value').float_val[0]

    if op_tensor_tuple[0].type == 'Minimum':
        new_tensor = tf.minimum(op_tensor_tuple[1][0],
                                rate,
                                name=name)
    else:
        new_tensor = tf.maximum(op_tensor_tuple[1][0],
                                rate,
                                name=name)
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_add(sess: tf.compat.v1.Session,
               op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _) -> (str, tf.Operation, tf.Operation):
    """
    Add module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """

    name = "reduced_" + op_tensor_tuple[0].dotted_name
    if len(op_tensor_tuple[1]) > 2:
        new_tensor = tf.add_n(op_tensor_tuple[1],
                              name=name)
    else:
        new_tensor = tf.add(op_tensor_tuple[1][0],
                            op_tensor_tuple[1][1],
                            name=name)
    module = sess.graph.get_operation_by_name(name)

    return name, new_tensor.op, module

def reduce_concat(sess: tf.compat.v1.Session, op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _):
    """
    Concat module reducer
    :param sess: current tf.compat.v1.Session
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    """
    raise AssertionError("Should not need to call this function unless mask propagation rules for concat change")

def reduce_downsample(_, op_tensor_tuple: Tuple[Op, List[tf.Tensor]], op_mask) -> (str, tf.Operation, tf.Operation):
    """
    Downsample module reducer
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    :param op_mask: Mask of downsample op
    """

    # Get tuple of channels which downsample is currently keeping
    indices_op = op_tensor_tuple[0].get_module().inputs[1].op
    indices_length = indices_op.get_attr('value').tensor_shape.dim[0].size
    unpack_string = str(indices_length) + 'i'       # i for int, indices_length tells how many integers to parse out
    child_indices = struct.unpack(unpack_string, indices_op.get_attr('value').tensor_content)

    # Get list of remaining child indices after the existing tuple of channels is further pruned according to new mask
    child_mask = op_mask.output_channel_masks[0]
    new_child_indices = [index for i, index in enumerate(child_indices) if child_mask[i]]

    # Create new downsample op applying new list of child indices
    new_tensor = create_downsample_op('downsample', op_tensor_tuple[1][0], new_child_indices)
    module = new_tensor.op

    return new_tensor.op.name, new_tensor.op, module

def reduce_upsample2d(_, op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _op_mask) -> (str, tf.Operation,
                                                                                tf.Operation):
    """
    Upsample2D module reducer
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    :param _op_mask: unused parameter
    """
    name = "reduced_" + op_tensor_tuple[0].dotted_name
    # Get size attribute
    strided_slice = op_tensor_tuple[0].get_module().outputs[0].consumers()[0]
    assert strided_slice.type == 'StridedSlice'
    mul = strided_slice.outputs[0].consumers()[0]
    const_op = mul.inputs[1].op
    tensor_content_length = const_op.get_attr('value').tensor_shape.dim[0].size
    # i for int, tensor_content_length tells how many integers to parse out
    unpack_string = str(tensor_content_length) + 'i'
    size = struct.unpack(unpack_string, const_op.get_attr('value').tensor_content)
    new_tensor = tf.keras.layers.UpSampling2D(size=size, name=name)(op_tensor_tuple[1][0])
    module = new_tensor.op

    return name, new_tensor.op, module

def reduce_leaky_relu(_, op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _op_mask) -> (str, tf.Operation,
                                                                                tf.Operation):
    """
    Leaky_relu module reducer
    :param op_tensor_tuple: tuple containing the op to reduce, and a list of input tensors to the op
    :param _op_mask: unused parameter
    """
    name = "reduced_" + op_tensor_tuple[0].dotted_name
    alpha = op_tensor_tuple[0].get_module().get_attr('alpha')
    assert alpha is not None
    new_tensor = tf.nn.leaky_relu(op_tensor_tuple[1][0], alpha=alpha, name=name)
    module = new_tensor.op

    return name, new_tensor.op, module

def reduce_default(_, op_tensor_tuple: Tuple[Op, List[tf.Tensor]], _op_mask):
    """ Default reducer handler """
    raise NotImplementedError("reduction for op type %s not implemented" % op_tensor_tuple[0].type)

def _get_kernel_regularizer(kernel_tensor: tf.Tensor) -> Union[None, tf.Tensor]:
    """
    Get a kernel regularizer of the same kind as attached to kernel_tensor
    :param kernel_tensor: Kernel tensor to check for regularization
    :return: A new kernel regularizer if kernel_tensor has regularization, None otherwise
    """
    kernel_regularizer = None
    for consumer in kernel_tensor.consumers():
        if consumer.type == 'L2Loss':
            # Try to see if there is a scale value associated with it
            try:
                l2_regularizer_mul = consumer.outputs[0].consumers()[0]
                scale_op = l2_regularizer_mul.inputs[0].op
                scale_val = scale_op.get_attr('value').float_val[0]
                kernel_regularizer = tf.contrib.layers.l2_regularizer(scale_val)
            except:     # pylint: disable=bare-except
                kernel_regularizer = tf.nn.l2_loss      # pylint: disable=no-member
    return kernel_regularizer

def _get_reduced_params(sess: tf.compat.v1.Session, product: Product, mask: Mask, input_dim: Union[int, None],
                        output_dim: Union[int, None]) -> np.ndarray:
    """
    Reduce parameter tensor shapes
    :param sess: Current session
    :param product: represents the tensor which we want to reduce, using information contained in mask
    :param mask: contains information regarding input and output channels to reduce
    :param input_dim: dimension corresponding to input channels in product.  User specifies -1 to mean no input
    dim is present
    :param output_dim: dimension corresponding to output channels in product.  User specifies -1 to mean no output
    dim is present
    """

    input_ch_masks, output_ch_masks = mask.input_channel_masks, mask.output_channel_masks
    if len(input_ch_masks) == 1:
        input_ch_indices_to_reduce = get_zero_positions_in_binary_mask(input_ch_masks[0])
    else:
        input_ch_indices_to_reduce = []

    if output_ch_masks:
        output_ch_indices_to_reduce = get_zero_positions_in_binary_mask(output_ch_masks[0])
    else:
        output_ch_indices_to_reduce = []

    # get values of old parameter tensor as ndarray
    assert len(product.tensor_dict.keys()) == 1
    assert len(product.consumers) == 1
    old_tensor = product.tensor_dict[product.consumers[0]]
    values = old_tensor.eval(session=sess)

    # use np.delete to slice out specific elements of the ndarray
    # ensure that dimension size is large enough to delete all specified channels
    if input_ch_indices_to_reduce and input_dim is not None:
        assert max(input_ch_indices_to_reduce) < values.shape[input_dim]
        values = np.delete(values, input_ch_indices_to_reduce, input_dim)
    if output_ch_indices_to_reduce and output_dim is not None:
        assert max(output_ch_indices_to_reduce) < values.shape[output_dim]
        values = np.delete(values, output_ch_indices_to_reduce, output_dim)

    return values

def create_downsample_op(name, input_tensor, index_list) -> tf.Tensor:
    """
    Create a downsample op and return the output tensor
    :param name: scope name to create the op under
    :param input_tensor: Input tensor to the downsample op
    :param index_list: Indices to drop out
    :return: Output tensor of the downsample op
    """
    with tf.name_scope(name):
        gather_tensor = tf.gather(input_tensor, index_list, axis=-1)
    return gather_tensor


import logging

from google.appengine.ext import ndb

from components import utils

import metrics

class NumberSequence(ndb.Model):
  """A named number sequence.

  Entity key:
    ID is the name of the sequence.
    NumberSequence has no parent.
  """
  # Next number in the sequence.
  next_number = ndb.IntegerProperty(default=1, indexed=False)

@ndb.tasklet
def generate_async(seq_name, count):
  """Generates sequence numbers.

  Supports up to 5 QPS. If we need more, we will need to implement something
  more advanced.

  Args:
    name: name of the sequence.
    count: number of sequence numbers to allocate.

  Returns:
    The generated number. For a returned number i, numbers [i, i+count) can be
    used by the caller.
  """
  @ndb.transactional_tasklet
  def txn():
    seq = (
      (yield NumberSequence.get_by_id_async(seq_name))
      or NumberSequence(id=seq_name))
    result = seq.next_number
    seq.next_number += count
    yield seq.put_async()
    raise ndb.Return(result)

  started = utils.utcnow()
  number = yield txn()
  ellapsed_ms = (utils.utcnow() - started).total_seconds() * 1000
  if ellapsed_ms > 1000:  # pragma: no cover
    logging.error(
        'sequence number generation took > 1s\n'
        'it took %dms\n'
        'sequence: %s',
        ellapsed_ms, seq_name)
  else:
    logging.info('sequence number generation took %dms', ellapsed_ms)
  metrics.SEQUENCE_NUMBER_GEN_DURATION_MS.add(
      ellapsed_ms, fields={'sequence': seq_name})
  raise ndb.Return(number)


from flask import Blueprint,render_template,request,jsonify
from models.province import Province
from checkToken import verify_auth_token

province=Blueprint('province',__name__,template_folder='../templates/province')

#获取所有的省份疫情信息
@province.route('/all', methods=['GET'])
def all():
    #token校验
    try:
        token = request.headers["token"]
    except Exception as e:
        return {'tips': '请先登录'}, 401
    if verify_auth_token(token):
        try:
            provinces=Province.query.all()
            datas = []
            for i in provinces:
                nprovince = {}
                nprovince['id'] = i.id
                nprovince['name'] = i.name
                nprovince['nowCon'] = i.nowCon
                nprovince['con'] = i.con
                nprovince['heal'] = i.heal
                nprovince['dead'] = i.dead
                datas.append(nprovince)
            return jsonify(datas), 200
        except Exception as e:
            return {'tips': '抱歉后台出了一点小错误'}, 205
    else:
        return {'tips': '请先登录'}, 401


from collections import defaultdict
from copy import copy
from django.contrib import messages
from django.db import transaction
from django.utils.translation import gettext as _

from corehq.apps.data_interfaces.models import AutomaticUpdateRule
from corehq.apps.sms.util import get_language_list
from corehq.messaging.scheduling.exceptions import RuleUpdateError
from corehq.messaging.scheduling.forms import ScheduleForm
from corehq.messaging.scheduling.models.alert_schedule import AlertSchedule
from corehq.messaging.scheduling.models.content import SMSContent
from corehq.messaging.scheduling.models.timed_schedule import TimedSchedule
from corehq.util.workbook_json.excel import WorksheetNotFound

def get_conditional_alerts_queryset_by_domain(domain, query_string=''):
    query = (
        AutomaticUpdateRule
        .objects
        .filter(domain=domain, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING, deleted=False)
    )
    if query_string:
        query = query.filter(name__icontains=query_string)
    query = query.order_by('case_type', 'name', 'id')
    return query

def get_conditional_alert_headers(domain):
    common_headers = ['id', 'name']
    langs = get_language_list(domain)
    return ((TranslatedConditionalAlertUploader.sheet_name,
             common_headers + ['message_' + lang for lang in langs]),
            (UntranslatedConditionalAlertUploader.sheet_name,
             common_headers + ['message']))

def get_conditional_alert_rows(domain):
    translated_rows = []
    untranslated_rows = []

    langs = get_language_list(domain)
    for rule in get_conditional_alerts_queryset_by_domain(domain):
        schedule = rule.get_schedule()
        events = schedule.memoized_events
        send_frequency = ScheduleForm.get_send_frequency_by_ui_type(schedule.ui_type)

        # Custom schedules may have multiple events each with different content
        # Non-custom schedules may have multiple events (e.g., daily schedule sent MWF) but each
        # event will have identical content, so only include the first one
        if send_frequency not in (ScheduleForm.SEND_CUSTOM_DAILY, ScheduleForm.SEND_CUSTOM_IMMEDIATE):
            events = [events[0]]

        for event in events:
            common_columns = [rule.pk, rule.name]
            if UntranslatedConditionalAlertUploader.event_is_relevant(event):
                untranslated_rows.append(common_columns + [event.content.message.get('*', '')])
            elif TranslatedConditionalAlertUploader.event_is_relevant(event):
                translated_rows.append(common_columns + [event.content.message.get(lang, '') for lang in langs])

    return (translated_rows, untranslated_rows)

def upload_conditional_alert_workbook(domain, workbook):
    translated_uploader = TranslatedConditionalAlertUploader(domain)
    untranslated_uploader = UntranslatedConditionalAlertUploader(domain)
    return translated_uploader.upload(workbook) + untranslated_uploader.upload(workbook)

class ConditionalAlertUploader(object):
    sheet_name = None

    def __init__(self, domain):
        super(ConditionalAlertUploader, self).__init__()
        self.domain = domain
        self.langs = get_language_list(domain)
        self.msgs = []

    @classmethod
    def event_is_relevant(cls, event):
        """
        Whether the event belongs on the given sheet.
        During download, this filters events onto the correct sheet.
        During upload, depending on the circumstances, events may be allowed to change sheets.
        """
        return isinstance(event.content, SMSContent)

    def get_worksheet_errors(self, worksheet):
        if 'id' not in worksheet.headers:
            return [(messages.error, _("The '{sheet_name}' sheet is missing an id column. "
                                       "This sheet has been skipped.".format(sheet_name=self.sheet_name)))]

        return []

    def upload(self, workbook):
        self.msgs = []
        success_count = 0

        try:
            worksheet = workbook.get_worksheet(title=self.sheet_name)
        except WorksheetNotFound:
            return [(messages.error, _("This file is missing the '{sheet_name}' sheet. Please add this sheet "
                                       "and upload again.".format(sheet_name=self.sheet_name)))]

        errors = self.get_worksheet_errors(worksheet)
        if errors:
            return errors

        # Most rules are represented by a single row, but rules with custom schedules have one row per event.
        # Read through the worksheet, grouping rows by rule id and caching rule definitions.
        condensed_rows = defaultdict(list)
        rules_by_id = {}

        for index, row in enumerate(worksheet, start=2):    # one-indexed, plus header row
            if not row.get('id', None):
                self.msgs.append((messages.error, _("Row {index} in '{sheet_name}' sheet is missing "
                                  "an id.").format(index=index, sheet_name=self.sheet_name)))
                continue

            if row['id'] in condensed_rows:
                # This is the 2nd (or 3rd, 4th, ...) row for a rule we've already seen
                condensed_rows[row['id']].append(row)
                continue

            try:
                rule = AutomaticUpdateRule.objects.get(
                    pk=row['id'],
                    domain=self.domain,
                    workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                    deleted=False,
                )
            except AutomaticUpdateRule.DoesNotExist:
                self.msgs.append((messages.error,
                                 _("""Could not find rule for row {index} in '{sheet_name}' sheet, """
                                   """with id {id}""").format(index=index,
                                                              id=row['id'],
                                                              sheet_name=self.sheet_name)))
                continue

            if rule.locked_for_editing:
                self.msgs.append((messages.error, _("Row {index} in '{sheet_name}' sheet, with rule id {id}, "
                                  "is currently processing and cannot be updated.").format(index=index,
                                    id=row['id'], sheet_name=self.sheet_name)))
                continue

            if not isinstance(rule.get_schedule().memoized_events[0].content, SMSContent):
                self.msgs.append((messages.error, _("Row {index} in '{sheet_name}' sheet, with rule id {id}, "
                                  "does not use SMS content.").format(index=index, id=row['id'],
                                                                      sheet_name=self.sheet_name)))
                continue

            rules_by_id[row['id']] = rule
            condensed_rows[row['id']].append(row)

        # Update the condensed set of rules
        for rule_id, rows in condensed_rows.items():
            rule = rules_by_id[rule_id]
            schedule = rule.get_schedule()
            send_frequency = ScheduleForm.get_send_frequency_by_ui_type(schedule.ui_type)

            if send_frequency in (ScheduleForm.SEND_CUSTOM_DAILY, ScheduleForm.SEND_CUSTOM_IMMEDIATE):
                # Check that user provided one row for each event in the custom schedule
                all_events = rule.get_schedule().memoized_events
                expected = len([e for e in all_events if self.event_is_relevant(e)])
                actual = len(rows)
                if expected != actual and actual != len(all_events):
                    self.msgs.append((messages.error, _("Could not update rule with id {id} in '{sheet_name}' "
                                                        "sheet: expected {expected} row(s) but found "
                                                        "{actual}.").format(id=rule.id, sheet_name=self.sheet_name,
                                                                            expected=expected, actual=actual)))
                    continue

            with transaction.atomic():
                try:
                    dirty = self.update_rule(rule, rows)
                except RuleUpdateError as e:
                    self.msgs.append((messages.error, _("Error updating rule with id {id} in '{sheet_name}' "
                                      "sheet: {detail}").format(id=rule.id, sheet_name=self.sheet_name,
                                                                detail=str(e))))
                    continue

                if dirty:
                    rule.save()
                    success_count += 1

        self.msgs.append((messages.success, _("Updated {count} rule(s) in '{sheet_name}' sheet").format(
            count=success_count, sheet_name=self.sheet_name)))

        return self.msgs

    def update_rule(self, rule, rows):
        name_dirty = self.update_rule_name(rule, rows)
        message_dirty = self.save_rule_messages(rule, rows)
        return name_dirty or message_dirty

    def update_rule_name(self, rule, rows):
        if len(set([r['name'] for r in rows])) != 1:
            raise RuleUpdateError(_("Rule name must be the same in all rows"))

        row = rows[0]
        if 'name' in row and rule.name != row['name']:
            rule.name = row['name']
            return True
        return False

    def save_rule_messages(self, rule, rows):
        schedule = rule.get_schedule()
        send_frequency = ScheduleForm.get_send_frequency_by_ui_type(schedule.ui_type)

        # Iterate over rule's events
        if send_frequency not in (ScheduleForm.SEND_CUSTOM_DAILY, ScheduleForm.SEND_CUSTOM_IMMEDIATE):
            # For standard schedules, the rule may have multiple events (e.g., a daily rule run Mon and Tues
            # will have 2 events), but all events will have the same message, so we only need to look at the
            # first event. Since there's only one message, the user is free to move it between sheets.
            events = [rule.get_schedule().memoized_events[0]]
            allow_sheet_swap = True
        else:
            # For custom schedules, each event has its own message, so we need to look at all of them, and they may
            # be a mix of translated and untranslated.
            # If all messages are on one sheet: The user may update any or all and may move ALL of them to the
            #   other sheet. We assume that the order of rows is the same as the rule's order of events.
            # If messages are split between sheets: The user may only make updates on one sheet (once the first
            #   sheet is updated, the rule will begin processing, so the second sheet's updates will fail). Again,
            #   we assume the order of rows matches the order of events (with the other sheet's rows interleaved).
            #   The user may not move messages between sheets, since we can't match events to messages.
            events = rule.get_schedule().memoized_events
            allow_sheet_swap = len(rows) == len(events)

        message_dirty = False
        new_messages = []
        row_index = 0
        for event in events:
            old_message = event.content.message
            new_message = copy(old_message)
            if self.event_is_relevant(event) or allow_sheet_swap:
                new_message = self.update_message(new_message, rows[row_index])
                message_dirty = message_dirty or old_message != new_message
                row_index += 1
            new_messages.append(new_message)

        {
            ScheduleForm.SEND_IMMEDIATELY: self._save_immediate_schedule,
            ScheduleForm.SEND_DAILY: self._save_daily_schedule,
            ScheduleForm.SEND_WEEKLY: self._save_weekly_schedule,
            ScheduleForm.SEND_MONTHLY: self._save_monthly_schedule,
            ScheduleForm.SEND_CUSTOM_IMMEDIATE: self._save_custom_immediate_schedule,
            ScheduleForm.SEND_CUSTOM_DAILY: self._save_custom_daily_schedule,
        }[send_frequency](schedule, new_messages)

        return message_dirty

    def update_message(self, message, row):
        """
        Update the given message with values from given row, return updated message.
        """
        raise NotImplementedError()

    def _save_immediate_schedule(self, schedule, messages):
        AlertSchedule.assert_is(schedule)
        assert len(messages) == 1, "Immediate schedule expected 1 message, got %s" % len(messages)

        schedule.set_simple_alert(SMSContent(message=messages[0]),
                                  extra_options=schedule.get_extra_scheduling_options())

    def _save_daily_schedule(self, schedule, messages):
        TimedSchedule.assert_is(schedule)
        assert len(messages) == 1, "Daily schedule expected 1 message, got %s" % len(messages)

        schedule.set_simple_daily_schedule(
            schedule.memoized_events[0],
            SMSContent(message=messages[0]),
            total_iterations=schedule.total_iterations,
            start_offset=schedule.start_offset,
            extra_options=schedule.get_extra_scheduling_options(),
            repeat_every=schedule.repeat_every,
        )

    def _save_weekly_schedule(self, schedule, messages):
        TimedSchedule.assert_is(schedule)
        assert len(messages) == 1, "Weekly schedule expected 1 message, got %s" % len(messages)

        schedule.set_simple_weekly_schedule(
            schedule.memoized_events[0],
            SMSContent(message=messages[0]),
            [e.day for e in schedule.memoized_events],
            schedule.start_day_of_week,
            total_iterations=schedule.total_iterations,
            extra_options=schedule.get_extra_scheduling_options(),
            repeat_every=schedule.repeat_every,
        )

    def _save_monthly_schedule(self, schedule, messages):
        TimedSchedule.assert_is(schedule)
        assert len(messages) == 1, "Monthly schedule expected 1 message, got %s" % len(messages)

        # Negative numbers are used for monthly schedules.
        # See comment on TimedSchedule.repeat_every
        repeat_every = schedule.repeat_every * -1

        schedule.set_simple_monthly_schedule(
            schedule.memoized_events[0],
            [e.day for e in schedule.memoized_events],
            SMSContent(message=messages[0]),
            total_iterations=schedule.total_iterations,
            extra_options=schedule.get_extra_scheduling_options(),
            repeat_every=repeat_every,
        )

    def _save_custom_immediate_schedule(self, schedule, messages):
        AlertSchedule.assert_is(schedule)
        assert len(messages) == len(schedule.memoized_events), \
            "Custom schedule expected {} messages, got {}".format(len(messages), len(schedule.memoized_events))

        event_and_content_objects = list(zip(schedule.memoized_events, [SMSContent(message=m) for m in messages]))
        schedule.set_custom_alert(event_and_content_objects, extra_options=schedule.get_extra_scheduling_options())

    def _save_custom_daily_schedule(self, schedule, messages):
        TimedSchedule.assert_is(schedule)
        assert len(messages) == len(schedule.memoized_events), \
            "Custom schedule expected {} messages, got {}".format(len(messages), len(schedule.memoized_events))

        event_and_content_objects = list(zip(schedule.memoized_events, [SMSContent(message=m) for m in messages]))
        schedule.set_custom_daily_schedule(
            event_and_content_objects,
            total_iterations=schedule.total_iterations,
            start_offset=schedule.start_offset,
            extra_options=schedule.get_extra_scheduling_options(),
            repeat_every=schedule.repeat_every,
        )

class TranslatedConditionalAlertUploader(ConditionalAlertUploader):
    sheet_name = 'translated'

    @classmethod
    def event_is_relevant(cls, event):
        if not super(TranslatedConditionalAlertUploader, cls).event_is_relevant(event):
            return False

        message = event.content.message
        return len(message) and '*' not in message

    def update_message(self, message, row):
        message.pop('*', None)
        for lang in self.langs:
            key = 'message_' + lang
            if key in row:
                message[lang] = row[key]
        if not any(message.values()):
            raise RuleUpdateError(_("Missing message"))
        return message

class UntranslatedConditionalAlertUploader(ConditionalAlertUploader):
    sheet_name = 'not translated'

    @classmethod
    def event_is_relevant(cls, event):
        if not super(UntranslatedConditionalAlertUploader, cls).event_is_relevant(event):
            return False

        message = event.content.message
        return (not len(message) or '*' in message)

    def update_message(self, message, row):
        if 'message' in row:
            if not row['message']:
                raise RuleUpdateError(_("Missing message"))
            return {'*': row['message']}
        return message


import time
from win32com.client import Dispatch

class PetexException(Exception):

    def __init__(self, message):

        self.message = message

    def GetError(self):

        return self.message

    def __str__(self):

        return self.message

def Initialize():

    server = Dispatch("PX32.OpenServer.1")
    return server

def Stop():

    server = None
    return server

def DoCmd(server, command):

    lErr = server.DoCommand(command)

    if lErr > 0:
        raise PetexException("DoCmd: %s"%server.GetErrorDescription(lErr))

def DoSet(server, Sv, Val):

    lErr = server.SetValue(Sv, Val)
    appName = GetAppName(Sv)
    lErr = server.GetLastError(appName)

    if lErr > 0:
        raise PetexException("DoSet: %s"%server.GetErrorDescription(lErr))

def DoGet(server, Gv):

    value = server.GetValue(Gv)
    appName = GetAppName(Gv)
    lErr = server.GetLastError(appName)

    if lErr > 0:
        raise PetexException("DoGet: %s"%server.GetLastErrorMessage(appName))

    return value

def DoSlowCmd(server, command):

    appName = GetAppName(command)
    lErr = server.DoCommandAsync(command)

    if lErr > 0:
        raise PetexException("DoSlowCmd 1: %s"%server.GetErrorDescription(lErr))

    secs = 1
    while server.IsBusy(appName) > 0:
        time.sleep(1)
        secs += 1

    lErr = server.GetLastError(appName)
    if lErr > 0:
        raise PetexException("DoSlowCmd 2: %s"%server.GetErrorDescription(lErr))

def DoSlowGAPFunc(server, Gv):

    DoSlowCmd(server, Gv)
    DoGet(server, "GAP.LASTCMDRET")

    lErr = server.GetLastError("GAP")
    if lErr > 0:
        raise PetexException("DoSlowGAPFunc: %s"%server.GetErrorDescription(lErr))

def DoGAPFunc(server, Gv):

    DoCmd(server, Gv)
    DoGet(server, "GAP.LASTCMDRET")

    lErr = server.GetLastError("GAP")
    if lErr > 0:
        raise PetexException("DoGAPFunc: %s"%server.GetErrorDescription(lErr))

def DoSlowProsperFunc(server, Gv):

    DoSlowCmd(server, Gv)
    DoGet("PROSPER.LASTCMDRET")

    lErr = server.GetLastError("PROSPER")
    if lErr > 0:
        raise PetexException("DoProsperFunc: %s"%server.GetErrorDescription(lErr))

def GetAppName(command):

   point = command.index(".")
   appName = command[0:point].upper()

   if appName not in ["PROSPER", "MBAL", "GAP", "PVT", "RESOLVE", "REVEAL"]:
       raise PetexException("Unrecognised application name in tag string (%s)"%appName)

   return appName


import torch
from torch import nn
import torch.optim as optim
from torch.autograd import Variable
from torchvision import transforms
from DataSet import DataSet
from argparse import ArgumentParser
import numpy as np
import sys
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0"

class Net(nn.Module):
    def __init__(self, batch_size, total_words, decoder_length, dataset):
        super(Net, self).__init__()

        self.video_frame = 80
        self.batch_size = batch_size
        self.decoder_length = decoder_length
        self.total_words = total_words
        self.dataset = dataset

        # model
        self.lstm1 = nn.LSTMCell(input_size=4096, hidden_size=self.dataset.EmbeddingDim())
        # nn.init.orthogonal(self.lstm1.weight_hh);
        # nn.init.orthogonal(self.lstm1.weight_ih);
        self.lstm2 = nn.LSTMCell(input_size=2*self.dataset.EmbeddingDim(), hidden_size=self.dataset.EmbeddingDim())
        # nn.init.orthogonal(self.lstm2.weight_hh);
        # nn.init.orthogonal(self.lstm2.weight_ih);

        self.one_hot = nn.Linear(self.dataset.EmbeddingDim(), total_words)

    def forward(self, feat, caption, caption_one_hot, keep_rate):
        # initialize
        h1 = Variable(torch.zeros(self.batch_size, self.lstm1.hidden_size)).cuda();
        h2 = Variable(torch.zeros(self.batch_size, self.lstm2.hidden_size)).cuda();
        c1 = Variable(torch.zeros(self.batch_size, self.lstm1.hidden_size)).cuda();
        c2 = Variable(torch.zeros(self.batch_size, self.lstm2.hidden_size)).cuda();

        # <pad>
        padding_lstm1 = Variable(torch.zeros(self.batch_size, 4096)).cuda();
        padding_lstm2 = Variable(torch.zeros(self.batch_size, self.dataset.EmbeddingDim())).cuda();

        total_loss=Variable(torch.FloatTensor([0])).cuda();
        loss = nn.CrossEntropyLoss(reduce=None)
        # encoding
        for step in range(self.video_frame):
            h1, c1 = self.lstm1(feat[:,step,:], (h1, c1))
            h2, c2 = self.lstm2(torch.cat((padding_lstm2, h1), 1), (h2, c2))

        # decoding
        for step in range(self.decoder_length-1):
            h1, c1 = self.lstm1(padding_lstm1, (h1, c1))

            # schedule sampling with increasing rate
            coin = np.random.randint(10)
            if step > 0 and coin >= keep_rate:
                word_ids = np.argmax(one_hot_word.data.cpu().numpy(), axis=1)
                word_vec = self.dataset.IDs2VECs(word_ids)
                word_vec = Variable(torch.FloatTensor(word_vec)).cuda()
                h2, c2 = self.lstm2(torch.cat((word_vec, h1), 1), (h2, c2))
            else:
                h2, c2 = self.lstm2(torch.cat((caption[:,step,:], h1), 1), (h2, c2))

            one_hot_word = self.one_hot(h2)
            word_loss    = loss(one_hot_word, torch.max(caption_one_hot[:,step+1,:], 1)[1])
            total_loss  += torch.sum(word_loss) / self.batch_size

        return total_loss

    def inference(self, feat):
        # initialize
        h1 = Variable(torch.zeros(1, self.lstm1.hidden_size)).cuda()
        h2 = Variable(torch.zeros(1, self.lstm2.hidden_size)).cuda()
        c1 = Variable(torch.zeros(1, self.lstm1.hidden_size)).cuda()
        c2 = Variable(torch.zeros(1, self.lstm2.hidden_size)).cuda()

        sentence = []

        # <pad>
        padding_lstm1 = Variable(torch.zeros(1, 4096)).cuda();
        padding_lstm2 = Variable(torch.zeros(1, self.dataset.EmbeddingDim())).cuda();

        # encoding
        for step in range(self.video_frame):
            h1, c1 = self.lstm1(feat[:,step,:], (h1, c1))
            h2, c2 = self.lstm2(torch.cat((padding_lstm2, h1), 1), (h2, c2))

        # decoding
        previous_word = self.dataset.Word2Vec('<BOS>')

        for step in range(self.decoder_length-1):
            previous_word = Variable(torch.FloatTensor(previous_word))
            previous_word = previous_word.reshape((1, self.dataset.EmbeddingDim()))
            previous_word = previous_word.cuda()
            h1, c1 = self.lstm1(padding_lstm1, (h1, c1))
            h2, c2 = self.lstm2(torch.cat((previous_word, h1), 1), (h2, c2))

            word_one_hot = self.one_hot(h2)
            _, word_id = torch.max(word_one_hot, 1)
            word = self.dataset.ID2Word(word_id.data.cpu().numpy()[0])
            sentence.append(word)

            if word == '<EOS>': break

            previous_word = self.dataset.Word2Vec(word)

        return sentence

def train(savepath):
    if savepath is None:
        print ('specify path to save the trained model by -s')
        sys.exit()

    batch_size = 8
    epochs = 200

    data = DataSet(word2vec='word_model/model_350.wv', training_features='MLDS_hw2_1_data/training_data/id.txt', training_label='MLDS_hw2_1_data/training_label.json', testing_features='MLDS_hw2_1_data/testing_data/id.txt', testing_label='MLDS_hw2_1_data/testing_label.json', batch_size=batch_size)

    total_words = data.VocabSize()
    decoder_length = data.MaxSeqLength()

    model = Net(batch_size=batch_size, total_words=total_words, decoder_length=decoder_length, dataset=data)
    model.cuda()
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    loss_list = []

    training_data_size = data.training_num
    batch_per_epoch = training_data_size // batch_size
    for epoch in range(epochs):
        for batch in range(batch_per_epoch):
            feature, caption, caption_one_hot = data.next_batch()
            feature, caption, caption_one_hot = Variable(torch.FloatTensor(feature)), Variable(torch.FloatTensor(caption)), Variable(torch.LongTensor(caption_one_hot))
            feature, caption, caption_one_hot = feature.cuda(), caption.cuda(), caption_one_hot.cuda()

            # print (caption_one_hot.shape)
            loss = model(feature, caption, caption_one_hot, 9-9/epochs*epoch)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            loss_list.append(loss.item())

            print ('===> epoch: {}, steps: {}, loss: {:.4f}, sampling rate: {}'.format(epoch+1, batch, loss.item(), 1/epochs*epoch))
        if epoch%10 == 0:
           torch.save(model.state_dict(), '{}/s2vt.pytorch-{}'.format(savepath, epoch))

    torch.save(model.state_dict(), '{}/s2vt.pytorch'.format(savepath))

def test(checkpoint):

    data = DataSet(word2vec='word_model/model_350.wv', training_features='MLDS_hw2_1_data/training_data/id.txt', training_label='MLDS_hw2_1_data/training_label.json', testing_features='MLDS_hw2_1_data/testing_data/id.txt', testing_label='MLDS_hw2_1_data/testing_label.json', batch_size=8)
    total_words = data.VocabSize()
    decoder_length = data.MaxSeqLength()

    model = Net(batch_size=8, total_words=total_words, decoder_length=decoder_length, dataset=data)
    model.cuda()

    model.load_state_dict(state_dict=torch.load(checkpoint))
    test_data, test_id = data.test_batch()

    i = 0
    output_txt = open("./caption.txt", 'w')

    for test_feat in test_data:
        test_feat = Variable(torch.FloatTensor(test_feat))
        if (test_feat.shape[0] != 80):
            continue
        test_feat = test_feat.reshape((1, 80, -1))
        test_feat = test_feat.cuda()
        output   = model.inference(test_feat)
        sentence = ' '.join(output)
        sentence = sentence.replace('<BOS>', '')
        sentence = sentence.replace('<EOS>', '')
        sentence = sentence.replace('<PAD>', '')

        output_txt.write(test_id[i]+','+sentence+'\n'); i += 1
        print (sentence)

def main(args):
    if args.mode == 'train': train(args.save)
    elif args.mode == 'inference': test(args.model)
    else:
        print ('invalid mode:', args.mode)
        sys.exit()

def parse():
    parser = ArgumentParser()
    parser.add_argument('--mode', required=True, help='either train or inference')
    parser.add_argument('--save', '-s', default=None, help='the directory to store the trained model')
    parser.add_argument('--model', default=None, help='the model to load for inference')
    return parser.parse_args()

if __name__ == '__main__':
    main(parse())


MSG_400 = 'Missing or illegal argument'
MSG_403 = 'Forbidden'
DUPLICATE_USERNAME = 'User with the same username exists'
MSG_404 = 'Wrong URL or resource not found'
DUPLICATE_TITLE = 'Content with the same title exists'
NO_POST_EXISTS = 'there is no such post id'
NO_COMMENT_EXISTS = 'there is no such comment'

ILLEGAL_ARGUMENT = MSG_400
REQUEST_FORBIDDEN = MSG_403


__all__ = [ 'libgobgp', '_PATTRS_CAP', '_AF_NAME', 'Buf', 'Path', 'unpack_buf', ]

from ctypes import *
from struct import *
import os

# load C shared library
libgobgp = cdll.LoadLibrary(os.environ["GOPATH"] +"/src/github.com/osrg/gobgp/gobgp/lib/libgobgp.so")

# constants
_PATTRS_CAP = 32
_AF_NAME = dict([(4, "ipv4-unicast"), (6, "ipv6-unicast"), ])

# structs
class Buf(Structure):
  _fields_ = [("value", POINTER(c_char)),
              ("len", c_int),
             ]
class Path(Structure):
  _fields_ = [("nlri", Buf),
              ("path_attributes", POINTER(POINTER(Buf) * _PATTRS_CAP)),
              ("path_attributes_len", c_int),
              ("path_attributes_cap", c_int),
             ]

# argument and return type definitions
libgobgp.serialize_path.restype = POINTER(Path)
libgobgp.serialize_path.argtypes = (c_int, c_char_p)
libgobgp.decode_path.restype = c_char_p
libgobgp.decode_path.argtypes = (POINTER(Path), )

def unpack_buf(buf):
  # unpack Buf obj
  return unpack(str(buf.len)+"s", buf.value[:buf.len])[0]

if __name__ == '__main__':
  pass


from datetime import datetime
import json
import os
import xml.etree.ElementTree as ET

import sys

def parsexml(f):
    result = []

    et = ET.fromstring(f.read())
    for prochazejici in et.findall('./PROCHAZEJICI'):
        uco = prochazejici.attrib['UCO']
        # print(uco)
        for pruchod in prochazejici.findall('./PRUCHOD'):
            body = None
            zacatek = None
            konec = None
            for akce in pruchod.findall('./AKCE'):
                cas = akce.attrib['CAS']
                typ = akce.attrib['TYP']
                suma = akce.attrib['SUMA']

                cas = datetime.strptime(cas, '%Y%m%d%H%M%S')

                # print("{} {} {} {}".format(uco, cas, typ, suma))

                if body is None or body < suma:
                    body = suma

                if typ == 'a':
                    if zacatek != None and konec != None:
                        #store data
                        trvani = konec - zacatek if konec is not None else None
                        result.append({'uco': uco, 'cas': trvani, 'body': body})
                    zacatek = cas
                    konec = None
                if typ in ['b', 'c']:
                    if konec is None or konec < cas:
                        konec = cas

            if konec is not None and zacatek is not None:
                assert(konec > zacatek)
            trvani = konec - zacatek if konec is not None else None
            result.append({'uco': uco, 'cas': trvani, 'body': body})
    return result

def parse_zaverecny_dotaznik(filedir):
    file_path = 'Zaverecny_dotaznik.xml'
    with open(os.path.join(file_dir, file_path)) as f:
        result = []

        et = ET.fromstring(f.read())
        for prochazejici in et.findall('./PROCHAZEJICI'):
            uco = prochazejici.attrib['UCO']
            #print(uco)
            for pruchod in prochazejici.findall('./PRUCHOD'):
                for akce in pruchod.findall('./AKCE'):
                    if akce.attrib['TYP'] == 'c':
                        odps = {'uco': uco}
                        for odp in akce.findall('./ODP'):
                            por = 'q' + odp.attrib['POR']
                            hod = odp.attrib['HOD']
                            try:
                                int(hod)
                            except ValueError:
                                continue
                            odps[por] = hod
                        result.append(odps)
        with open(os.path.join(file_dir, '..', 'json', 'zaverecny.json'), 'w') as fp:
            json.dump(result, fp)

def parse_cas():
    result = []
    # for file_path in ['howtowork.xml']:
    for file_path in os.listdir(file_dir):
        if not file_path.endswith('.xml'):
            continue
        with open(os.path.join(file_dir, file_path)) as f:
            print(file_path)
            blok = parsexml(f)
            for record in blok:
                record['blok'] = file_path.rstrip('.xml')
                record['cas'] = record['cas'].total_seconds() if record['cas'] is not None else None
            result.extend(blok)

    # print([r for r in result if r['uco']=='374368'])

    with open(os.path.join(file_dir, '..', 'json', 'casy.json'), 'w') as fp:
        json.dump(result, fp)

class Question:
    def __init__(self, idmd5, text):
        self.idmd5 = idmd5
        self.text = text

def merge_t_answers_by_idmd5(answers):
    merged = {}
    for answer in answers:
        if not answer['idmd5'] in merged:
            merged[answer['idmd5']] = answer
        else:
            merged[answer['idmd5']]['odpovedi'].extend(answer['odpovedi'])
    return merged

class QANSW:
    def __init__(self, file_path):
        fp = open(file_path)
        self.tree = ET.fromstring(fp.read())

    def t_answers_unmerged(self):
        result = []
        for pruchod in self.tree.findall('.//PROCHAZEJICI/PRUCHOD'):
            otazky = {}

            for otazka in pruchod.findall('./OTAZKA'):
                poradi = otazka.attrib['PORADI']
                idmd5 = otazka.find('./IDMD5').text
                sada = otazka.find('./SADA').text

                otazky[poradi] = {'idmd5': idmd5, 'sada': sada, 'odpovedi': []}

            for akce in pruchod.findall('./AKCE'):
                odpovedi = {}
                for odpoved in akce.findall('./ODP'):
                    poradi = odpoved.attrib['POR']
                    id = odpoved.attrib['ID']
                    hodnota = odpoved.attrib['HOD']

                    if not poradi in odpovedi:
                        odpovedi[poradi] = []
                    odpovedi[poradi].append({'id': id, 'hodnota': hodnota})

                for bod in akce.findall('./BOD'):
                    poradi = bod.attrib['POR']
                    pocet = bod.attrib['POC']

                    if poradi in odpovedi:
                        # skip last word and nulls
                        for i, ok in enumerate([x for x in pocet.split() if x != 'null'][:-1]):
                            try:
                                odpovedi[poradi][i]['body'] = ok
                            except IndexError:
                                continue
                                idmd5 = otazky[poradi]['idmd5']
                                q = None
                                for qq in self.questions():
                                    if qq.idmd5 == idmd5:
                                        q = qq.text

                                print(pocet, odpovedi[poradi], file=sys.stderr)
                                print("overlapping options in question: ", q, file=sys.stderr)
                                #raise('halt')

                for poradi in otazky.keys():
                    if poradi in odpovedi:
                        otazky[poradi]['odpovedi'].extend(odpovedi[poradi])

            result.extend(list(otazky.values()))
        return result

    def questions(self):
        result = []
        for otazka in self.tree.findall('.//TEXTREG/TEXT'):
            idmd5 = otazka.attrib['IDMD5']
            text = otazka.text
            result.append(Question(idmd5, text))
        return result


import sys

from gi.repository import Gtk, Gdk

from .XmlDataProvider import XmlDataProvider
from .lib.common import APP_ICON_ON
from .lib import utils
from .lib import i18n
import uuid
import logging

drop_yes = ("drop_yes", Gtk.TargetFlags.SAME_WIDGET, 0)
drop_no = ("drop_no", Gtk.TargetFlags.SAME_WIDGET, 0)

class BookmarkConfiguration(object):

    GROUP_TYPE = 'GROUP'
    RADIO_TYPE = 'RADIO'
    SEPARATOR_TYPE = 'SEPARATOR'

    def __init__(self, dataProvider, updateFunc, standalone=False):
        self.dataProvider = dataProvider
        self.updateFunc = updateFunc
        self.standalone = standalone

        self.log = logging.getLogger('radiotray')

        # get gui objects
        gladefile = utils.load_ui_file("configBookmarks.glade")
        self.wTree = gladefile
        self.window = self.wTree.get_object("window1")
        self.list = self.wTree.get_object("treeview1")

        # edit bookmark
        self.nameEntry = self.wTree.get_object("nameEntry")
        self.nameEntryLabel = self.wTree.get_object("label1")
        self.urlEntry = self.wTree.get_object("urlEntry")
        self.urlEntryLabel = self.wTree.get_object("label2")
        self.config = self.wTree.get_object("editBookmark")
        self.radioGroup = self.wTree.get_object("radioGroup")
        self.radioGroupLabel = self.wTree.get_object("label8")

        # edit group
        self.configGroup = self.wTree.get_object("editGroup")
        self.groupNameEntry = self.wTree.get_object("groupNameEntry")
        self.parentGroup = self.wTree.get_object("parentGroup")
        self.parentGroupLabel = self.wTree.get_object("label4")

        # separator move
        self.sepMove = self.wTree.get_object("sepMove")
        self.sepGroup = self.wTree.get_object("sepGroup")

        # set icon
        self.window.set_icon_from_file(APP_ICON_ON)
        self.config.set_icon_from_file(APP_ICON_ON)
        self.configGroup.set_icon_from_file(APP_ICON_ON)
        self.sepMove.set_icon_from_file(APP_ICON_ON)

        # populate list of radios
        self.load_data()

        # config tree ui
        cell = Gtk.CellRendererText()
        tvcolumn = Gtk.TreeViewColumn(_('Radio Name'), cell)
        self.list.append_column(tvcolumn)
        tvcolumn.add_attribute(cell, 'text', 0)

        # config combo ui
        cell2 = Gtk.CellRendererText()
        self.parentGroup.pack_start(cell2, True)
        self.parentGroup.add_attribute(cell2, 'text', 0)

        # config add radio group combo ui
        cell4 = Gtk.CellRendererText()
        self.radioGroup.pack_start(cell4, True)
        self.radioGroup.add_attribute(cell4, 'text', 0)

        # separator new group combo ui
        cell3 = Gtk.CellRendererText()
        self.sepGroup.pack_start(cell3, True)
        self.sepGroup.add_attribute(cell3, 'text', 0)

        # connect events
        if (self.window):
            dic = { "on_newBookmarkButton_clicked" : self.on_add_bookmark_clicked,
                "on_newSeparatorButton_clicked" : self.on_add_separator_clicked,
                "on_editBookmarkButton_clicked" : self.on_edit_bookmark_clicked,
                "on_delBookmarkButton_clicked" : self.on_remove_bookmark_clicked,
                "on_close_clickedButton_clicked" : self.on_close_clicked,
                "on_nameEntry_activated" : self.on_nameEntry_activated,
                "on_urlEntry_activated" : self.on_urlEntry_activated,
                "on_newGroupButton_clicked" : self.on_newGroupButton_clicked}
            self.wTree.connect_signals(self)

        # enable drag and drop support
        self.list.enable_model_drag_source(
            Gdk.ModifierType.BUTTON1_MASK, [drop_yes], Gdk.DragAction.MOVE)
        self.list.enable_model_drag_dest(
            [drop_yes], Gdk.DragAction.MOVE)

        self.list.connect("drag-drop", self.drag_drop_cb)

        # Connect row activation with bookmarks conf
        self.list.connect("row-activated", self.on_row_activated)

    def drag_drop_cb(self, treeview, dragcontext, x, y, eventtime):
        #check if there's a valid drop location
        if(treeview.get_dest_row_at_pos(x, y) == None):
            print("Dropped into nothing")
            return

        target_path, drop_position = treeview.get_dest_row_at_pos(x, y)
        model, source = treeview.get_selection().get_selected()
        target = model.get_iter(target_path)
        sourceName = model.get_value(source,1)
        targetName = model.get_value(target,1)

        is_parentable = self.checkParentability(model, target, drop_position)

        if is_parentable:
            writeData = False
            if drop_position == Gtk.TreeViewDropPosition.INTO_OR_BEFORE:
                pass
                #model.insert_before (source, target)
                #model.prepend(source, target)
            elif drop_position == Gtk.TreeViewDropPosition.INTO_OR_AFTER:
                pass
                #model.insert_after (source, target)
                #model.append(source, target)
            elif drop_position == Gtk.TreeViewDropPosition.BEFORE:
                if model.iter_depth(source) == model.iter_depth(target):
                    writeData = True
                    model.move_before(source, target)
            elif drop_position == Gtk.TreeViewDropPosition.AFTER:
                if model.iter_depth(source) == model.iter_depth(target):
                    writeData = True
                    model.move_after(source, target)

            dragcontext.finish(True, False, eventtime)
            if writeData:
                self.dataProvider.moveToPosition(sourceName, targetName, drop_position)
        else:
            return dragcontext.finish(False, False, eventtime)

    def load_data(self):
        # the meaning of the three columns is: description, id, type
        treestore = Gtk.TreeStore(str, str, str)
        root = self.dataProvider.getRootGroup()
        self.add_group_data(root, None, treestore)
        self.list.set_model(treestore)

    #drag and drop support
    def checkParentability(self, model, target, drop_position):
        if (drop_position == Gtk.TreeViewDropPosition.INTO_OR_BEFORE
                or drop_position == Gtk.TreeViewDropPosition.INTO_OR_AFTER) \
                and (model.get_value(target, 2) == self.RADIO_TYPE or model.get_value(target, 2) == self.SEPARATOR_TYPE):
            return False
        else:
            return True

    #drag and drop support
    def expandToPath(self, treeview, path):
        for i in range(len(path)):
            treeview.expand_row(path[:i+1], open_all=False)

    def add_group_data(self, group, parent, treestore):

        iter = None
        if(group.get('name') != 'root'):
            iter = treestore.append(parent, [group.get('name'), group.get('name'), self.GROUP_TYPE])

        for item in group:

            if item.get('name') == None:
                continue

            if (item.tag == 'bookmark'):
                if(item.get('name').startswith('[separator')):
                    treestore.append(iter, ['-- ' + _('Separator') + ' --', item.get('name'), self.SEPARATOR_TYPE])
                else:
                    treestore.append(iter, [item.get('name'), item.get('name'), self.RADIO_TYPE])
            else:
                self.add_group_data(item, iter, treestore)

    def on_add_separator_clicked(self, widget):
        # hack: generate a unique name
        name = '[separator-' + str(uuid.uuid4()) + ']'
        self.dataProvider.addRadio(name, name)
        self.load_data()

    def on_add_bookmark_clicked(self, widget):

        # reset old dialog values
        self.nameEntry.set_text('')
        self.urlEntry.set_text('')
        self.config.set_title(_('Add new station'))
        self.nameEntry.grab_focus()
        self.radioGroup.show()
        self.radioGroupLabel.show()

        # populate groups
        liststore = Gtk.ListStore(str)

        for group in self.dataProvider.listGroupNames():
            liststore.append([group])
            print("group found: " + group)

        self.radioGroup.set_model(liststore)

        # default to root
        self.radioGroup.set_active(0)

        # get current selected group and set it as default
        selection = self.list.get_selection()
        (model, iter) = selection.get_selected()

        if type(iter).__name__=='TreeIter':
            selectedName = model.get_value(iter,1)
            selectedType = model.get_value(iter, 2)

            if (selectedType == self.GROUP_TYPE):
                groupIndex = self.dataProvider.listGroupNames().index(selectedName)
                self.radioGroup.set_active(groupIndex)

        # show dialog
        result = self.config.run()
        if result == 2:
            name = self.nameEntry.get_text()
            url = self.urlEntry.get_text()
            index = self.radioGroup.get_active()
            new_group = liststore[index][0]

            if len(name) > 0 and len(url) > 0:
                if self.dataProvider.addRadio(name, url, new_group):
                    self.load_data()
            else:
                print('No radio information provided!')
        self.config.hide()

    def on_edit_bookmark_clicked(self, widget):

        #get current selected element
        selection = self.list.get_selection()
        (model, iter) = selection.get_selected()

        if type(iter).__name__=='TreeIter':

            selectedName = model.get_value(iter,1)
            selectedType = model.get_value(iter, 2)

            liststore = Gtk.ListStore(str)

            for group in self.dataProvider.listGroupNames():
                liststore.append([group])
                self.log.debug('group found: "%s"', group)

            if (selectedType == self.RADIO_TYPE):

                #set combo box model
                self.radioGroup.set_model(liststore)

                #get radio bookmark details
                selectedRadioUrl = self.dataProvider.getRadioUrl(selectedName)
                selectedRadio = self.dataProvider._radioExists(selectedName)
                currentGroup = selectedRadio.getparent().get("name")
                groupIndex = self.dataProvider.listGroupNames().index(currentGroup)

                # populate dialog with radio information
                self.nameEntry.set_text(selectedName)
                self.urlEntry.set_text(selectedRadioUrl)
                oldName = selectedName
                self.config.set_title(_('Edit %s') % selectedName)
                self.nameEntry.grab_focus()
                self.radioGroup.set_active(groupIndex)

                # show dialog
                result = self.config.run()

                if result == 2:
                    name = self.nameEntry.get_text()
                    url = self.urlEntry.get_text()
                    index = self.radioGroup.get_active()
                    new_group = liststore[index][0]

                    if len(name) > 0 and len(url) > 0:
                        if self.dataProvider.updateRadio(oldName, name, url):
                            model.set_value(iter,0,name)
                            model.set_value(iter,1,name)
                        if new_group != currentGroup:
                            self.dataProvider.updateElementGroup(selectedRadio, new_group)
                            self.load_data()
                    else:
                        self.log.debug('No radio information provided!')
                self.config.hide()

            elif(selectedType == self.GROUP_TYPE):

                #set  combo box model
                self.parentGroup.set_model(liststore)

                #get group details
                selectedGroup = self.dataProvider._groupExists(selectedName)
                currentGroup = selectedGroup.getparent().get("name")
                groupIndex = self.dataProvider.listGroupNames().index(currentGroup)

                #populate dialog with group information
                self.groupNameEntry.set_text(selectedName)
                self.configGroup.set_title(_('Edit group'))
                oldName = selectedName
                self.parentGroup.set_active(groupIndex)

                result = self.configGroup.run()
                if result == 2:
                    name = self.groupNameEntry.get_text()
                    index = self.parentGroup.get_active()
                    new_group = liststore[index][0]

                    if len(name) > 0:
                        if(self.dataProvider.updateGroup(oldName, name)):
                            model.set_value(iter,0,name)
                            model.set_value(iter,1,name)
                        if new_group != selectedName and new_group != currentGroup:
                            self.dataProvider.updateElementGroup(selectedGroup, new_group)
                            self.load_data()
                        else:
                            self.log.debug('No group information provided')

                self.configGroup.hide()

            elif(selectedType == self.SEPARATOR_TYPE):

                #Set combo box model
                self.sepGroup.set_model(liststore)

                #get radio bookmark details
                selectedRadio = self.dataProvider._radioExists(selectedName)
                currentGroup = selectedRadio.getparent().get("name")
                groupIndex = self.dataProvider.listGroupNames().index(currentGroup)

                # populate dialog with radio information
                self.sepMove.set_title(_('Edit Separator'))
                self.sepGroup.grab_focus()
                self.sepGroup.set_active(groupIndex)

                # show dialog
                result = self.sepMove.run()

                if result == 2:
                    index = self.sepGroup.get_active()
                    new_group = liststore[index][0]

                    if new_group != currentGroup:
                        self.dataProvider.updateElementGroup(selectedRadio, new_group)
                        self.load_data()

                self.sepMove.hide()

    def on_row_activated(self, widget, row, cell):
      self.on_edit_bookmark_clicked(widget)

    def on_remove_bookmark_clicked(self, widget):

        #get current selected element
        selection = self.list.get_selection()
        (model, iter) = selection.get_selected()

        if type(iter).__name__=='TreeIter':

            selectedRadioName = model.get_value(iter,0)
            separatorFlag = model.get_value(iter,1)
            print(selectedRadioName + " - " + separatorFlag)

            # if separator then just remove it
            if not separatorFlag.startswith("[separator-"):

                confirmation = Gtk.MessageDialog(
                    self.window,
                    Gtk.DialogFlags.MODAL,
                    Gtk.MessageType.QUESTION,
                    Gtk.ButtonsType.YES_NO,
                    _("Are you sure you want to delete \"%s\"?") % selectedRadioName
                )

                result = confirmation.run()

                if result == -8:
                    # remove from data provider
                    self.dataProvider.removeRadio(selectedRadioName)

                    # remove from gui
                    model.remove(iter)

                confirmation.hide()
            else:
                self.dataProvider.removeRadio(separatorFlag)
                # remove from gui
                model.remove(iter)

    def on_close_clicked(self, widget):

        self.updateFunc()
        self.window.hide()

    # close the window and quit
    def on_delete_event(self, widget, event, data=None):
        if self.standalone:
            Gtk.main_quit()
        return False

    def on_nameEntry_activated(self, widget):
        self.urlEntry.grab_focus()

    def on_urlEntry_activated(self, widget):
        self.config.response(2)

    def on_newGroupButton_clicked(self, widget):

        # reset old dialog values
        self.groupNameEntry.set_text('')
        self.configGroup.set_title(_('Add new group'))
        self.parentGroupLabel.show()
        self.parentGroup.show()
        self.groupNameEntry.grab_focus()

        # populate parent groups
        liststore = Gtk.ListStore(str)

        for group in self.dataProvider.listGroupNames():
            liststore.append([group])
            self.log.debug('group found: "%s"', group)

        self.parentGroup.set_model(liststore)

        # default to root
        self.parentGroup.set_active(0)

        # get current selected group and set it as default
        selection = self.list.get_selection()
        (model, iter) = selection.get_selected()

        if type(iter).__name__=='TreeIter':
            selectedName = model.get_value(iter,1)
            selectedType = model.get_value(iter, 2)

            if (selectedType == self.GROUP_TYPE):
                groupIndex = self.dataProvider.listGroupNames().index(selectedName)
                self.parentGroup.set_active(groupIndex)

        # show dialog
        result = self.configGroup.run()
        if result == 2:

            # get group name
            name = self.groupNameEntry.get_text()

            # get parent group name
            index = self.parentGroup.get_active()
            parent_group = liststore[index][0]

            if len(name) > 0:
                if self.dataProvider.addGroup(parent_group, name):
                    self.load_data()
            else:
                self.log.debug('No group information provided!')
        self.configGroup.hide()