bl_info = {
    'name': 'Blender Pyrogenesis Importer',
    'author': 'Stanislas Daniel Claude Dolcini',
    'version': (1, 3, 12),
    'blender':  (2, 90, 3),
    'location': 'File > Import-Export',
    'description': 'Import ',
    'wiki_url': 'https://github.com/StanleySweet/blender_pyrogenesis_importer',
    'category': 'Import-Export'
}

def get_version_string():
    return str(bl_info['version'][0]) + '.' + str(bl_info['version'][1]) + '.' + str(bl_info['version'][2])

#
# Script reloading (if the user calls 'Reload Scripts' from Blender)
#

def reload_package(module_dict_main):
    import importlib
    from pathlib import Path

    def reload_package_recursive(current_dir, module_dict):
        for path in current_dir.iterdir():
            if "__init__" in str(path) or path.stem not in module_dict:
                continue

            if path.is_file() and path.suffix == ".py":
                importlib.reload(module_dict[path.stem])
            elif path.is_dir():
                reload_package_recursive(path, module_dict[path.stem].__dict__)

    reload_package_recursive(Path(__file__).parent, module_dict_main)

if "bpy" in locals():
    reload_package(locals())

import bpy
from bpy_extras.io_utils import ImportHelper

def menu_func_import(self, context):
    self.layout.operator(ImportPyrogenesisActor.bl_idname, text='Pyrogenesis Actor (.xml)')

def register():
    # bpy.utils.register_module(__name__)
    bpy.utils.register_class(ImportPyrogenesisActor)
    # add to the export / import menu
    bpy.types.TOPBAR_MT_file_import.append(menu_func_import)

def unregister():
    # bpy.utils.unregister_module(__name__)
    bpy.utils.unregister_class(ImportPyrogenesisActor)
    # remove from the export / import menu
    bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)

import os, sys

class ImportPyrogenesisActor(bpy.types.Operator, ImportHelper):
    """Load a Pyrogenesis actor file"""
    bl_label = "Import Pyrogenesis Actor"
    bl_idname = 'import_pyrogenesis_scene.xml'
    currentPath = ""
    filter_glob:  bpy.props.StringProperty(
        default="*.xml",
        options={'HIDDEN'}
    )

    import_props: bpy.props.BoolProperty(
        name='Import props',
        description='Whether to include props in the importation.',
        default=True
    )

    import_textures: bpy.props.BoolProperty(
        name='Import textures',
        description='Whether to include textures in the importation.',
        default=True
    )

    import_depth: bpy.props.IntProperty(
        name='Import Depth',
        description='How much prop depth there should be',
        default=-1
    )

    def draw(self, context):
        layout = self.layout

        layout.prop(self, 'import_props')
        layout.prop(self, 'import_textures')
        layout.prop(self, 'import_depth')

    def execute(self, context):
        return self.import_pyrogenesis_actor(context)

    def find_parent_armature(self, bone):
        for armature in bpy.data.armatures:
            for armature_bone in armature.bones:
                if bone.name == armature_bone.name:
                    for obj in bpy.data.objects:
                        if hasattr(obj, 'data') and hasattr(obj.data, 'name') and obj.data.name == armature.name:
                             return obj

        return None

    def set_copy_transform_constraint(self, obj, parent):
        """Set constraints for props so that they fit their prop point."""

        if(str(type(parent)) == '<class \'bpy_types.Bone\'>'):
            armature = self.find_parent_armature(parent)
            print(obj.name + " -> " + armature.name +  " -> " + parent.name)
            constraint = obj.constraints.new('COPY_LOCATION')
            constraint.show_expanded = False
            constraint.mute = False
            constraint.target = armature
            constraint.subtarget = parent.name
            constraint2 = obj.constraints.new('COPY_ROTATION')
            constraint2.show_expanded = False
            constraint2.mute = False
            constraint2.target = armature
            constraint2.subtarget = parent.name
            return

        print(obj.name + " -> " + parent.name)
        constraint = obj.constraints.new('COPY_LOCATION')
        constraint.show_expanded = False
        constraint.mute = False
        constraint.target = parent
        constraint2 = obj.constraints.new('COPY_ROTATION')
        constraint2.show_expanded = False
        constraint2.mute = False
        constraint2.target = parent
        obj.parent = parent

    def create_new_material(self, textures, material):
        mname = None
        for texture in textures:
            if texture.split('|')[0] == 'baseTex':
                mname = os.path.basename(texture.split('|')[1])
                break

        if mname is None:
            mname = os.path.basename(textures[0].split('|')[1])

        if (bpy.data.materials.get(mname) is not None):
            return mname

        mat = bpy.data.materials.new(name= mname)
        mat.use_nodes = True
        bsdf = mat.node_tree.nodes["Principled BSDF"]

        for texture in textures:
            fname = os.path.basename(texture.split('|')[1])
            print(fname)
            if fname not in bpy.data.images:
                continue

            texImage = mat.node_tree.nodes.new('ShaderNodeTexImage')
            texImage.image = bpy.data.images[fname]
            if texture.split('|')[0] == 'baseTex':
                mat.node_tree.links.new(bsdf.inputs['Base Color'], texImage.outputs['Color'])

                if 'player_trans' in material:
                    color_node = mat.node_tree.nodes.new('ShaderNodeRGB')
                    color_node.outputs[0].default_value = (1, 0.213477, 0.0543914, 1)
                    multiply_node = mat.node_tree.nodes.new('ShaderNodeMixRGB')
                    multiply_node.blend_type = 'MULTIPLY'
                    invert_node = mat.node_tree.nodes.new('ShaderNodeInvert')
                    mat.node_tree.links.new(invert_node.inputs['Color'], texImage.outputs['Alpha'])
                    mat.node_tree.links.new(multiply_node.inputs[1], texImage.outputs['Color'])
                    mat.node_tree.links.new(multiply_node.inputs[2], color_node.outputs['Color'])
                    mat.node_tree.links.new(multiply_node.inputs[0], invert_node.outputs['Color'])
                    mat.node_tree.links.new(bsdf.inputs['Base Color'], multiply_node.outputs['Color'])

                elif 'basic_trans' in material:
                    mix_shader_node = mat.node_tree.nodes.new('ShaderNodeMixShader')
                    transparent_node = mat.node_tree.nodes.new('ShaderNodeBsdfTransparent')
                    mat.node_tree.links.new(mix_shader_node.inputs[0], texImage.outputs['Alpha'])
                    mat.node_tree.links.new(mix_shader_node.inputs[2], bsdf.outputs['BSDF'])
                    mat.node_tree.links.new(mix_shader_node.inputs[1], transparent_node.outputs['BSDF'])

                    output_node = mat.node_tree.nodes.get("Material Output")
                    mat.node_tree.links.new(output_node.inputs['Surface'], mix_shader_node.outputs['Shader'])
                    mat.blend_method = 'CLIP'

                continue

            if texture.split('|')[0] == 'normTex':
                texImage.image.colorspace_settings.name='Non-Color'
                normal_node = mat.node_tree.nodes.new('ShaderNodeNormalMap')
                separate_node = mat.node_tree.nodes.new('ShaderNodeSeparateXYZ')
                invert_node = mat.node_tree.nodes.new('ShaderNodeInvert')
                join_node = mat.node_tree.nodes.new('ShaderNodeCombineXYZ')

                mat.node_tree.links.new(separate_node.inputs['Vector'], texImage.outputs['Color'])
                #Direct X normals need to have their Y channel inverted for OpenGL
                mat.node_tree.links.new(invert_node.inputs['Color'], separate_node.outputs['Y'])
                mat.node_tree.links.new(join_node.inputs['X'], separate_node.outputs['X'])
                mat.node_tree.links.new(join_node.inputs['Z'], separate_node.outputs['Z'])
                mat.node_tree.links.new(join_node.inputs['Y'], invert_node.outputs['Color'])
                mat.node_tree.links.new(normal_node.inputs['Color'], join_node.outputs['Vector'])
                mat.node_tree.links.new(bsdf.inputs['Normal'], normal_node.outputs['Normal'])
                continue

            if texture.split('|')[0] == 'specTex':
                texImage.image.colorspace_settings.name='Non-Color'
                mat.node_tree.links.new(bsdf.inputs['Specular'], texImage.outputs['Color'])
                continue

        return mat.name

    def assign_material_to_object(self, ob, material_name):
        """Assigns a given material name to an object."""

        # Get any exiting material with that name
        mat = bpy.data.materials.get(material_name)
        if mat is None:
            # No material, create.
            mat = bpy.data.materials.new(name=material_name)
        if ob.data.materials:
            # Assign to 1st material slot.
            ob.data.materials[0] = mat
        else:
            # No slots, append.
            ob.data.materials.append(mat)

    def import_pyrogenesis_actor(self, context):
        import xml.etree.ElementTree as ET
        print('loading ' + self.filepath + '...')

        self.currentPath = (self.filepath[0:self.filepath.find('actors')]).replace("\\", "/")
        root = ET.parse(self.filepath).getroot()
        self.parse_actor(root)

        return {'FINISHED'}
    def create_custom_mesh(self, objname, px, py, pz, width, depth):
        # Define arrays for holding data
        myvertex = []
        myfaces = []

        # Create all Vertices

        # vertex 0
        mypoint = [(-width/2, -depth/2, 0.01)]
        myvertex.extend(mypoint)

        # vertex 1
        mypoint = [(width/2, -depth/2, 0.01)]
        myvertex.extend(mypoint)

        # vertex 2
        mypoint = [(-width/2, depth/2, 0.01)]
        myvertex.extend(mypoint)

        # vertex 3
        mypoint = [(width/2, depth/2, 0.01)]
        myvertex.extend(mypoint)

        # -------------------------------------
        # Create all Faces
        # -------------------------------------
        myface = [(0, 1, 3, 2)]
        myfaces.extend(myface)

        # Generate mesh data
        mymesh = bpy.data.meshes.new(objname)
        mymesh.from_pydata(myvertex, [], myfaces)
        # Calculate the edges
        mymesh.update(calc_edges=True)
        myobject = bpy.data.objects.new(objname, mymesh)
        # Set Location
        myobject.location.x = px
        myobject.location.y = py
        myobject.location.z = pz
        scene = bpy.context.scene
        scene.collection.objects.link(myobject)

        return myobject

    def get_element_from_variant(self, root, name):
        import xml.etree.ElementTree as ET
        for child in root:
            if child.tag == name:
                return child

        # No mesh was found in this variant.
        if 'file' in root.attrib is not None:
            variantParent = ET.parse(self.currentPath + 'variants/' +  root.attrib['file']).getroot()
            return self.get_element_from_variant(variantParent, name)

        return None

    def get_mesh_from_variant(self, root):
        return self.get_element_from_variant(root, 'mesh')

    def get_textures_from_variant(self, root):
        import xml.etree.ElementTree as ET
        child_textures = None
        for child in root:
            if child.tag == 'textures':
                child_textures = child

        if 'file' in root.attrib is not None:
            variantParent = ET.parse(self.currentPath + 'variants/' +  root.attrib['file']).getroot()
            parent_textures = self.get_textures_from_variant(variantParent)
            if parent_textures is not None and child_textures is not None:
                for texture in child_textures:
                    parent_textures.append(texture)
                return parent_textures
            else:
                return child_textures

        return child_textures

    def get_props_from_variant(self, root):
        import xml.etree.ElementTree as ET

        childProps = None
        for child in root:
            if child.tag == 'props':
                childProps = child
                break

        if 'file' in root.attrib is not None:
            variantParent = ET.parse(self.currentPath + 'variants/' +  root.attrib['file']).getroot()
            parentProps = self.get_props_from_variant(variantParent)
            if parentProps is not None and childProps is not None:
                for prop in childProps:
                    parentProps.append(prop)
                return parentProps
            else:
                return childProps

        return childProps

    def parse_actor(self, root, proppoint="root", parentprops=[], rootObj=None, propDepth=0):
        import xml.etree.ElementTree as ET
        import bpy
        import os
        import sys
        import random
        import math
        meshprops = []
        imported_props = []
        imported_textures = []
        imported_objects = []
        material_object = None
        rootObject = rootObj
        material_type = 'default.xml'
        for group in root:
            if group.tag == 'material':
                material_type = group.text

        if rootObj is not None:
            print("Root object is:" + rootObj.name)

        for group in root:
            if group.tag == 'material':
                continue

            if len(group) == 0:
                continue

            variant = group[0]
            # If there is more than one group pick one randomly.
            if len(group) > 1:
                retries = 0
                while (('frequency' not in variant.attrib or variant.attrib['frequency'] == "0") and retries < len(group)):
                    variant = group[random.randint(0,len(group) - 1)]
                    retries = retries + 1

            if 'file' in variant.attrib:
                present_tags = [child.tag for child in variant]

                variantParent = ET.parse(self.currentPath + 'variants/' +  variant.attrib['file']).getroot()
                if 'mesh' not in present_tags:
                    mesh_child = self.get_mesh_from_variant(variantParent)
                    if mesh_child is not None:
                        variant.append(mesh_child)

                variant_parent_textures = self.get_textures_from_variant(variantParent)
                if 'textures' not in present_tags:
                    if variant_parent_textures is not None:
                        variant.append(variant_parent_textures)
                elif variant_parent_textures is not None:
                    variant_tags = [texture.attrib['name'] for texture in variant.find('textures')]
                    for texture in variant_parent_textures:
                        if texture is not None and texture.attrib['name'] not in variant_tags:
                            variant.find('textures').append(texture)

                variant_parent_props = self.get_props_from_variant(variant)
                if 'props' not in present_tags and variant_parent_props is not None:
                    variant.append(variant_parent_props)
                elif variant_parent_props is not None:
                    variant_tags = [prop.attrib['attachpoint'] for prop in variant.find('props')]
                    for prop in variant_parent_props:
                        if prop is not None and prop.attrib['attachpoint'] not in variant_tags:
                            variant.find('props').append(prop)

            for child in variant:
                if(child.tag == 'mesh' or child.tag == 'decal'):
                    print("=======================================================")
                    print("============== Gathering Mesh =========================")
                    print("=======================================================")

                    # Get the objects prior to importing
                    prior_objects = [Object for Object in bpy.context.scene.objects]
                    prior_materials = [material for material in bpy.data.materials]
                    # Deselect all the previously selected objects.
                    for obj in prior_objects:
                        obj.select_set(False)
                    # Import the new objects
                    if child.tag == "mesh":
                        mesh_path = self.currentPath + 'meshes/' + child.text
                        try:

                            fixer = MaxColladaFixer(mesh_path)
                            fixer.execute()
                            bpy.ops.wm.collada_import(filepath=mesh_path, import_units=True)
                        except:
                            print('Could not load' + mesh_path)
                    else:
                        bpy.ops.object.select_all(action='DESELECT')
                        if material_type == 'default.xml' or 'terrain' in material_type:
                            material_type = 'basic_trans.xml'

                        decal = self.create_custom_mesh("Decal", float(child.attrib['offsetx']), float(child.attrib['offsetz']), 0, float(child.attrib['width']), float(child.attrib['depth']))
                        decal.select_set(True)
                        bpy.context.view_layer.objects.active = decal
                        bpy.ops.object.mode_set(mode='EDIT')
                        bpy.ops.uv.reset()
                        bpy.ops.object.mode_set(mode='OBJECT')
                        decal.rotation_euler = (0,0, math.radians(float(child.attrib['angle'])))

                    new_current_objects = [object for object in bpy.context.scene.objects]
                    # Select those objects
                    for obj in (set(new_current_objects) - set(prior_objects)):
                        obj.select_set(True)

                    backup =  bpy.context.selected_objects.copy()
                    for b in backup :
                        if b.data is None or b.data.uv_layers is None or not len(b.data.uv_layers):
                            continue
                        if len(b.data.uv_layers) > 0:
                            print("Renaming" + b.data.uv_layers[0].name + " to " + "UVMap")
                            b.data.uv_layers[0].name = "UVMap"

                        if len(b.data.uv_layers) > 1:
                            print("Renaming" + b.data.uv_layers[1].name + " to " + "AOMap")
                            b.data.uv_layers[1].name = "AOMap"

                    # Get those objects
                    imported_objects = bpy.context.selected_objects.copy()

                    for imported_object in imported_objects:
                        if imported_object is not None and 'prop.' in imported_object.name:
                            imported_object.name = imported_object.name.replace("prop.","prop_")
                            if imported_object.data is not None and 'prop.' in imported_object.data.name:
                                imported_object.data.name = imported_object.data.name.replace("prop.","prop_")
                        # props are parented so they should follow their root object.
                        if "prop-" in imported_object.name:
                            imported_object.name = imported_object.name.replace("prop-","prop_")
                            if imported_object.data is not None and 'prop-' in imported_object.data.name:
                                imported_object.data.name = imported_object.data.name.replace("prop-","prop_")
                        if "prop_" in imported_object.name:
                            meshprops.append(imported_object)
                            imported_object.select_set(False)

                    bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

                    for obj in backup:
                        obj.select_set(True)
                    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

                    # Clear old materials
                    for ob in bpy.context.selected_editable_objects:
                        ob.active_material_index = 0
                        for i in range(len(ob.material_slots)):
                            bpy.ops.object.material_slot_remove()

                    new_current_materials = [material for material in bpy.data.materials]
                    for material in (set(new_current_materials) - set(prior_materials)):
                        bpy.data.materials.remove(material)
                    print("=======================================================")
                    print("============== Setting Constraints ====================")
                    print("=======================================================")

                    for imported_object in imported_objects:
                        # props are parented so they should follow their root object.
                        if ('prop-' in imported_object.name or 'prop_' in imported_object.name) and hasattr(imported_object, 'type') and imported_object.type == 'EMPTY':
                            continue

                        if proppoint == 'root' and rootObj is not None:
                            self.set_copy_transform_constraint(imported_object, rootObj)
                            continue

                        found = False
                        for prop in parentprops:
                            if proppoint in prop.name:
                                found = True
                                self.set_copy_transform_constraint(imported_object, prop)
                                break

                        if found == True:
                            continue

                        print('' + imported_object.name + ' has no parent prop point named prop_' + proppoint + '. Root object name: ' + (rootObj.name if rootObj is not None else 'Undefined')  + '')

                if(child.tag == 'textures' and self.import_textures):
                    print("=======================================================")
                    print("============== Gathering Textures =====================")
                    print("=======================================================")
                    if(len(child) > 0):
                        for texture in child:
                            imported_textures.append(texture)

                if(child.tag == 'props' and self.import_props and (self.import_depth == -1 or (self.import_depth > propDepth and self.import_depth > 0))):

                    print("=======================================================")
                    print("============== Gathering Parent Props =================")
                    print("=======================================================")

                    finalprops = imported_objects.copy()
                    if len(finalprops) > 0:
                        rootObject = None
                        for obj in finalprops:
                            if ('prop-' in obj.name or 'prop_' in obj.name):
                                continue

                            if hasattr(obj, 'type') and obj.type == 'ARMATURE':
                                print("=======================================================")
                                print("============== Gathering Armature Props ===============")
                                print("=======================================================")

                                for bone in obj.data.bones:
                                    if 'prop.' in bone.name:
                                        bone.name = bone.name.replace('prop.','prop_')
                                    if 'prop-' in bone.name:
                                        bone.name = bone.name.replace('prop-','prop_')
                                    if 'prop_' in bone.name:
                                        print(bone.name)
                                        finalprops.append(bone)

                                continue
                            if hasattr(obj, 'type'):
                                rootObject = bpy.data.objects[obj.name]
                            finalprops.remove(obj)

                    if rootObject is not None:
                        print(rootObject.name)

                    for prop in child:
                        imported_props.append(prop)

        mat_textures = []
        for texture in imported_textures:
            print("Loading " + texture.attrib['name'] + ": " + self.currentPath + 'textures/skins/' + texture.attrib['file'])
            bpy.data.images.load(self.currentPath + 'textures/skins/' + texture.attrib['file'], check_existing=True)
            mat_textures.append(texture.attrib['name'] + '|' + self.currentPath + 'textures/skins/' + texture.attrib['file'])

        if len(mat_textures):
            material_object = self.create_new_material(mat_textures, material_type)

            for obj in imported_objects:
                if ('prop-' in obj.name or 'prop_' in obj.name) and not hasattr(obj, 'type'):
                    continue
                if hasattr(obj, 'type') and obj.type == 'EMPTY':
                    continue
                if hasattr(obj, 'type') and obj.type == 'ARMATURE':
                    continue

                self.assign_material_to_object(obj, material_object)

        for prop in imported_props:
            print("=======================================================")
            print("============== Gathering Props ========================")
            print("=======================================================")
            if prop.attrib['actor'] == "":
                continue

            try:
                prop_path = self.currentPath + 'actors/' +  prop.attrib['actor']
                print('Loading ' + prop_path + '.')
                proproot = ET.parse(prop_path).getroot()

                propRootObj = self.find_prop_root_object(finalprops, prop.attrib['attachpoint'])
                if propRootObj is not None and prop.attrib['attachpoint'] != 'root' and rootObject is None:
                    rootObject = propRootObj

                self.parse_actor(proproot, prop.attrib['attachpoint'], meshprops if finalprops is None or len(finalprops) <= 0 else finalprops, rootObject, propDepth + 1)
            except:
                print('Could not load' + mesh_path)

    def find_prop_root_object(self, imported_objects, proppoint):
        for imported_object in imported_objects:
            if 'prop_' + proppoint in imported_object.name:
                return imported_object

        return None
class MaxColladaFixer:
    file_path = None
    collada_prefix = '{http://www.collada.org/2005/11/COLLADASchema}'

    def sortchildrenby(self, parent):
        parent[:] = sorted(parent, key=lambda child: child.tag)

    def indent(self, elem, level=0):
        i = "\n" + level*"  "
        if len(elem):
            if not elem.text or not elem.text.strip():
                elem.text = i + "  "
            if not elem.tail or not elem.tail.strip():
                elem.tail = i
            for elem in elem:
                self.indent(elem, level+1)
            if not elem.tail or not elem.tail.strip():
                elem.tail = i
        else:
            if level and (not elem.tail or not elem.tail.strip()):
                elem.tail = i

    def __init__(self, file_path=None):
        self.file_path = file_path

    def execute(self):
        import xml.etree.ElementTree as ET
        from datetime import date

        tree = ET.parse(self.file_path)
        ET.register_namespace("", "http://www.collada.org/2005/11/COLLADASchema")
        root = tree.getroot()
        new_elements = []
        for child in root:
            print("Fixing Collada..." + child.tag)
        for child in root[:]:
            if child.tag == self.collada_prefix + 'library_images':
                root.remove(child)
                element = ET.Element(self.collada_prefix + 'library_images')
                new_elements.append(element)
                continue

            if child.tag == self.collada_prefix + 'library_materials':
                root.remove(child)

                # If there is no material no need to append it.
                continue

            if child.tag == self.collada_prefix + 'library_effects':
                root.remove(child)

                element = ET.Element(self.collada_prefix + 'library_effects')
                new_elements.append(element)
                continue

            if child.tag == self.collada_prefix + 'library_visual_scenes':
                for visual_scene in child:
                    for node in visual_scene:
                        for subchild in node:
                            if subchild.tag == self.collada_prefix + 'instance_geometry':
                                for binding in subchild:
                                    if binding.tag == self.collada_prefix + 'bind_material':
                                        subchild.remove(binding)
                                        break
                            if subchild.tag == self.collada_prefix + 'instance_controller':
                                for binding in subchild:
                                    if binding.tag == self.collada_prefix + 'bind_material':
                                        subchild.remove(binding)
                                        break
                continue

            if child.tag == self.collada_prefix + 'asset':
                for property in child:
                    if property.tag == self.collada_prefix + 'modified':
                        property.text = str(date.today())

        for element in new_elements:
            root.append(element)

        self.sortchildrenby(root)
        for child in root:
            self.sortchildrenby(child)
        self.indent(root)
        tree.write(open(self.file_path, 'wb'), encoding='utf-8')
        tree.write(open(self.file_path, 'wb'),encoding='utf-8')

if __name__ == '__main__':
    register()


# Import the example classes

import fire
import numpy as np
# Useful imports
# from example_save_trials import *
import tensorflow as tf
# Import the necessary garage classes
# from garage.baselines.linear_feature_baseline import LinearFeatureBaseline
# from garage.misc import logger
from garage.experiment import run_experiment
from garage.tf.experiment import LocalTFRunner

import ast_toolbox.mcts.BoundedPriorityQueues as BPQ
from ast_toolbox.algos import MCTS
from ast_toolbox.algos import MCTSBV
from ast_toolbox.algos import MCTSRS
# Import the AST classes
from ast_toolbox.envs import ASTEnv
from ast_toolbox.rewards import ExampleAVReward
from ast_toolbox.samplers import ASTVectorizedSampler
from ast_toolbox.simulators import ExampleAVSimulator
from ast_toolbox.spaces import ExampleAVSpaces
from ast_toolbox.utils.go_explore_utils import load_convert_and_save_mcts_expert_trajectory
import pickle

def runner(
    mcts_type=None,
    env_args=None,
    run_experiment_args=None,
    sim_args=None,
    reward_args=None,
    spaces_args=None,
    algo_args=None,
    runner_args=None,
    bpq_args=None,
    sampler_args=None,
    save_expert_trajectory=False,
    # log_dir='.',
):
    if mcts_type is None:
        mcts_type = 'mcts'

    if env_args is None:
        env_args = {}

    if run_experiment_args is None:
        run_experiment_args = {}

    if sim_args is None:
        sim_args = {}

    if reward_args is None:
        reward_args = {}

    if spaces_args is None:
        spaces_args = {}

    if algo_args is None:
        algo_args = {}

    if runner_args is None:
        runner_args = {}

    if sampler_args is None:
        sampler_args = {}

    if bpq_args is None:
        bpq_args = {}

    if 'n_parallel' in run_experiment_args:
        n_parallel = run_experiment_args['n_parallel']
    else:
        n_parallel = 1
        run_experiment_args['n_parallel'] = n_parallel

    if 'max_path_length' in sim_args:
        max_path_length = sim_args['max_path_length']
    else:
        max_path_length = 50
        sim_args['max_path_length'] = max_path_length

    if 'batch_size' in runner_args:
        batch_size = runner_args['batch_size']
    else:
        batch_size = max_path_length * n_parallel
        runner_args['batch_size'] = batch_size

    def run_task(snapshot_config, *_):

        seed = 0
        # top_k = 10
        np.random.seed(seed)

        config = tf.ConfigProto()
        config.gpu_options.allow_growth = True
        with tf.Session(config=config) as sess:
            with tf.variable_scope('AST', reuse=tf.AUTO_REUSE):

                with LocalTFRunner(
                        snapshot_config=snapshot_config, max_cpus=4, sess=sess) as local_runner:

                    # Instantiate the example classes
                    sim = ExampleAVSimulator(**sim_args)
                    reward_function = ExampleAVReward(**reward_args)
                    spaces = ExampleAVSpaces(**spaces_args)

                    # Create the environment
                    if 'id' in env_args:
                        env_args.pop('id')
                    env = ASTEnv(simulator=sim,
                                 reward_function=reward_function,
                                 spaces=spaces,
                                 **env_args
                                 )

                    top_paths = BPQ.BoundedPriorityQueue(**bpq_args)

                    if mcts_type == 'mcts':
                        print('mcts')
                        algo = MCTS(env=env,
                                    top_paths=top_paths,
                                    **algo_args)
                    elif mcts_type == 'mctsbv':
                        print('mctsbv')
                        algo = MCTSBV(env=env,
                                      top_paths=top_paths,
                                      **algo_args)
                    elif mcts_type == 'mctsrs':
                        print('mctsrs')
                        algo = MCTSRS(env=env,
                                      top_paths=top_paths,
                                      **algo_args)
                    else:
                        raise NotImplementedError

                    sampler_cls = ASTVectorizedSampler
                    sampler_args['sim'] = sim
                    sampler_args['reward_function'] = reward_function

                    local_runner.setup(algo=algo,
                                       env=env,
                                       sampler_cls=sampler_cls,
                                       sampler_args=sampler_args)

                    # Run the experiment
                    local_runner.train(**runner_args)

                    log_dir = run_experiment_args['log_dir']

                    if save_expert_trajectory:
                        load_convert_and_save_mcts_expert_trajectory(
                            best_actions_filename=log_dir + '/best_actions.p',
                            expert_trajectory_filename=log_dir + '/expert_trajectory.p',
                            sim=sim,
                            s_0=env_args['s_0'],
                            reward_function=reward_function)
                    objects = []
                    with (open(log_dir + '/expert_trajectory.p', "rb")) as openfile:
                        while True:
                            try:
                                objects.append(pickle.load(openfile))
                            except EOFError:
                                break
                    #print(objects)
    run_experiment(
        run_task,
        **run_experiment_args,
    )

if __name__ == '__main__':
    fire.Fire()


def bm_mine(str, pat):

    skip = [len(pat)] * 256

    # for pt in range(256):
        # skit[pt] = len(pat)         # skip의 문자들을 patter의 크기로 다 맞춘다.
    for pt in range(len(pat)):
        # skip에서 pat 속에 있는 문자들을
        print(pat[pt])
        print(ord(pat[pt]), len(pat) - pt - 1)
        skip[ord(pat[pt])] = len(pat) - pt - 1

    print(skip)

bm_mine('ABCXDEZCABACABAC', 'ABAC')


# function that returns reversed string using recursion
def reverseString(string):
    if len(string) < 1:
        return string
    else:
        foo = reverseString(string[1:]) + string[0]
        return foo

# function that prints string in reverse starting from rightmost character
def rec_string(string):
    if len(string) < 1:
        return string
    else:
        foo = rec_string(string[1:]) + string[0]
        print(string[0:])
        return foo

def main():
    # reverse function
    print(reverseString("hello world"))
    print('\n')

    # rec_string function
    rec_string('abcde')
    print('\n')
    rec_string('abc')

if __name__ == "__main__":
    main()


# 获取执行后的结果

from s6.result import AsyncResult
from .tasks import my_task

# 异步获取任务返回值
async_task = AsyncResult(id="4bc32858-9d13-440b-a2f1-20845677593f",app=my_task)

# 判断异步任务是否执行成功
if async_task.successful():
    #获取异步任务的返回值
    result = async_task.get()
    print(result)
else:
    print("任务还未执行完成")


import os
import sys
import subprocess
sys.path.append(snakemake.config['args']['mcc_path'])
import scripts.mccutils as mccutils
import config.relocate.relocate_post as config

class Insertion:
    def __init__(self):
        self.chromosome = "None"
        self.start = -1
        self.end = -1
        self.name = "None"
        self.type = "None"
        self.left_support = -1
        self.right_support = -1
        self.strand = "."

def main():
    relocate_gff = snakemake.input.relocate_gff
    te_gff = snakemake.input.te_gff

    out_dir = snakemake.params.out_dir
    log = snakemake.params.log
    sample_name = snakemake.params.sample_name
    chromosomes = snakemake.params.chromosomes.split(",")

    mccutils.log("relocate","processing RelocaTE results")

    insertions = get_insertions(relocate_gff, sample_name, chromosomes, ref_l_threshold=config.REF_LEFT_THRESHOLD, ref_r_threshold=config.REF_RIGHT_THRESHOLD, nonref_l_threshold=config.NONREF_LEFT_THRESHOLD, nonref_r_threshold=config.NONREF_RIGHT_THRESHOLD)

    insertions = set_ref_orientations(insertions, te_gff)

    if len(insertions) >= 1:
        insertions = make_redundant_bed(insertions, sample_name, out_dir)
        make_nonredundant_bed(insertions, sample_name, out_dir)
    else:
        mccutils.run_command(["touch",out_dir+"/"+sample_name+"_relocate_redundant.bed"])
        mccutils.run_command(["touch",out_dir+"/"+sample_name+"_relocate_nonredundant.bed"])

    mccutils.log("relocate","RelocaTE postprocessing complete")

def get_insertions(gff, sample_name, chromosomes, ref_l_threshold=0, ref_r_threshold=0, nonref_l_threshold=0, nonref_r_threshold=0):
    insertions = []
    with open(gff, "r") as ingff:
        for line in ingff:
            if "#" not in line:
                split_line = line.split("\t")
                feats = split_line[8].split(";")
                insert = Insertion()
                insert.chromosome = split_line[0]
                insert.start = int(split_line[3])
                insert.end = int(split_line[4])
                insert.strand = split_line[6]

                feat_id = ""
                feat_te_name = ""
                for feat in feats:
                    if "ID=" in feat:
                        feat_id = feat.split("=")[1]
                    elif "TE_Name=" in feat:
                        feat_te_name = feat.split("=")[1]
                    elif "Note=" in feat:
                        if "Shared" in feat:
                            insert.type = "ref"
                        elif "Non-reference" in feat:
                            insert.type = "nonref"
                        else:
                            insert.type = "missing"

                    elif "left_flanking_read_count=" in feat:
                        insert.left_support = int(feat.split("=")[1])

                    elif "right_flanking_read_count=" in feat:
                        insert.right_support = int(feat.split("=")[1])

                if insert.type == "ref":
                    insert.name = feat_te_name+"_reference_"+sample_name+"_relocate_sr_"
                elif insert.type == "nonref":
                    feat_te_name = feat_id.split(".")[0]
                    insert.name = feat_te_name+"_non-reference_"+sample_name+"_relocate_sr_"

            if insert.type == "ref" and insert.left_support >= ref_l_threshold and insert.right_support >= ref_r_threshold and insert.chromosome in chromosomes:
                insertions.append(insert)
            elif insert.type == "nonref" and insert.left_support >= nonref_l_threshold and insert.right_support >= nonref_r_threshold and insert.chromosome in chromosomes:
                insertions.append(insert)

    return insertions

def set_ref_orientations(insertions, te_gff):
    ref_strands = {}
    out_inserts = []
    with open(te_gff, "r") as gff:
        for line in gff:
            if "#" not in line:
                split_line = line.split("\t")
                chrom = split_line[0]
                start = split_line[3]
                end = split_line[4]
                strand = split_line[6]
                ref_strands["_".join([chrom, start, end])] = strand

    for insert in insertions:
        if insert.type == "ref":
            insert.strand = ref_strands["_".join([insert.chromosome, str(insert.start), str(insert.end)])]

        out_inserts.append(insert)

    return out_inserts

def make_redundant_bed(insertions, sample_name, out_dir):
    tmp_bed = out_dir+"/tmp.bed"

    insertion_dict = {}
    out_inserts = []
    for insert in insertions:
        insertion_dict[ "_".join([insert.chromosome, str(insert.start-1), str(insert.end), insert.name, "0", insert.strand])] = insert

    with open(tmp_bed, "w") as out:
        for insert in insertions:
            out_line = "\t".join([insert.chromosome, str(insert.start-1), str(insert.end), insert.name, "0", insert.strand])
            out.write(out_line+"\n")

    sorted_bed = out_dir+"/sorted.bed"
    command = ["bedtools", "sort", "-i", tmp_bed]
    mccutils.run_command_stdout(command, sorted_bed)

    redundant_bed = out_dir+"/"+sample_name+"_relocate_redundant.bed"
    with open(redundant_bed, "w") as outbed:
        header = 'track name="'+sample_name+'_RelocaTE" description="'+sample_name+'_RelocaTE"\n'
        outbed.write(header)
        with open(sorted_bed, "r") as inbed:
            for x, line in enumerate(inbed):

                # outputs inserts in sorted order with unique number added to name
                key = line.replace("\t","_")
                key = key.replace("\n","")
                insert = insertion_dict[key]
                insert.name += str(x+1)
                out_inserts.append(insert)

                # write to bed with unique number added to name
                split_line = line.split("\t")
                split_line[3] += str(x+1)
                line = "\t".join(split_line)
                outbed.write(line)

    mccutils.remove(tmp_bed)
    mccutils.remove(sorted_bed)

    return out_inserts

def make_nonredundant_bed(insertions, sample_name, out_dir):
    uniq_inserts = {}

    for insert in insertions:
        key = "_".join([insert.chromosome, str(insert.end)])
        if key not in uniq_inserts.keys():
            uniq_inserts[key] = insert
        else:
            if (uniq_inserts[key].left_support + uniq_inserts[key].right_support) < (insert.left_support + insert.right_support):
                uniq_inserts[key] = insert

    tmp_bed = out_dir+"/tmp.bed"
    with open(tmp_bed, "w") as outbed:
        for key in uniq_inserts.keys():
            insert = uniq_inserts[key]
            out_line = "\t".join([insert.chromosome, str(insert.start-1), str(insert.end), insert.name, "0", insert.strand])
            outbed.write(out_line+"\n")

    sorted_bed = out_dir+"/sorted.bed"
    command = ["bedtools", "sort", "-i", tmp_bed]
    mccutils.run_command_stdout(command, sorted_bed)

    nonredundant_bed = out_dir+"/"+sample_name+"_relocate_nonredundant.bed"
    with open(sorted_bed, "r") as inbed:
        with open(nonredundant_bed, "w") as outbed:
            header = 'track name="'+sample_name+'_RelocaTE" description="'+sample_name+'_RelocaTE"\n'
            outbed.write(header)
            for line in inbed:
                outbed.write(line)

    mccutils.remove(tmp_bed)
    mccutils.remove(sorted_bed)

if __name__ == "__main__":
    main()


# -*- coding: utf-8 -*-
"""
IAMSI - TME4
Programmation SAT - Générateur de championnat
2022-2023

@author: @dataymeric
"""

def encodage(ne, nj, j, x, y):
    """Code une variable m_{j, x, y} par k.

    Parameters
    ----------
    ne : int
        Nombre d'équipes.
    nj : int
        Nombre de jours.
    j : int
        Jour.
    x : int
        Equipe jouant à domicile.
    y : int
        Equipe jouant à l'extérieur.

    Returns
    -------
    k : int
        Variable encodée.

    Examples
    --------
    >>> encodage(3, 4, 1, 0, 1)
    11
    """
    return j * ne**2 + x * ne + y + 1

def decodage(k, ne):
    """Retrouve j, x et y à partir de k, ne.

    Parameters
    ----------
    k : int
        Variable encodée.
    ne : int
        Nombre d'équipes.

    Returns
    -------
    j : int
        Jour.
    x : int
        Equipe jouant à domicile.
    y : int
        Equipe jouant à l'extérieur.

    Examples
    --------
    >>> decodage(11, 3)
    (1, 0, 1)
    """
    y = (k - 1) % ne
    x = ((k - 1 - y) // ne) % ne
    j = (k - 1 - y - x * ne) // (ne * ne)
    return j, x, y

def au_moins_un(variables):
    """Renvoie une clause (au format DIMACS) correspondant à la contrainte
    "au moins une de ces variables est vraie".

    Parameters
    ----------
    variables : liste d'int
        Variables à encoder sous la contrainte de cardinalité "au moins 1".

    Returns
    -------
    clause : liste de str de taille (1,)
        Clause encodée sous la contrainte de cardinalité "au moins 1".

    Examples
    --------
    >>> variables = [1, 2, 3]
    >>> au_moins_un(variables)
    ['1 2 3 0']
    """
    clause = [str(v) for v in variables] + ["0"]
    return [" ".join(clause)]

def au_plus_un(variables):
    """Renvoie une clause (au format DIMACS) correspondant à la contrainte
    "au plus une de ces variables est vraie".

    Parameters
    ----------
    variables : liste d'int
        Variables à encoder sous la contrainte de cardinalité "au plus 1".

    Returns
    -------
    clauses : liste de str
        Clauses encodées sous la contrainte de cardinalité "au plus 1".

    Examples
    --------
    >>> variables = [1, 2, 3]
    >>> au_plus_un(variables)
    ['-1 -2 0', '-1 -3 0', '-2 -3 0']
    """
    clauses = []
    n = len(variables)
    for i in range(n):
        for j in range(i + 1, n):
            clauses.append(f"{-variables[i]} {-variables[j]} 0")
    return clauses

def au_plus_k(variables, k):
    """Renvoie une clause (au format DIMACS) correspondant à la contrainte
    "au plus k de ces variables sont vraies".

    Parameters
    ----------
    variables : liste d'int
        Variables à encoder.
    k : int
        Limite.

    Returns
    -------
    clauses : liste de str
        Clauses encodées.

    Examples
    --------
    >>> variables = [1, 2, 3]
    >>> au_plus_k(variables, 2)
    ['-1 -2 -3 0']

    On peut vérifier que cette contrainte est satisfaite lorsque 1 ou 2 (mais
    pas 3) variables sont vraies (au plus 2).
    """
    from itertools import combinations

    clauses = []
    n = len(variables)
    for i in range(k + 1, n + 1):
        for c in combinations(variables, i):
            clause = [str(-x) for x in c] + ["0"]
            clauses.append(" ".join(clause))
    return clauses

def au_moins_k(variables, k):
    """Renvoie une clause (au format DIMACS) correspondant à la contrainte
    "au moins k de ces variables sont vraies".

    Parameters
    ----------
    variables : liste d'int
        Variables à encoder.
    k : int
        Limite.

    Returns
    -------
    liste de str
        Clauses encodées.

    Examples
    --------
    >>> variables = [1, 2, 3]
    >>> au_moins_k(variables, 2)
    ['1 2 0', '1 3 0', '2 3 0', '1 2 3 0']

    On peut vérifier que ces contraintes de cardinalités ne sont satisfaites
    que si 2 ou 3 (mais pas 1) variables sont vraies (au moins 2).
    """
    n = len(variables)
    neg_variables = [-x for x in variables]
    return au_plus_k(neg_variables, n - k)

def encoderC1(ne, nj):
    """Renvoie des contraintes (au format DIMACS) correspondant à la contrainte
    C1 : "chaque équipe ne peut jouer plus d'un match par jour".

    Indication : pour 3 équipes et 4 jours, cela génère 72 contraintes.

    Parameters
    ----------
    ne : int
        Nombre d'équipes.
    nj : int
        Nombre de jours.

    Returns
    -------
    clauses : liste de str
        Contraintes générées.
    """
    clauses = []
    for j in range(nj):
        for x in range(ne):
            # Contrainte "au plus 1" pour chaque équipe x et chaque jour j
            domicile = [encodage(ne, nj, j, x, y) for y in range(ne) if y != x]
            exterieur = [encodage(ne, nj, j, y, x) for y in range(ne) if y != x]
            clauses.extend(au_plus_un(domicile + exterieur))
    return clauses

def encoderC2(ne, nj):
    """Renvoie des contraintes (au format DIMACS) correspondant à la contrainte
    C2 : "sur la durée d'un championnat, chaque équipe doit rencontrer l'ensemble
    des autres équipes une fois à domicile et une fois à l'extérieur, soit
    exactement 2 matchs par équipe adverse".

    Indication : pour 3 équipes et 4 jours, cela génère 42 contraintes.

    Parameters
    ----------
    ne : int
        Nombre d'équipes.
    nj : int
        Nombre de jours.

    Returns
    -------
    clauses : liste de str
        Contraintes générées.
    """
    clauses = []
    for x in range(ne):
        for y in range(ne):
            if x != y:
                list_match = [encodage(ne, nj, j, x, y) for j in range(nj)]
                clauses.extend(au_moins_un(list_match))
                clauses.extend(au_plus_un(list_match))
    return clauses

def encoderC3(ne, nj, pext=50, pdom=40):
    """Renvoie des contraintes (au format DIMACS) correspondant aux contraintes
    des matchs le dimanche.
    On considère que le dimanche est le deuxième jour de la semaine (chiffres impairs).

    Indication : pour 3 équipes et 4 jours, cela génère 24 contraintes.

    Parameters
    ----------
    ne : int
        Nombre d'équipes.
    nj : int
        Nombre de jours.
    pext : int, default=50
        Pourcentage de matchs à l'extérieur.
    pdom : int, default=40
        Pourcentage de matchs à domicile.

    Returns
    -------
    clauses : liste de str
        Contraintes générées.
    """
    kext = int((ne - 1) * 2 * pext / 100)
    kdom = int((ne - 1) * 2 * pdom / 100)
    clauses = []
    for x in range(ne):
        for y in range(ne):
            if x != y:
                # tous les matchs à domicile joués le dimanche
                domicile = [encodage(ne, nj, j, x, y) for j in range(1, nj, 2)]
                clauses.extend(au_moins_k(domicile, kdom))
                # tous les matchs à l'extérieur joués le dimanche
                exterieur = [encodage(ne, nj, j, y, x) for j in range(1, nj, 2)]
                clauses.extend(au_moins_k(exterieur, kext))
    return clauses

def encoderC4(ne, nj):
    """Renvoie des contraintes (au format DIMACS) correspondant aux contraintes
    des matchs consécutifs.

    Note : cette implémentation est fausse, n'ayant pas bien compris comment récupérer
    la notion de matchs consécutifs.

    Parameters
    ----------
    ne : int
        Nombre d'équipes.
    nj : int
        Nombre de jours.

    Returns
    -------
    clauses : liste de str
        Contraintes générées.
    """
    clauses = []
    for j in range(nj):
        for x in range(ne):
            domicile = [encodage(ne, nj, j, x, y) for y in range(ne) if y != x]
            clauses.extend(au_plus_k(domicile, 2))
            exterieur = [encodage(ne, nj, j, y, x) for y in range(ne) if y != x]
            clauses.extend(au_plus_k(exterieur, 2))
    return clauses

def encoder(ne, nj, extension=False):
    """Encode toutes les contraintes C1 et C2 dans un fichier 'championnat.cnf'.

    Parameters
    ----------
    ne : int
        Nombre d'équipes.
    nj : int
        Nombre de jours.
    extension : bool, optional
        Rajoute les contraintes afin d'étendre le problème avec l'équilibrage
        des déplacements et des week-ends.

    Returns
    -------
    contraintes : liste de str
        Contraintes C1 et C2 (avec C3 et C4 si extension).
    """
    contraintes = encoderC1(ne, nj) + encoderC2(ne, nj)
    if extension:
        contraintes.extend(encoderC3(ne, nj) + encoderC4(ne, nj))
    with open("championnat.cnf", "w") as f:
        # Ecrire l'en-tête du fichier DIMACS
        f.write(f"p cnf {ne**2 * nj - 1} {len(contraintes)}\n")
        # Ecrire les contraintes
        for contrainte in contraintes:
            f.write(contrainte + "\n")
    return contraintes

def decoder(output_file, ne, nj, team_names_file=None):
    """Traduit le modèle en un planning. Si le modèle est insatisfiable, renvoie
    None. Sinon, renvoie un dictionnaire.

    Parameters
    ----------
    output_file : str
        Nom du fichier généré par glucose.
    ne : int
        Nombre d'équipes.
    nj : int
        Nombre de jours.
    team_names_file : str, optional
        Nom du fichier contenant le nom des équipes.

    Returns
    -------
    planning : dict
        Planning du championnat.
        Les clés correspondent aux jours (0, nj - 1).
        Les items correspondent aux équipes devant jouer ce jour-là, sous la forme
        d'un dictionnaire et de listes (car plusieurs matchs peuvent avoir lieu
        le même jour !) {"Domicile": [x_1, x_2, ...], "Extérieur": [y_1, y_2, ...]}.
    """
    # Ouvrir le fichier contenant les noms d'équipe
    if team_names_file is not None:
        with open(team_names_file, "r") as f:
            team_names = [line.strip() for line in f]
        assert len(team_names) == ne, "Le nombre d'équipes ne correspondent pas."

    # Ouvrir le fichier de sortie de glucose
    with open(output_file, "r") as f:
        output_lines = f.readlines()

    # Extraire le modèle s'il existe
    model = output_lines[0].split()

    # Si aucun modèle n'a été trouvé, il n'y a pas de solution
    if "UNSAT" in model:
        return None

    # Cas SAT : initialisation du planning sous forme de dictionnaire
    planning = {j: {"Domicile": [], "Extérieur": []} for j in range(nj)}

    for literal in model:
        literal = int(literal)
        if literal > 0:
            j, x, y = decodage(literal, ne)
            if team_names_file is not None:
                x = team_names[x]
                y = team_names[y]
            planning[j]["Domicile"].append(x)
            planning[j]["Extérieur"].append(y)

    return planning

def affichage(planning):
    """Affiche un planning de matchs sous la forme d'un tableau ASCII dans la console.

    Parameters
    ----------
    planning : dict
        Planning des matchs.

    Returns
    -------
    None (stdout)
        Affichage console.
    """
    if planning is None:
        print("Le problème posé n'a pas de solutions. :(")
    else:
        # Détermine la largeur maximale de chaque colonne
        column_lengths = {"Jour": len("Jour")}
        for row in planning.values():
            for column, values in row.items():
                if values:  # cas liste vide
                    column_lengths[column] = max(
                        column_lengths.get(column, 0),
                        len(column),
                        max(len(str(value)) for value in values),
                    )
                else:
                    column_lengths[column] = max(
                        column_lengths.get(column, 0), len(column)
                    )

        # Calcule la largeur totale de la table
        total_width = sum(column_lengths.values()) + len(column_lengths) * 3 + 1

        # Affiche le titre
        title = "Planning des matchs"
        print(f"\n{title:^{total_width}}")

        # Affiche la bordure supérieure du tableau
        columns = sorted(planning[0].keys())
        headers = ["Jour"] + columns
        print(
            "+"
            + "+".join("-" * (column_lengths[column] + 2) for column in headers)
            + "+"
        )

        # Affiche le header
        print(
            "| "
            + " | ".join(f"{header:^{column_lengths[header]}}" for header in headers)
            + " |"
        )

        # Affiche le séparateur du header
        print(
            "+"
            + "+".join("-" * (column_lengths[column] + 2) for column in headers)
            + "+"
        )

        # Affiche chaque ligne
        for row_name, row in planning.items():
            row_length = max(len(value) for value in row.values())
            for i in range(row_length):
                row_values = [str(row_name)]
                for column in columns:
                    if i < len(row[column]):
                        value = row[column][i]
                        row_values.append(str(value))
                    else:
                        row_values.append("")
                print(
                    "| "
                    + " | ".join(
                        f"{value:^{column_lengths[column]}}"
                        for column, value in zip(headers, row_values)
                    )
                    + " |"
                )

        # Affiche la bordure inférieure du tableau
        print(
            "+"
            + "+".join("-" * (column_lengths[column] + 2) for column in headers)
            + "+"
        )

def glucose():
    """Appel le solveur SAT glucose. Considére qu'il fait parti de l'environnement
    (e.g. /usr/local/bin/, ne marchera pas sous Windows). Travaille dans le répertoire
    courant, à partir des fichiers générés à l'aide de la fonction `encoder`.
    Enregistre les résultats dans un fichier `planning.cnf`.
    """
    import subprocess

    cmd = "glucose championnat.cnf planning.cnf"
    subprocess.run(cmd, shell=True, stdout=subprocess.DEVNULL)

def optimisation(ne, nj_min, nj_max, timeout=10):
    """Trouve le nombre de jours minimal pour pouvoir planifier tous les matchs
    de championnat pour un nombre d'équipes donné. Utilise des appels SAT (glucose) de
    façon itérative. Si le solveur met plus de 10 secondes à traiter le problème,
    on passe à une itération suivante. Si la fonction ne renvoie rien, c'est que
    le problème est trop lourd pour être trouvé en 10 secondes (une solution existe
    forcément dans notre problème). Le programme dure au maximum (nj_max - nj_min) * 10
    secondes.

    Parameters
    ----------
    ne : int
        Nombre d'équipes.
    nj_min : int
        Borne minimale de la recherche.
    nj_max : int
        Borne maximale de la recherche.
    timeout : int, default=10
        Valeur du timeout (en secondes).

    Returns
    -------
    j : int
        Nombre de jours optimal.

    Examples
    --------
    >>> optimisation(3, 3, 10)
    6

    >>> optimisation(10, 10, 20)
    18

    >>> optimisation(20, 20, 40)
    None

    Le timeout est trop bas pour permettre à glucose de résoudre le problème avec ne=20.
    """
    import multiprocessing

    for j in range(nj_min, nj_max + 1):
        encoder(ne, j)
        p = multiprocessing.Process(target=glucose)
        p.start()
        p.join(timeout)  # on attend 10 secondes
        if p.is_alive():  # si le processus est toujours vivant après 10 secondes
            print("timeout atteint...")
            p.terminate()  # on le termine
            continue  # on passe à l'itération suivante
        # sinon, c'est qu'il a fini : on regarde si le modèle est satisfiable, et si
        # c'est le cas, on renvoie j
        if decoder("planning.cnf", ne, j) is not None:
            return j

if __name__ == "__main__":
    """Programme principal. Permet à l'utilisateur de saisir le nombre d'équipes,
    le nombre de jours, préciser s'il y a un fichier texte contenant le nom des équipes,
    avec la possibilité de nettoyer les fichiers générés par glucose. Affiche le
    planning s'il y a une solution au problème.
    """
    import os

    # Saisie
    ne = int(input("Saisir le nombre d'équipes : "))
    nj = int(input("Saisir le nombre de jours : "))
    teams = input(
        "Nom du fichier donnant le nom des équipes "
        + "(appuyer sur la touche 'enter' pour passer ou s'il n'y en a pas) : "
    )
    janitor = input(
        "Voulez vous nettoyer les fichiers de travail à la fin du programme ?"
        + "(o/oui, appuyer sur la touche 'enter' pour passer) "
    )

    # Checks
    if teams == "":
        teams = None
    elif not os.path.isfile(teams):
        raise ValueError(f"Le fichier '{teams}' n'existe pas.")

    # Programme
    encoder(ne, nj)  # Encodage du problème
    glucose()  # Résolution grâce à glucose
    planning = decoder("planning.cnf", ne, nj, teams)  # Décodage le résultat
    affichage(planning)  # Affichage du résultat sous la forme d'un tableau

    # Nettoyage des fichiers de travail (si l'utilisateur l'a spécifié)
    if janitor.lower() in ("o", "oui"):
        os.remove("planning.cnf")
        os.remove("championnat.cnf")

"""---- Résultats ----
Pour ne=3, nj=4, le problème n'est pas satisfiable. Par optimisation, il faut au minimum
6 jours pour organiser un championnat avec 3 équipes.

Pour ne=3, nj=6, et le nom des équipes étant stockées dans 'equipes.txt' on
obtient le résultat suivant à l'aide de glucose et du programme python :

                Planning des matchs
+------+---------------------+---------------------+
| Jour |      Domicile       |      Extérieur      |
+------+---------------------+---------------------+
|  0   |    Saint-Etienne    | Paris Saint-Germain |
|  1   | Paris Saint-Germain |    Saint-Etienne    |
|  2   | Paris Saint-Germain |       Monaco        |
|  3   |    Saint-Etienne    |       Monaco        |
|  4   |       Monaco        | Paris Saint-Germain |
|  5   |       Monaco        |    Saint-Etienne    |
+------+---------------------+---------------------+

Que les meilleurs gagnent !

Pour ne=8, nj=14 (valeur trouvée par optimisation), on a 4 matchs par jour (soit 56
matchs en tout) :

                   Planning des matchs
+------+------------------------+------------------------+
| Jour |        Domicile        |       Extérieur        |
+------+------------------------+------------------------+
|  0   |         Monaco         |        RC Lens         |
|  0   | Olympique de Marseille |     Saint-Etienne      |
|  0   |   Olympique Lyonnais   |       FC Nantes        |
|  0   |       LOSC Lille       |  Paris Saint-Germain   |
|  1   |  Paris Saint-Germain   |        RC Lens         |
|  1   |     Saint-Etienne      |       FC Nantes        |
|  1   |         Monaco         | Olympique de Marseille |
|  1   |   Olympique Lyonnais   |       LOSC Lille       |
|  2   |  Paris Saint-Germain   | Olympique de Marseille |
|  2   |     Saint-Etienne      |        RC Lens         |
|  2   |   Olympique Lyonnais   |         Monaco         |
|  2   |       LOSC Lille       |       FC Nantes        |
|  3   |         Monaco         |   Olympique Lyonnais   |
|  3   |       FC Nantes        |     Saint-Etienne      |
|  3   |        RC Lens         |  Paris Saint-Germain   |
|  3   |       LOSC Lille       | Olympique de Marseille |
|  4   |     Saint-Etienne      |  Paris Saint-Germain   |
|  4   |   Olympique Lyonnais   | Olympique de Marseille |
|  4   |       FC Nantes        |         Monaco         |
|  4   |        RC Lens         |       LOSC Lille       |
|  5   |  Paris Saint-Germain   |         Monaco         |
|  5   | Olympique de Marseille |   Olympique Lyonnais   |
|  5   |       FC Nantes        |       LOSC Lille       |
|  5   |        RC Lens         |     Saint-Etienne      |
|  6   |     Saint-Etienne      | Olympique de Marseille |
|  6   |         Monaco         |       LOSC Lille       |
|  6   |   Olympique Lyonnais   |        RC Lens         |
|  6   |       FC Nantes        |  Paris Saint-Germain   |
|  7   | Olympique de Marseille |  Paris Saint-Germain   |
|  7   |   Olympique Lyonnais   |     Saint-Etienne      |
|  7   |       FC Nantes        |        RC Lens         |
|  7   |       LOSC Lille       |         Monaco         |
|  8   |     Saint-Etienne      |   Olympique Lyonnais   |
|  8   |         Monaco         |  Paris Saint-Germain   |
|  8   | Olympique de Marseille |       LOSC Lille       |
|  8   |        RC Lens         |       FC Nantes        |
|  9   |  Paris Saint-Germain   |       LOSC Lille       |
|  9   |         Monaco         |     Saint-Etienne      |
|  9   | Olympique de Marseille |        RC Lens         |
|  9   |       FC Nantes        |   Olympique Lyonnais   |
|  10  |  Paris Saint-Germain   |   Olympique Lyonnais   |
|  10  |         Monaco         |       FC Nantes        |
|  10  |        RC Lens         | Olympique de Marseille |
|  10  |       LOSC Lille       |     Saint-Etienne      |
|  11  |  Paris Saint-Germain   |       FC Nantes        |
|  11  |     Saint-Etienne      |       LOSC Lille       |
|  11  | Olympique de Marseille |         Monaco         |
|  11  |        RC Lens         |   Olympique Lyonnais   |
|  12  |     Saint-Etienne      |         Monaco         |
|  12  |   Olympique Lyonnais   |  Paris Saint-Germain   |
|  12  |       FC Nantes        | Olympique de Marseille |
|  12  |       LOSC Lille       |        RC Lens         |
|  13  |  Paris Saint-Germain   |     Saint-Etienne      |
|  13  | Olympique de Marseille |       FC Nantes        |
|  13  |        RC Lens         |         Monaco         |
|  13  |       LOSC Lille       |   Olympique Lyonnais   |
+------+------------------------+------------------------+
"""


from ctypes import CDLL, c_char
import base64

def authpubkey():
    dll = CDLL("c_/lib_anwei.so")
    ecc_list = (c_char * 128)()
    dll.eccpublickey(ecc_list)
    b64_eccpubkey = str(base64.b64encode(bytes.hex(ecc_list.raw).encode("utf-8")), "utf-8")
    return b64_eccpubkey

a = authpubkey()
print(a)


# # format string

# # string
# nama = "ASTA"
# format_str = f"Hello {nama}"
# print(format_str)
# # angka
# umur = 20.5
# format_str = f"umur kamu : {umur}"
# print(format_str)
# # bolean
# boolean = True
# format_str = f"boolean {boolean}"
# print(format_str)
# # bilangan bulat (:d)
# angka = 23
# format_str = f"bilangan bulat {angka:d}"
# print(format_str)
# # bilangan ribuan sd jutaan
# angka = 1000000
# format_str = f"jutaan {angka:,}"
# print(format_str)
# # bilanga desimal ditambah (.angka dibelakng koma sama f/float)
# angka = 2023.919012
# format_str = f"desimal {angka:.4f}"
# print(format_str)
# # menampilkan landing zero
# angka = 2023.919012
# format_str = f"desimal {angka:011.4f}"
# print(format_str)
# # menampilkan tanda + dan -
# minus = -10
# plus = +62.8551864437
# format_minus = f"minus = {minus:+d}"
# format_plus = f"plus = {plus:+.2f}"
# print(format_minus)
# print(format_plus)
# # memformat persen
# persentase = 0.0880
# format_persen = f"persentase = {persentase:.2%}"
# print(format_persen)
# # melakukan operasi aritmatika di dalam placeholder/kurung kurawal/tempalete literal
# sales = 15650122
# adt = 329
# averageTransaction = f"AT = {sales/adt:,.0f}"
# print("sales = ", sales)
# print("adt = ", adt)
# print(averageTransaction)
# # format angka binary, octal, hexadesimal
# angka = 2023
# format_binary = f"binary {bin(angka)}\n"
# format_octal = f"octal {oct(angka)}\n"
# format_hex = f"hex {hex(angka)}\n"
# print(format_binary, format_octal, format_hex)

# string width dan multiline

nama = "Mugiwara Asta"
umur = 23
tinggi = 170.3
beratBadan = 70

# string standart
data_str = f"Nama = {nama}, Umur = {umur}, Tinggi = {tinggi} Berat Badan = {beratBadan} KG"
print(5*"="+"DATA STRING"+5*"=")
print(data_str)
# string multiline dengan menggunakan \n
data_str = f"Nama = {nama}, \nUmur = {umur}, \nTinggi = {tinggi} \nBerat Badan = {beratBadan} KG"
print(5*"="+"DATA STRING"+5*"=")
print(data_str)
# string multiline (""" kutip triple)
data_str = f"""
Nama = {nama}
Umur = {umur}
Tinggi = {tinggi}
Berat Badan = {beratBadan}
"""
print(5*"="+"DATA STRING"+5*"=")
print(data_str)
# merubah lebar
data_str = f"""
Nama        = {nama:>5}
Umur        = {umur:>5}
Tinggi      = {tinggi:>5}
Berat Badan = {beratBadan:>5}
"""
print(5*"="+"DATA STRING"+5*"=")
print(data_str)


import os
import torch
import torchvision
from torchsummary import summary
from matplotlib import pyplot as plt
from torch.utils.data import DataLoader as dataloader
from tensorboardX import SummaryWriter
from torch.autograd import Variable
import torch.nn.functional as F
import torch.optim as optim
import torch.nn as nn
import numpy as np
import time

# hyper parameters
path_model = "./checkpoint/"
batch_size = 512
epochs = 50
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
# sampler = torch.utils.data.SubsetRandomSampler(indices=list(range(2000)))

# download mnist dataset
data_train = torchvision.datasets.MNIST(root='./data/',train=True,download=True,transform=transform)
data_test = torchvision.datasets.MNIST(root='./data/',train=False,download=True,transform=transform)

class_names = data_train.classes  # 获取数据集的分类信息 返回一个字典
# load dataset
data_train = dataloader(dataset=data_train,batch_size=batch_size,shuffle=False)
data_test = dataloader(dataset=data_test,batch_size=batch_size,shuffle=True)

"""
# 查看测试数据的构成
examples = enumerate(data_test)
batch_idx, (example_data, example_targets) = next(examples)
print(example_targets)
print(example_data.shape)

fig = plt.figure()
for i in range(6):
  plt.subplot(2,3,i+1)
  plt.tight_layout()
  plt.imshow(example_data[i][0], cmap='gray', interpolation='none')
  plt.title("Ground Truth: {}".format(example_targets[i]))
  plt.xticks([])
  plt.yticks([])
plt.show()
"""
def test():
    net.eval()  # 切换到测试模式
    test_correct_num = 0
    with torch.no_grad():   # 不更新参数

        for batch_idx,(data,target) in enumerate(data_test):
            # data = data.to(device)
            # target = target.to(device)
            output = net(data) # 正向传播得到预测值
            _, pred = torch.max(output.data, 1)
            test_correct_num += torch.sum(pred==target).item()
            print("Test Epoch:{} [{}/{} ({:.0f}%)]\t acc:{:.2f}".format(epoch,batch_idx*batch_size,len(data_test.dataset),
                                                 100. * batch_size*batch_idx/len(data_test.dataset),test_correct_num/len(data_test.dataset)))
def train():

    for batch_idx, (data, target) in enumerate(data_train):
        # 清除grad累积值
        optimizer.zero_grad()
        # 读取dataloader中的数据，前半部分是tensor变量，后半部分是真实label
        data = data.to(device)
        target = target.to(device)
        # forward之后得到预测值
        output = net(data)
        # 计算loss
        loss = cost_fun(output, target)
        # backward
        loss.backward()
        # 收集一组新的梯度，并使用optimizer.step()将其传播回每个网络参数
        optimizer.step()
        # 给出loss和acc
        train_loss.append(loss)
        _, pred = torch.max(output.data, 1)
        correct_num = torch.sum(pred == target).item()
        train_acc.append(correct_num / batch_size)
        print("Train Epoch:{}[{}/{} ({:.0f}%)]\t Loss:{:.6f} acc:{:.2f}".format(epoch, batch_idx * batch_size,
               len(data_train.dataset),100. * batch_size * batch_idx / len(data_train.dataset), loss.item(),correct_num / batch_size))

def save_state():
    print('===> Saving weights...')
    state = {
        'state': net.state_dict(),
        'epoch': epoch  # 将epoch一并保存
    }
    if not os.path.isdir('checkpoint'):
        os.mkdir('./checkpoint')
    torch.save(state, path_model + 'Epoch:' + str(epoch) + ' Loss:' + str(train_loss[-1].item()) + '.pth')

def predict():
    state_path = './checkpoint/***' #  ***为指定加载的权重文件名称
    print('===> Loading weights : ' + state_path)
    torch.load(state_path)  # 加载最后训练出的权重
    # 从测试集中选取一个batch做预测
    pred_test = enumerate(data_test)
    batch_idx, (pred_data, pred_gt) = next(pred_test)
    output = net(pred_data)
    _, pred = torch.max(output.data, 1) # 得到预测值
    print("ground truth: ",pred_gt)
    print("predict value: ",pred)

# 构建网络
class LeNet(nn.Module): 					# 继承于nn.Module这个父类
    def __init__(self):						# 初始化网络结构
        super(LeNet, self).__init__()    	# 多继承需用到super函数
        self.block_1 = nn.Sequential(
            nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=2),  # 输出为6*28*28
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 输出为6*14*14
            nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5, stride=1),  # 输出为16*10*10
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),  # 输出为16*5*5
        )
        self.block_2 = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, 10),
        )

    def forward(self, x):  # 正向传播过程
        x = self.block_1(x)
        x = x.view(-1,16*5*5)
        x = self.block_2(x)
        return

# 显示各层参数量和训练过程batch所占内存大小
def variaes_show():
    net = LeNet()
    data_input = Variable(torch.randn(16,1,28,28))
    print(data_input.size())
    net(data_input)
    print(summary(net,(1,28,28)))
if __name__ == '__main__':
    writer = SummaryWriter('logs')
    device = torch.device('cuda:0')
    net = LeNet()  # 实例化网络
    # data_input = Variable(torch.randn(16,1,28,28))
    # print(net(data_input))
    net.to(device) # 将参数送入GPU中
    cost_fun = nn.CrossEntropyLoss()
    # optim
    optimizer = torch.optim.SGD(net.parameters(), lr=1e-3, momentum=0.95, weight_decay=1e-3)

    # train
    for epoch in range(epochs):

        # train
        train_loss = []
        train_acc = []
        # train
        train()
        writer.add_scalar('Train/Loss', train_loss[-2].item(), epoch)
        writer.add_scalar('Train/Acc', train_acc[-2], epoch)

        # ----------------------------------------- #
        # save_state
        # ----------------------------------------- #
        print('===> Saving models...')
        state = {
            'state': net.state_dict(),
            'epoch': epoch  # 将epoch一并保存
        }
        if not os.path.isdir('checkpoint'):
            os.mkdir('./checkpoint')
        torch.save(state, path_model + 'Epoch:' + str(epoch) + ' Loss:'+ str(train_loss[-1].item()) + '.pth')

        # ----------------------------------------- #
        # test
        # ----------------------------------------- #
        test()
    writer.close()

    # ----------------------------------------- #
    # 加载指定的weights进行预测
    # ----------------------------------------- #
    # predict()


# min number of boats required to transfer the person

def boats(people, limit):
    # sort the people array as it contains weight of the people
    people.sort()

    lightest_person = 0
    heaviest_person = len(people) -1
    no_of_boats = 0

    while lightest_person <= heaviest_person:
        no_of_boats +=1
        if people[lightest_person] + people[heaviest_person] <= limit:

            lightest_person+=1
            heaviest_person-=1
        else:
            heaviest_person-=1

    return no_of_boats

if __name__ == "__main__":

    people = [3,1,4,2,4]
    limit = 4

    result = boats(people, limit)

    print(result)


import os
import sys
sys.path.append('/home/yang/Research/CNN/')

from Prepare_Data import pickle_load, prepare_CIFAR10, prepare_CIFAR100, prepare_SVHN
from Prepare_Model import prepare_GBPdenoising_end2end, prepare_resnet
sys.path.append('/home/yang/Research/CNN/Tools')
from Plot import simple_plot
from logging import warning
import numpy as np
import tensorflow as tf
import keras
from keras.datasets import cifar10
import pickle as pkl

trainable = False

ADVs = [
    "ADVs_SVHN_End2End_off_L2_DeepFool",
    "ADVs_SVHN_End2End_off_L2_FGSM",
    "ADVs_SVHN_End2End_off_L2_IterG",
    "ADVs_SVHN_End2End_off_L2_IterGS",
    "ADVs_SVHN_End2End_off_L2_LBFG",
    "ADVs_SVHN_End2End_off_L2_SalMap"
]

def softmax_np(x, axis=None):
    return np.exp(x) / np.sum(np.exp(x), axis=axis)

def main():

    num_classes = 10

    with tf.Session() as sess:

        # # pure Resnet
        # tf_model = prepare_resnet(sess=sess,
        #                           load_weights='./Models/CIFAR10_Resnet.ckpt',
        #                           num_classes=num_classes)

        tf_model = prepare_GBPdenoising_end2end(sess=sess,
                                                saved='./Models/CIFAR10_Resnet.ckpt')

        input_pl = tf_model.inputs
        logits = tf_model.logits

        for tag in ADVs:

            print(tag)

            with open('{}.pkl'.format(tag), 'rb') as file:
                (x_test, y_test) = pkl.load(file)

            print("Number of examples = {}".format(len(x_test)))

            if len(x_test) == 0:
                continue

            # # predict one by one
            # # for each, we predict for N times to test the model stability
            # # set N = 1 for normal prediction
            # # check the testing accuracy
            # test_accu_single = 0.
            # test_accu_vote = 0.
            # var = 0.
            # times = 50
            # stable = 0.
            # for index, image in enumerate(x_test):
            #
            #     label = y_test[index]
            #
            #     batch_image = np.expand_dims(image, axis=0)
            #     # prediction
            #     logit_vals = []
            #     for step in range(times):
            #         logit_vals.append(np.argmax(sess.run(logits, feed_dict={input_pl: batch_image})[0]))
            #
            #     diffs = len(np.unique(logit_vals))
            #     var += diffs
            #
            #     if diffs == 1:
            #         stable += 1
            #
            #     if logit_vals[0] == label:
            #         test_accu_single += 1
            #
            #     if np.bincount(logit_vals).argmax() == label:
            #         test_accu_vote += 1
            #
            # print("Single Test Accuracy = {:.4f}".format(test_accu_single / len(x_test)))
            # print("Vote Test Accuracy = {:.4f}".format(test_accu_vote / len(x_test)))
            # print("Stability = {:.4f}".format(var / len(x_test)))
            # print("Percentage = {:.4f}".format(stable / len(x_test)))

if __name__ == '__main__':
    os.environ['CUDA_VISIBLE_DEVICES'] = '0'
    main()


import pytest
import brownie

DAY = 86400  # seconds

@pytest.fixture
def vault(gov, token, Vault):
    # NOTE: Because the fixture has tokens in it already
    yield gov.deploy(Vault, token, gov, gov, "", "")

@pytest.fixture
def strategy(gov, vault, TestStrategy):
    # NOTE: Because the fixture has tokens in it already
    yield gov.deploy(TestStrategy, vault)

def test_losses(chain, vault, strategy, gov, token):
    vault.addStrategy(strategy, 1000, 1000, 0, {"from": gov})
    token.approve(vault, 500, {"from": gov})
    vault.deposit(500, {"from": gov})

    chain.sleep(DAY // 10)
    strategy.harvest({"from": gov})
    assert token.balanceOf(strategy) == 500

    # First loss
    chain.sleep(DAY // 10)
    strategy._takeFunds(100, {"from": gov})
    strategy.harvest({"from": gov})
    params = vault.strategies(strategy).dict()
    assert params["totalLoss"] == 100
    assert params["totalDebt"] == 400

    # Harder second loss
    chain.sleep(DAY // 10)
    strategy._takeFunds(300, {"from": gov})
    strategy.harvest({"from": gov})
    params = vault.strategies(strategy).dict()
    assert params["totalLoss"] == 400
    assert params["totalDebt"] == 100

    # Strike three
    chain.sleep(DAY // 10)
    assert token.balanceOf(strategy) == 100
    strategy._takeFunds(100, {"from": gov})
    assert token.balanceOf(strategy) == 0
    strategy.harvest({"from": gov})
    params = vault.strategies(strategy).dict()
    assert params["totalLoss"] == 500
    assert params["totalDebt"] == 0


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun May 20 00:53:34 2018

@author: abhay
"""

import bs4 as bs
import urllib.request
import pandas as pd

##------------------------data to fetch from the website##################

details=['Name of the Company','Contact Person','Designation','Street Address','City','State'
         ,'Pincode','Email','Phone','Mobile','Fax','Website']

Na=[]
Contact=[]
De=[]
Street=[]
City=[]
State=[]
Pincode=[]
Email=[]
Phone=[]
Mobile=[]
Fax=[]
Website=[]

link=pd.read_csv('website_list.csv')

print(link['Address'].head())

#link=['http://www.iato.in/members/view/1925 ',
#      'http://www.iato.in/members/view/11402',
#      'http://www.iato.in/members/view/1844',
#      'http://www.iato.in/members/view/2053',
#      'http://www.iato.in/members/view/314']

for url in link['Address']:
    source=urllib.request.urlopen(url).read()
    soup = bs.BeautifulSoup(source,'html5lib')
    te=[]
    for p_tags in  soup.find_all('p'):
        te.append(p_tags.text)
        print(p_tags.text)

    cont=[]
    for t in te:
        cont.append(t.split(':'))

    for i in range(0,len(cont)):
       if details[0] in cont[i]:
           Na.append(cont[i][1])
       if details[1] in cont[i]:
           Contact.append(cont[i][1])
       if details[2] in cont[i]:
           De.append(cont[i][1])
       if details[3] in cont[i]:
           Street.append(cont[i][1])
       if details[4] in cont[i]:
           City.append(cont[i][1])
       if details[5] in cont[i]:
           State.append(cont[i][1])
       if details[6] in cont[i]:
           Pincode.append(cont[i][1])
       if details[7] in cont[i]:
           Email.append(cont[i][1])
       if details[8] in cont[i]:
           Phone.append(cont[i][1])
       if details[9] in cont[i]:
           Mobile.append(cont[i][1])
       if details[10] in cont[i]:
           Fax.append(cont[i][1])
       if details[11] in cont[i]:
           Website.append(cont[i][1])

del(Mobile[1659])
data={details[0]:Na,details[1]:Contact,details[2]:De,details[3]:Street,details[4]:City,
      details[5]:State,details[6]:Pincode,details[7]:Email,details[8]:Phone,details[9]:Mobile,
       details[10]:Fax,details[11]:Website }

df=pd.DataFrame(data)
df.to_csv('content_deatils.csv')


import numpy as np

from tensorflow.python.framework import ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.distributions import uniform as uniform_lib

def assert_finite(array):
  if not np.isfinite(array).all():
    raise AssertionError("array was not all finite. %s" % array[:15])

def assert_strictly_increasing(array):
  np.testing.assert_array_less(0., np.diff(array))

def assert_strictly_decreasing(array):
  np.testing.assert_array_less(np.diff(array), 0.)

def assert_strictly_monotonic(array):
  if array[0] < array[-1]:
    assert_strictly_increasing(array)
  else:
    assert_strictly_decreasing(array)

def assert_scalar_congruency(bijector,
                             lower_x,
                             upper_x,
                             n=int(10e3),
                             rtol=0.01,
                             sess=None):
  """Assert `bijector`'s forward/inverse/inverse_log_det_jacobian are congruent.

  We draw samples `X ~ U(lower_x, upper_x)`, then feed these through the
  `bijector` in order to check that:

  1. the forward is strictly monotonic.
  2. the forward/inverse methods are inverses of each other.
  3. the jacobian is the correct change of measure.

  This can only be used for a Bijector mapping open subsets of the real line
  to themselves.  This is due to the fact that this test compares the `prob`
  before/after transformation with the Lebesgue measure on the line.

  Args:
    bijector:  Instance of Bijector
    lower_x:  Python scalar.
    upper_x:  Python scalar.  Must have `lower_x < upper_x`, and both must be in
      the domain of the `bijector`.  The `bijector` should probably not produce
      huge variation in values in the interval `(lower_x, upper_x)`, or else
      the variance based check of the Jacobian will require small `rtol` or
      huge `n`.
    n:  Number of samples to draw for the checks.
    rtol:  Positive number.  Used for the Jacobian check.
    sess:  `tf.compat.v1.Session`.  Defaults to the default session.

  Raises:
    AssertionError:  If tests fail.
  """
  # Checks and defaults.
  if sess is None:
    sess = ops.get_default_session()

  # Should be monotonic over this interval
  ten_x_pts = np.linspace(lower_x, upper_x, num=10).astype(np.float32)
  if bijector.dtype is not None:
    ten_x_pts = ten_x_pts.astype(bijector.dtype.as_numpy_dtype)
  forward_on_10_pts = bijector.forward(ten_x_pts)

  # Set the lower/upper limits in the range of the bijector.
  lower_y, upper_y = sess.run(
      [bijector.forward(lower_x), bijector.forward(upper_x)])
  if upper_y < lower_y:  # If bijector.forward is a decreasing function.
    lower_y, upper_y = upper_y, lower_y

  # Uniform samples from the domain, range.
  uniform_x_samps = uniform_lib.Uniform(
      low=lower_x, high=upper_x).sample(n, seed=0)
  uniform_y_samps = uniform_lib.Uniform(
      low=lower_y, high=upper_y).sample(n, seed=1)

  # These compositions should be the identity.
  inverse_forward_x = bijector.inverse(bijector.forward(uniform_x_samps))
  forward_inverse_y = bijector.forward(bijector.inverse(uniform_y_samps))

  # For a < b, and transformation y = y(x),
  # (b - a) = \int_a^b dx = \int_{y(a)}^{y(b)} |dx/dy| dy
  # "change_measure_dy_dx" below is a Monte Carlo approximation to the right
  # hand side, which should then be close to the left, which is (b - a).
  # We assume event_ndims=0 because we assume scalar -> scalar. The log_det
  # methods will handle whether they expect event_ndims > 0.
  dy_dx = math_ops.exp(bijector.inverse_log_det_jacobian(
      uniform_y_samps, event_ndims=0))
  # E[|dx/dy|] under Uniform[lower_y, upper_y]
  # = \int_{y(a)}^{y(b)} |dx/dy| dP(u), where dP(u) is the uniform measure
  expectation_of_dy_dx_under_uniform = math_ops.reduce_mean(dy_dx)
  # dy = dP(u) * (upper_y - lower_y)
  change_measure_dy_dx = (
      (upper_y - lower_y) * expectation_of_dy_dx_under_uniform)

  # We'll also check that dy_dx = 1 / dx_dy.
  dx_dy = math_ops.exp(
      bijector.forward_log_det_jacobian(
          bijector.inverse(uniform_y_samps), event_ndims=0))

  [
      forward_on_10_pts_v,
      dy_dx_v,
      dx_dy_v,
      change_measure_dy_dx_v,
      uniform_x_samps_v,
      uniform_y_samps_v,
      inverse_forward_x_v,
      forward_inverse_y_v,
  ] = sess.run([
      forward_on_10_pts,
      dy_dx,
      dx_dy,
      change_measure_dy_dx,
      uniform_x_samps,
      uniform_y_samps,
      inverse_forward_x,
      forward_inverse_y,
  ])

  assert_strictly_monotonic(forward_on_10_pts_v)
  # Composition of forward/inverse should be the identity.
  np.testing.assert_allclose(
      inverse_forward_x_v, uniform_x_samps_v, atol=1e-5, rtol=1e-3)
  np.testing.assert_allclose(
      forward_inverse_y_v, uniform_y_samps_v, atol=1e-5, rtol=1e-3)
  # Change of measure should be correct.
  np.testing.assert_allclose(
      upper_x - lower_x, change_measure_dy_dx_v, atol=0, rtol=rtol)
  # Inverse Jacobian should be equivalent to the reciprocal of the forward
  # Jacobian.
  np.testing.assert_allclose(
      dy_dx_v, np.divide(1., dx_dy_v), atol=1e-5, rtol=1e-3)

def assert_bijective_and_finite(
    bijector, x, y, event_ndims, atol=0, rtol=1e-5, sess=None):
  """Assert that forward/inverse (along with jacobians) are inverses and finite.

  It is recommended to use x and y values that are very very close to the edge
  of the Bijector's domain.

  Args:
    bijector:  A Bijector instance.
    x:  np.array of values in the domain of bijector.forward.
    y:  np.array of values in the domain of bijector.inverse.
    event_ndims: Integer describing the number of event dimensions this bijector
      operates on.
    atol:  Absolute tolerance.
    rtol:  Relative tolerance.
    sess:  TensorFlow session.  Defaults to the default session.

  Raises:
    AssertionError:  If tests fail.
  """
  sess = sess or ops.get_default_session()

  # These are the incoming points, but people often create a crazy range of
  # values for which these end up being bad, especially in 16bit.
  assert_finite(x)
  assert_finite(y)

  f_x = bijector.forward(x)
  g_y = bijector.inverse(y)

  [
      x_from_x,
      y_from_y,
      ildj_f_x,
      fldj_x,
      ildj_y,
      fldj_g_y,
      f_x_v,
      g_y_v,
  ] = sess.run([
      bijector.inverse(f_x),
      bijector.forward(g_y),
      bijector.inverse_log_det_jacobian(f_x, event_ndims=event_ndims),
      bijector.forward_log_det_jacobian(x, event_ndims=event_ndims),
      bijector.inverse_log_det_jacobian(y, event_ndims=event_ndims),
      bijector.forward_log_det_jacobian(g_y, event_ndims=event_ndims),
      f_x,
      g_y,
  ])

  assert_finite(x_from_x)
  assert_finite(y_from_y)
  assert_finite(ildj_f_x)
  assert_finite(fldj_x)
  assert_finite(ildj_y)
  assert_finite(fldj_g_y)
  assert_finite(f_x_v)
  assert_finite(g_y_v)

  np.testing.assert_allclose(x_from_x, x, atol=atol, rtol=rtol)
  np.testing.assert_allclose(y_from_y, y, atol=atol, rtol=rtol)
  np.testing.assert_allclose(-ildj_f_x, fldj_x, atol=atol, rtol=rtol)
  np.testing.assert_allclose(-ildj_y, fldj_g_y, atol=atol, rtol=rtol)


# -*- coding: utf-8 -*-
"""
Created on 20.04.2023

@author: Roman A.

Create and save plots of photometry performed on 1-minute stacked frames

"""

import matplotlib.pyplot as plt
import pathlib
import numpy as np
import pandas as pd
import linecache
import matplotlib.dates as mdates
from tqdm import trange
from matplotlib.dates import DateFormatter
from astropy import wcs, stats

def get_Lightcurve(file):
    ''' retrieve lightcurve data from .txt files
    input: file name (pathlib.Path object)
    output: lightcurve (dict) '''
    # print(file)
    lightcurve = {}

    flux = pd.read_csv(file, delim_whitespace = True, names = ['filename', 'time', 'flux', 'flux_err', 'rel_flux', 'x', 'y'], comment = '#')

    coords = linecache.getline(str(file),6).split(': ')[1].split(' ')
    # med = np.median(flux['rel_flux'])                                   #median flux
    # std = np.std(flux['rel_flux'])

    med=stats.sigma_clipped_stats(flux['rel_flux'])[1]
    std=stats.sigma_clipped_stats(flux['rel_flux'])[2]
    try:
        SNR = med/std
    except:
        SNR=0

    lightcurve = {'flux': flux, 'coords': coords, 'median': med, 'std': std, 'SNR': SNR}
    return lightcurve

def lookLightcurve(star, lightcurve, save_path):
    ''' used to plot lightcurve against time
    input: star number (str), star lightcurve (dict),

    output: None'''

    #get star info
    med = lightcurve['median']
    std = lightcurve['std']
    flux = lightcurve['flux']
    coords = lightcurve['coords']
    SNR = lightcurve['SNR']

    seconds=flux['time']

    fig, ax1 = plt.subplots()

    ax1.scatter(seconds, flux['rel_flux']) #can also plot raw flux
    ax1.hlines(med, min(seconds), max(seconds), color = 'black', label = 'median: %.5f' % med)
    ax1.hlines(med + std, min(seconds), max(seconds), linestyle = '--', color = 'black', label = 'stddev: %.5f' % std)
    ax1.hlines(med - std, min(seconds), max(seconds), linestyle = '--', color = 'black')
    ax1.set_xlabel('time (UT)')
    ax1.set_ylabel('relative counts')
    ax1.set_title('Star #%s [%.1f, %.1f], SNR = %.2f' %(star, float(coords[0]), float(coords[1]), SNR))
    xfmt = DateFormatter('%H:%M') #time format works even for JD
    ax1.xaxis.set_major_formatter(xfmt)
    ax1.legend()
    directory = save_path
    plt.savefig(directory.joinpath(str(star) + '_'+telescope+'_rel.png'), bbox_inches = 'tight',dpi=500)
    # plt.show()
    plt.close()

if __name__ == '__main__':
    base_path=pathlib.Path('/','E:')
    field_name='field6'
    obs_date='2023-04-10'
    telescope='Green'
    lc_path=base_path.joinpath('/StackedData',field_name,obs_date, telescope, 'lightcurves')#path where photometry results are
    stars=[f for f in lc_path.iterdir() if '.txt' in f.name]#list of txts with photometry data
    rel_SNRs=[]#for statistics only
    rel_MEDs=[]#for statistics only
    for i in trange(len(stars)):

        lightcurve = get_Lightcurve(stars[i]) #dictionary with photometric results
        # flux = lightcurve['flux']
        # clipped_flux=stats.sigma_clip(flux['flux'],masked=False,axis=0)
        # rel_SNRs.append(lightcurve['SNR'])
        # rel_MEDs.append(lightcurve['median'])
        lookLightcurve(stars[i].name.split('_')[0], lightcurve, lc_path) #plot each star


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

import subprocess

from jinja2 import Environment, PackageLoader

from .._compat import decode, PY3K

TEMPLATE_ENV = Environment(
    loader=PackageLoader('poet', 'templates'),
    autoescape=False,
    lstrip_blocks=True,
    trim_blocks=True
)

TEMPLATE_ENV.globals.update({
    'isinstance': isinstance,
    'list': list,
    'sorted': sorted,
    'repr': repr
})

def call(args):
    """
    Calls a command and return the output.

    :param args: The command args.
    :type args: list

    :rtype: str
    """
    output = subprocess.check_output(args, stderr=subprocess.STDOUT)

    if PY3K:
        return decode(output)

    return output

def template(name):
    """
    Returns a template given a name.

    :param name: The name of the template.
    :type name: str

    :rtype: jinja2.Template
    """
    if not name.endswith('.jinja2'):
        name += '.jinja2'

    return TEMPLATE_ENV.get_template(name)


import time
import datetime
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.ticker as ticker
import pandas as pd

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.uic import *

class GraphWindow(QDialog):
    def __init__(self, input_df):
        super(GraphWindow, self).__init__()
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.resize(1200, 800)
        self.setWindowTitle("Plot of the Working time")
        self.setWindowFlags(Qt.Window | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint | Qt.WindowStaysOnTopHint)
        self.setModal(True)

        # a figure instance to plot on
        plt.rcParams["date.autoformatter.day"] = "%y/%m/%d"
        self.figure = plt.figure(figsize=(13, 8), dpi=128)
        # adds a button to go back
        self.backbutton = QPushButton("< Back")
        # sets the minimum size and the fontsize
        self.backbutton.setMinimumSize(QSize(0, 50))
        font = QFont()
        font.setPointSize(20)
        font.setBold(True)
        font.setWeight(75)
        self.backbutton.setFont(font)
        self.backbutton.clicked.connect(lambda: self.close())
        # this is the Canvas Widget that displays the `figure`
        # it takes the `figure` instance as a parameter to __init__
        self.canvas = FigureCanvas(self.figure)
        # set the layout
        layout = QVBoxLayout()
        layout.addWidget(self.backbutton)
        layout.addWidget(self.canvas)
        self.setLayout(layout)
        # clears the old values and then adds a subplot to isert all the data
        self.figure.clear()
        ax = self.figure.add_subplot(111)

        df = input_df.copy()
        df["pause"] = df["pause"] / 60
        df["work"] = df["worktime"] / 60 - df["pause"]
        df["overtime"] = df["work"] - 8
        df["overtime"] = df["overtime"].clip(lower=0)
        df["work"] = df["work"].clip(upper=8, lower=0)
        plotdf = df[["work", "overtime", "pause"]]
        plotdf.plot.bar(stacked=True, ax=ax, width=0.8, color=["#2693ff", "#ff4e26", "#25cf5e"])
        ax.legend(fancybox=True, framealpha=0.7)
        plt.axhline(8, color="k", ls="--", lw=1)
        ax.xaxis.get_label().set_visible(False)

        ticklabels = [""] * len(df.index)
        ticklabels[::2] = [item.strftime("%y/%m/%d") for item in df.index[::2]]
        ax.xaxis.set_major_formatter(ticker.FixedFormatter(ticklabels))
        plt.gcf().autofmt_xdate()

        # plt.tight_layout()
        self.canvas.draw()


# Data base bridge
from database_bridge import Db_bridge
#API
from flask import Flask, request, jsonify
from flask_cors import CORS
import json
import os

app = Flask(__name__)
CORS(app)
db = None;

@app.route('/api/')
def hello_world_api():
    return 'Backend up!!'

#Get all the workers
@app.route('/api/workers/all')
def get_workers():
    workers = db.get_workers()
    print(workers)
    return jsonify(workers)

@app.route('/api/workers/add', methods=['POST'])
def add_worker():
    return "To do ..."

#Save a rotine to a worker
@app.route('/api/routines/add', methods=['POST'])
def add_routine():
    return "To do ..."

@app.route('/api/test/workers')
def test_workers():
    db.add_workers('Koko', 'Leonardo','Ortega','koko@loscantoresdechipuco.tocapues')
    db.add_workers('Coronel', 'Julian','Coronel','Coronel@loscantoresdechipuco.tocapues')
    db.add_workers('Karval', 'Carlos','Valencia','Karval@loscantoresdechipuco.tocapues')

    return get_workers()

@app.route('/')
def hello_world_web():
    return 'Yo, sup bro'

@app.route('/api/temp/')
def temp():
    return jsonify(data)

if __name__ == '__main__':
    dev_state, state = None,None

    try:
        state = os.environ['ENVSTATE']
    except:

        state = os.environ['ENVSTATE'] = 'DEV'

    if state == 'PROD':
        print("Production ENV")
        dev_state = False
    elif state == 'DEV':
        print("Deveploment ENV")
        dev_state = True
    else:
        raise Exception("ENVSTATE is unknowing, STATE -> {}".format(state))

    try:
        db = Db_bridge(dev_state)
        app.run(host='0.0.0.0', port=5000, debug=dev_state)
    except KeyboardInterrupt:
        print('Closing server....')