import bpy
import mathutils
import math
from statistics import mean
from bpy import data as d
from mathutils import Vector
from math import radians, degrees, pi
import numpy as np
from enum import Enum
LF_SENSORS = ["Capteur.002", "Capteur.001", "Capteur", "Capteur.003", "Capteur.004"]
SENSOR = d.objects["DistanceSensor"]
LINE = "Plane"
CAR = "Vehicle"
MARBLE = "Marble"
OBSTACLE= "Cube"
FRAME_NUM = 3000
FRAMERATE = 30 # Framerate
TIME = 1 / FRAMERATE
DISTANCE_WHEELS = 0.14 # 14cm d'empattement
DISTANCE_STOP=0.1 #200 mm
TURN_ANGLE = 15
FIND_LINE_ANGLE = 30

ACCELERATIONFACTOR = 1.5
DECELERATIONFACTOR = 1
TURN_ACCELERATION_FACTOR = 10

# Constantes pour bille
ROTATION_RADIUS = 0.14
CONTAINER_HEIGHT = 0.0085 # 10mm - 1.5mm concavity
MARBLE_MASS = 0.0052
MARBLE_R = 0.005
G = 9.810
DAMP = 0.5
Z = np.sqrt(G / ROTATION_RADIUS)

# Constantes Simule Course
CRUISE_SPEED = 70
DODGE_SPEED = 50
SLOW_DOWN_DIST = 330 # 100 de stop + 230 de slow
DECCEL_RATE = 1

VEHICLE_STATES = {
    'STOP' : 0,
    'FORWARD' : 1,
    'BACKWARD' : 2,
}

class Marble:

    def __init__(self, x=0.0, y=0.0, z=0.0, accel=0.0, angle = 0.0):
        self.x = x
        self.y = y
        self.z = z

        self.alpha = np.array([accel*np.cos(angle), accel*np.sin(angle)])
        self.omega = np.array([0,0])
        self.theta = np.arcsin(np.array([x,y]) / ROTATION_RADIUS)

        self.init_theta = self.theta
        self.init_omega = self.omega

        self.rel_time = 0

    def get_cartesian_position(self):
        return self.x, self.y, self.z

    def set_cartesian_position(self):
        linear_displacement = ROTATION_RADIUS * np.sin(self.theta)
        self.x = linear_displacement[0]
        self.y = linear_displacement[1]
        # self.z = MARBLE_R + CONTAINER_HEIGHT + ROTATION_RADIUS*(1 - cos(self.theta))

    def set_acceleration(self, accel, angle=0.0):
        self.alpha = np.array([accel * np.cos(angle), accel * np.sin(angle)])
        self.rel_time = TIME

    def compute_theta(self):
        damping_factor = np.exp(-DAMP * self.rel_time)

        C = -self.alpha / ROTATION_RADIUS

        A = self.init_theta - C / Z ** 2
        B = self.init_omega / Z

        pos = A * np.cos(Z * self.rel_time) + \
              B * np.sin(Z * self.rel_time) + C / Z ** 2

        return damping_factor * pos

    def compute_omega(self):
        damping_factor = np.exp(-DAMP * self.rel_time)

        C = -self.alpha / ROTATION_RADIUS

        A = self.init_theta - C / Z ** 2
        B = self.init_omega / Z

        pos = A * np.cos(Z * self.rel_time) + B * np.sin(Z * self.rel_time) + C / Z ** 2
        vel = -A * np.sin(Z * self.rel_time) * Z + B * np.cos(Z * self.rel_time) * Z

        return vel * damping_factor + -DAMP * damping_factor * pos

    def simulate(self):
        if self.rel_time == TIME:
            self.init_omega = self.omega
            self.init_theta = self.theta

        self.theta = self.compute_theta()
        self.omega = self.compute_omega()

        self.set_cartesian_position()
        self.rel_time += TIME

class Vehicle:

    _coordinates = [0, 0, 0] # Coordonnées du véhicule
    _heading = 0 # Cap du véhicule en radians
    _speed = 0 # Vitesse du véhicule actuelle en m/s
    _prev_speed = 0 # Vitesse précédente du véhicule en m/s. Sert à calculer l'ACCÉLÉRATION
    _wheel_angle = np.pi/2 # Angle des roues, 90 degrés est le centre
    _acceleration = 0 # Accélération du véhicule actuelle, en m/s2
    _state = VEHICLE_STATES['STOP'] # État, 0 = stop, 1 = avancer, 2 = arrêté
    _is_turning = False

    def __init__(self, coordinates, heading):
        self._coordinates = coordinates
        self._heading = heading
        self._marble = Marble()

    def get_marble_position(self, heading):

        rotation_matrix = np.array([
            [np.cos(heading), np.sin(heading), 0],
            [-np.sin(heading), np.cos(heading), 0],
            [0, 0, 1]
        ])

        coords = np.array(self._marble.get_cartesian_position())

        return rotation_matrix @ coords

    # Retourne le pourcentage nécessaire pour obtenir une certaine vitesse en m/s
    # À utiliser pour le vrai véhicule
    def speed_to_percent(self, speed):
        percent = speed/5 # TODO changer la vitesse maximale, ce qui devrait nous donner la bonne valeur
        return percent

    # Converti l'angle des roues en radians en rayon de virage en mètres
    # TODO utiliser les vraies valeurs
    # Pour l'instant on va utiliser un calcul théorique. Cela assume un cas
    # parfait qui n'est pas réaliste. Un meilleur algo est à faire, basé sur:
    # https://patentimages.storage.googleapis.com/c9/d1/1a/2826ee9a515566/WO2005102822A1.pdf
    def angle_to_radius(self, angle):
        try:
            # print("Angle commandé:", angle)
            if angle <= np.pi/2:
                radius = DISTANCE_WHEELS/np.tan(angle)
        except:
            print("Erreur de calcul du rayon")
            return 0
        return radius

    # Retourne l'accélération en fonction du changement de vitesse
    def update_acceleration(self, diff_speed):
       # Sécurité pour la méthode
        if diff_speed > 0.27:
            self._acceleration = 0
            return 0
        elif diff_speed < -0.27:
            self._acceleration = 0
            return 0

        if diff_speed > 0: # Accélération
            self._acceleration = ACCELERATIONFACTOR * (-794.171873034486*np.power(diff_speed, 3)\
                + 281.9660606932039*np.power(diff_speed, 2)\
                - 0.414200251036732*diff_speed\
                - 0.6495715769694956)
        elif diff_speed < 0: # Décélération, on assume l'inverse de l'accélération
            diff_speed = np.abs(diff_speed)
            self._acceleration = -1 * DECELERATIONFACTOR *(-794.171873034486*np.power(diff_speed, 3)\
                + 281.9660606932039*np.power(diff_speed, 2)
                - 0.414200251036732*diff_speed\
                - 0.6495715769694956)
            self._acceleration *= -1
        else:
            self._acceleration = 0

    # Ajuste la vitesse du robot, équivalent à la librairie du robot
    def speed(self, percent):
        self._is_turning = False
        if percent < 0:
            self._speed = 0
            return
        if percent > 100:
            self._speed = 0.27
            return
        speed = 0.002736871508379887 * percent - 0.01346368715083793
        self._speed = speed

    # Mettre les roues droites
    def turn_straight(self):
        self._is_turning = False
        self._wheel_angle = np.radians(90-90)

    # Tourner à un angle, en degrés. 90 est tout droit.
    def turn(self, angle):
        # LineFollower call toujours turn, donc on check si il
        # dit daller tout droit ou non
        if angle == 90:
            self.turn_straight()
            return

        self._is_turning = True
        if angle >= 0:
            self._wheel_angle = np.radians(90-angle)

    # Avancer
    def forward(self):
        self._state = VEHICLE_STATES['FORWARD']

    # Reculer
    def backward(self):
        self._state = VEHICLE_STATES['BACKWARD']

    # Arrêter
    def stop(self):
        self._is_turning = False
        self._speed = 0
        self._state = VEHICLE_STATES['STOP']

    # Méthode à rouler à chaque frame qui va automatiquement déplacer le véhicule. Ça sert à simuler le comportement du vrai véhicule
    def update(self):
        # Calcul de l'accélération
        self.update_acceleration(self._speed - self._prev_speed)
        car_acceleration = self._acceleration
        acceleration_angle = self._heading

        # print("Accélération à: ", self._acceleration)

        # Calcul du cap
        circon = np.abs(self.angle_to_radius(self._wheel_angle)) * 2 * np.pi
        # print("Circonférence: ", circon)
        # print("self._angleroues:", self._wheel_angle)

        # Virage
        if self._is_turning:
            car_acceleration = TURN_ACCELERATION_FACTOR  * self._speed**2 / np.abs(self.angle_to_radius(self._wheel_angle))
            if self._state == 1:
                distanceframe = self._speed * TIME
            elif self._state == 2:
                distanceframe = -1 * self._speed * TIME
            else:
                distanceframe = 0

            if self._wheel_angle >= 0:
                self._heading -= ((distanceframe/circon) * 2 * np.pi)

                if self._heading <= 0:
                    self._heading += 2*np.pi

                acceleration_angle = self._heading - np.pi/2

            else:
                self._heading += ((distanceframe/circon) * 2 * np.pi)

                if self._heading >= 2*np.pi:
                    self._heading -= 2*np.pi

                acceleration_angle = self._heading + np.pi/2

        # print("Cap:", self._heading)

        # Déplacement
        if self._state == VEHICLE_STATES['FORWARD']:
            self._coordinates[0] = self._coordinates[0] + (self._speed * TIME * np.cos(self._heading))
            self._coordinates[1] = self._coordinates[1] + (self._speed * TIME * np.sin(self._heading))
            # print("Avancer: ", self._coordinates)
        elif self._state == VEHICLE_STATES['BACKWARD']:
            self._coordinates[0] = self._coordinates[0] + (self._speed * TIME * np.cos(self._heading + np.pi))
            self._coordinates[1] = self._coordinates[1] + (self._speed * TIME * np.sin(self._heading + np.pi))
            # print("Reculer: ", self._coordinates)
        elif self._state == VEHICLE_STATES['STOP']:
            car_acceleration = -self._prev_speed / TIME
            acceleration_angle = self._heading
        else:
            print("Erreur, état invalide")

        # On met à jour l'ancienne vitesse
        self._prev_speed = self._speed

        # Simule le mouvement de la bille
        if self._state == VEHICLE_STATES['BACKWARD']:
            car_acceleration *= -1

        self._marble.set_acceleration(car_acceleration, angle=acceleration_angle)
        self._marble.simulate()
        return self._coordinates

class DistanceSensor:

    def __init__(self, vehicule, sensor, obstacle_prefix=OBSTACLE):
        vehicule_location = vehicule.matrix_world.translation
        self.vehicule = vehicule
        self.location = sensor.matrix_world.translation
        self.vector = self.location - vehicule_location
        self.angle_threshold = ((30 + 40) / 2) / 2
        self.obstacle_prefix = obstacle_prefix
        self.sensor = sensor

    def get_raw_distance(self, debug=False):
        bpy.context.view_layer.update()
        closest_obstacle_distance = None
        closest_obstacle_name = None
        self.location = self.sensor.matrix_world.translation
        vehicule_location = self.vehicule.matrix_world.translation
        self.vector = self.location - vehicule_location

        if debug:
            in_range = []
            out_of_range = []

        for obj in d.objects:
            if obj.name.startswith(self.obstacle_prefix):
                obstacle_vector = obj.location - self.location
                osbtacle_sensor_angle = self.vector.angle(obstacle_vector) * 360 / (2 * pi)
                if osbtacle_sensor_angle <= self.angle_threshold:
                    obstacle_distance = (obj.location - self.location).length * 1000
                    if closest_obstacle_distance is None or obstacle_distance < closest_obstacle_distance:
                        closest_obstacle_distance = obstacle_distance
                        closest_obstacle_name = obj.name
                    if debug:
                        in_range.append({
                            "name": obj.name,
                            "location": obj.location,
                            "vector": obstacle_vector,
                            "angle": osbtacle_sensor_angle,
                            "distance": obstacle_distance
                        })
                elif debug:
                    out_of_range.append({
                        "name": obj.name,
                        "location": obj.location,
                        "vector": obstacle_vector,
                        "angle": osbtacle_sensor_angle
                    })
        if debug:
            print("------------------ In range objects ------------------")
            for obj in in_range:
                print(str(obj))

            print("------------------ Out of range objects ------------------")
            for obj in out_of_range:
                print(str(obj))
            print("----------------------------------------------------------")

        return closest_obstacle_distance, closest_obstacle_name

    def get_sim_distance(self, debug=False):
        distance, name = self.get_raw_distance(debug=debug)
        if distance is None or distance <= 0:
            return distance
        return self.apply_trendline(raw_distance=distance), name

    # To do - change trendline values
    def apply_trendline(self, raw_distance):
        return 0.9207 * raw_distance - 34.295

class LineFollower():

    def __init__(self, sensors_names, line_name):
        self.last_angle = 0
        self.sensors = [bpy.data.objects.get(sensor) for sensor in sensors_names]
        self.line = bpy.data.objects.get(line_name)

    def is_sensor_over_line(self, sensor):
        bpy.context.view_layer.update()
        origin = sensor.matrix_world.translation

        ray_direction = mathutils.Vector((0, 0, -1))

        ray_begin_local = self.line.matrix_world.inverted() @ origin

        result, loc, normal, face_idx = self.line.ray_cast(ray_begin_local, ray_direction)

        return result

    def lf_read_digital(self):
        return [int(self.is_sensor_over_line(sensor)) for sensor in self.sensors]

    def is_over_line(self):
        for sensor in self.sensors:
            if self.is_sensor_over_line(sensor):
                return True
        return False

    def get_angle_to_turn(self):
        angle = 0
        read = self.lf_read_digital()

        if read == [0, 0, 0, 0, 0]:
            angle = np.sign(self.last_angle)*45
        else:
            angle = (2 - np.mean(np.nonzero(read))) * 45/2
        self.last_angle = angle
        return math.radians(angle)

    def stop(self):
        read = self.lf_read_digital()
        if np.count_nonzero(read) >=3:
            return True

        return False

def print_obstacle_distance(distance, name):
    if distance is None or distance <= 0:
        print("No obstacle found")
    else:
        print(name + " found at distance : " + str(distance) + " mm")

def should_stop(distance):
    distance_stop_mm = DISTANCE_STOP*1000
    if distance is None or distance <= 0 or  distance > distance_stop_mm:
        return False
    else:
        return True

def init_car():
    start_pos = [-0, 0, 0.025]
    start_angle = (0, 0, 0)

    blender_car = bpy.data.objects.get(CAR)
    blender_car.select_set(True)
    blender_car.animation_data_clear()
    blender_car.location = start_pos
    blender_car.rotation_euler = start_angle

    car = Vehicle(start_pos, 0)
    car.speed(CRUISE_SPEED)
    # vehicule.turn(87)
    car.turn_straight()
    car.forward()

    return car, blender_car

def init_marble():
    b_marble = d.objects['Marble']
    b_marble.animation_data_clear()

    return b_marble

def animate_frame(frame_num, car, blender_car, blender_marble):
    # print("FRAME :: ", frame_num)
    bpy.context.scene.frame_set(frame_num)

    # On déplace le véhicule
    blender_marble.location = car.get_marble_position(car._heading)
    blender_car.location = car.update()
    blender_car.rotation_euler.z = car._heading

    # On ajout un keyframe
    blender_car.keyframe_insert(data_path="location", frame=frame_num)
    blender_car.keyframe_insert("rotation_euler", frame=frame_num)
    blender_marble.keyframe_insert(data_path="location", frame=frame_num)

class State(Enum):
    OBSTACLE_APPROACHING = 1
    OBSTACLE_WAITING = 2
    OBSTACLE_BACKWARD = 3
    OBSTACLE_TURN = 4
    OBSTACLE_FIND_LINE = 5
    FOLLOW_LINE = 6
    OBSTACLE_WAIT_BEFORE_FORWARD = 7

def main():
    car, blender_car = init_car()
    blender_marble = init_marble()
    distance_sensor = DistanceSensor(vehicule=blender_car, sensor=SENSOR)
    line_follower = LineFollower(sensors_names=LF_SENSORS, line_name=LINE)
    get_around = False
    state = State.FOLLOW_LINE
    start_waiting_frame = 0
    WAITING_SEC = 5
    WAITING_FRAME_NUM = WAITING_SEC * FRAMERATE

    start_turn_frame = None

    current_speed = CRUISE_SPEED

    for i in range(1, FRAME_NUM):
        print(f"***********FRAME_NUM: {i}******************")
        obstacle_distance, _ = distance_sensor.get_raw_distance()
        slowing_down = obstacle_distance is not None and obstacle_distance <= SLOW_DOWN_DIST
        if(state == State.FOLLOW_LINE):

            if slowing_down:
                if current_speed >= DODGE_SPEED:
                    current_speed -= DECCEL_RATE
            else:
                desired_angle = 90 + line_follower.get_angle_to_turn()
                current_angle = 90 - degrees(car._wheel_angle)
                diff = desired_angle - current_angle
                current_speed = CRUISE_SPEED-0.56*abs(diff)*1

            car.speed(int(current_speed))

            if should_stop(distance=obstacle_distance):
                state= State.OBSTACLE_WAITING
                start_waiting_frame = i
            else:
                angle = line_follower.get_angle_to_turn()
                car.turn(degrees(angle)+90)
        elif(state == State.OBSTACLE_WAITING):
            car.stop()
            if i - start_waiting_frame > WAITING_FRAME_NUM:
                state = State.OBSTACLE_BACKWARD
        elif(state == State.OBSTACLE_BACKWARD):
            car.backward()
            car.turn_straight()
            car.speed(DODGE_SPEED)
            if obstacle_distance is None or obstacle_distance >= 300:
                state = State.OBSTACLE_WAIT_BEFORE_FORWARD
                start_waiting_frame = i
        elif(state == State.OBSTACLE_WAIT_BEFORE_FORWARD):
            car.stop()
            if i - start_waiting_frame > FRAMERATE*0.3:
                state = State.OBSTACLE_TURN
                start_turn_frame = i
        elif(state == State.OBSTACLE_TURN):
                car.forward()
                car.speed(DODGE_SPEED)
                frame_in_turn = math.sqrt(0.3**2+(DISTANCE_WHEELS/2)**2)/(car._speed / FRAMERATE)
                car.turn(90+TURN_ANGLE)
                if i-start_turn_frame>=frame_in_turn:
                    state = State.OBSTACLE_FIND_LINE
        elif(state == State.OBSTACLE_FIND_LINE):
                car.turn(90-FIND_LINE_ANGLE)
                if line_follower.is_over_line():
                    state= State.FOLLOW_LINE
                    car.speed(CRUISE_SPEED)
                    current_speed = CRUISE_SPEED

        if i >= 2477:
            car.stop()

        animate_frame(frame_num=i, car=car, blender_car=blender_car, blender_marble=blender_marble)

if __name__ == '__main__':
    main()
    print("Done")


import os
from setuptools import setup

def read(filename: str):
    return open(os.path.join(os.path.dirname(__file__), filename)).read()

setup(
    name="hsolver",
    version="0.3",
    author="Boris Bantysh",
    author_email="bbantysh60000@gmail.com",
    description="The tool for solving quantum state evolution under periodic hamiltonian",
    license="GPL-3.0",
    keywords="periodic hamiltonian",
    url="https://github.com/bbantysh/hsolver",
    packages=["hsolver"],
    long_description=read("README.md"),
    install_requires=["numpy", "scipy", "pytest"]
)


import asyncio
import logging
import os
from datetime import datetime, timedelta, timezone

import aiohttp
import discord
from discord import Member
from discord.channel import DMChannel
from discord.commands import Option, permissions
from discord.ext import commands, pages, tasks
from discord.ext.ui import (Button, Message, MessageProvider, View,
                            ViewTracker, state)
from holodex.client import HolodexClient
from newdispanderfixed import dispand

import Components.member_button as membership_button
import connect

logging.basicConfig(level=logging.INFO)

'''bot招待リンク
https://discord.com/api/oauth2/authorize?client_id=916956842440151070&permissions=1403113958646&scope=bot%20applications.commands

イベントハンドラ一覧(client)
async def の後を変えるだけで実行されるイベンドが変わる
メッセージ受信時に実行：   on_message(message)
Bot起動時に実行：      on_ready(message)
リアクション追加時に実行:  on_reaction_add(reaction, user)
新規メンバー参加時に実行： on_member_join(member)
ボイスチャンネル出入に実行： on_voice_state_update(member, before, after)'''

conn = connect.connect()
utc = timezone.utc
jst = timezone(timedelta(hours=9), 'Asia/Tokyo')

# onlinetoken@heroku
token = os.environ['DISCORD_BOT_TOKEN']

# help-command-localize-test

class JapaneseHelpCommand(commands.DefaultHelpCommand):
    def __init__(self):
        super().__init__()
        self.commands_heading = "コマンド:"
        self.no_category = "利用可能なコマンド"
        self.command_attrs["help"] = "コマンド一覧と簡単な説明を表示"

    def get_ending_note(self):
        return ('各コマンドの説明: /help <コマンド名>\n')

intents = discord.Intents.all()
bot = commands.Bot(command_prefix='/', intents=intents,
                   help_command=JapaneseHelpCommand())

INIT_EXTENSION_LIST = [
    'Cog.entrance',
    'Cog.ng_word',
    'Cog.poll',
    'Cog.thread',
]

for cog in INIT_EXTENSION_LIST:
    bot.load_extension(cog)

# 本番鯖IDなど

guild_id = 915910043461890078
log_channel = 917009541433016370
vc_log_channel = 917009562383556678
dm_box_channel = 921781301101613076
error_log_channel = 924142068484440084
alert_channel = 924744385902575616
member_check_channel = 926777825925677096
stream_channel = 916337386277969921
count_vc = 925256795491012668
mod_role = 916726433445986334
admin_role = 915954009343422494
everyone = 915910043461890078
yt_membership_role = 923789641159700500

'''
# 実験鯖IDなど
guild_id = 916965252896260117
log_channel = 916971090042060830
vc_log_channel = 916988601902989373
dm_box_channel = 918101377958436954
error_log_channel = 924141910321426452
alert_channel = 924744469327257602
member_check_channel = 926777719964987412
stream_channel = 930020966350876673
count_vc = 925249967478673519
mod_role = 924355349308383252
admin_role = 917332284582031390
everyone = 916965252896260117
yt_membership_role = 926268230417408010
'''

# ID-env
server_member_role = int(os.environ['SERVER_MEMBER_ROLE'])
thread_log_channel = int(os.environ['THREAD_LOG_CHANNEL'])
join_log_channel = int(os.environ['JOIN_LOG_CHANNEL'])
alert_channel = int(os.environ['ALERT_CHANNEL'])

# emoji
accept_emoji = "\N{Heavy Large Circle}"
reject_emoji = "\N{Cross Mark}"

# Boot-log

async def greet():
    channel = bot.get_channel(log_channel)
#    now = discord.utils.utcnow() + timedelta(hours=9)
    now = discord.utils.utcnow()
    await channel.send(f'起動完了({now.astimezone(jst):%m/%d-%H:%M:%S})\nBot ID:{bot.user.id}')
    return

# Task-MemberCount

@tasks.loop(minutes=30)
async def start_count():
    await bot.wait_until_ready()
    await membercount()

# 起動イベント

@bot.event
async def on_ready():
    print('logged in as {0.user}'.format(bot))
    await greet()
    return

# manualcount

@bot.command(name='manualcount')
@commands.has_role(admin_role)
async def _manual(ctx):
    await membercount()
    return

# Membercount本体

async def membercount():
    guild = bot.get_guild(guild_id)
    server_member_count = guild.member_count
    vc = bot.get_channel(count_vc)
    await vc.edit(name=f'Member Count: {server_member_count}')
    return

# error-log

@bot.event
async def on_error(event, something):
    channel = bot.get_channel(error_log_channel)
    now = discord.utils.utcnow().astimezone(jst)
    await channel.send(f'```エラーが発生しました。({now:%m/%d %H:%M:%S})\n{str(event)}\n{str(something)}```')
    return

@bot.event
async def on_command_error(ctx, error):
    channel = bot.get_channel(error_log_channel)
    now = discord.utils.utcnow().astimezone(jst)
    await channel.send(f'```エラーが発生しました。({now:%m/%d %H:%M:%S})\n{str(error)}```')
    if isinstance(error, commands.MissingRole):
        await ctx.reply(content='このコマンドを実行する権限がありません。', mention_author=False)
        return
    elif isinstance(error, commands.CommandNotFound):
        await ctx.reply(content='指定されたコマンドは存在しません。', mention_author=False)
        return
    elif isinstance(error, commands.BotMissingPermissions):
        await ctx.reply(content='Botに必要な権限がありません。', mention_author=False)
        return
    else:
        return

# error-logtest

@bot.command()
@commands.has_role(admin_role)
async def errortest(ctx):
    prin()

# Dispander-All

@bot.listen('on_message')
async def on_message_dispand(message):
    avoid_word_list_head = ['/send-message', '/edit-message', '/send-dm']
    if type(message.channel) == DMChannel:
        return
    else:
        for x in avoid_word_list_head:
            if message.content.startswith(x):
                return
        if message.content.endswith('中止に必要な承認人数: 1'):
            return
        else:
            await dispand(message)
            return

'''
デフォルトで提供されている on_message をオーバーライドすると、コマンドが実行されなくなります。
これを修正するには on_message の最後に bot.process_commands(message) を追加してみてください。
https://discordbot.jp/blog/17/
'''

# VC入退室ログ

@bot.listen()
async def on_voice_state_update(member, before, after):
    if member.guild.id == guild_id and (before.channel != after.channel):
        channel = bot.get_channel(vc_log_channel)
        now = discord.utils.utcnow()
        vc_log_mention = member.mention
        if before.channel is None:
            msg = f'{now.astimezone(jst):%m/%d %H:%M:%S} : {vc_log_mention} が {after.channel.mention} に参加しました。'
            await channel.send(msg)
            return
        elif after.channel is None:
            msg = f'{now.astimezone(jst):%m/%d %H:%M:%S} : {vc_log_mention} が {before.channel.mention} から退出しました。'
            await channel.send(msg)
            return
        else:
            msg = f'{now.astimezone(jst):%m/%d %H:%M:%S} : {vc_log_mention} が {before.channel.mention} から {after.channel.mention} に移動しました。'
            await channel.send(msg)
            return

# hello?

@bot.command()
@commands.has_role(mod_role)
async def test(ctx):
    """生存確認用"""
    await ctx.send('hello')
    return
'''
# user-info-command
@bot.command()
@commands.has_role(mod_role)
async def user(ctx,id:int):
    """ユーザー情報取得"""
    guild = bot.get_guild(guild_id)
    member = guild.get_member(id)
    # この先表示する用
    member_if_bot = member.bot
    member_reg_date = member.created_at.astimezone(jst)
    # NickNameあるか？
    if member.display_name == member.name :
        member_if_nickname = 'None'
    else:
        member_if_nickname = member.display_name
    member_id = member.id
    member_join_date = member.joined_at.astimezone(jst)
    membermention = member.mention
    roles = [[x.name,x.id] for x in member.roles]
# [[name,id],[name,id]...]
    x = ['/ID: '.join(str(y) for y in x) for x in roles]
    z = '\n'.join(x)
    # Message成形-途中
    user_info_msg = f'```ユーザー名:{member} (ID:{member_id})\nBot?:{member_if_bot}\nニックネーム:{member_if_nickname}\nアカウント作成日時:{member_reg_date:%Y/%m/%d %H:%M:%S}\n参加日時:{member_join_date:%Y/%m/%d %H:%M:%S}\n\n所持ロール:\n{z}```'
    await ctx.send(user_info_msg)
'''

@bot.slash_command(name='user', guild_ids=[guild_id], default_permission=False)
@permissions.has_role(mod_role)
async def _newuser(
    ctx,
    member: Option(Member, '対象のIDや名前を入力してください。'),
):
    '''ユーザー情報を取得できます。'''
    # guild = ctx.guild
    # member = guild.get_member(int(id))
    # この先表示する用
    avatar_url = member.display_avatar.replace(
        size=1024, static_format='webp').url
    if member.avatar is None:
        avatar_url = 'DefaultAvatar'
    member_reg_date = member.created_at.astimezone(jst)
    # NickNameあるか？
    if member.display_name == member.name:
        member_nickname = 'None'
    else:
        member_nickname = member.display_name
    member_join_date = member.joined_at.astimezone(jst)
    # membermention = member.mention
    roles = [[x.name, x.id] for x in member.roles]
    # [[name,id],[name,id]...]
    x = ['/ID: '.join(str(y) for y in x) for x in roles]
    z = '\n'.join(x)
    # Message成形-途中
    user_info_msg = f'```ユーザー名:{member} (ID:{member.id})\nBot?:{member.bot}\nAvatar url:{avatar_url}\nニックネーム:{member_nickname}\nアカウント作成日時:{member_reg_date:%Y/%m/%d %H:%M:%S}\n参加日時:{member_join_date:%Y/%m/%d %H:%M:%S}\n\n所持ロール:\n{z}```'
    await ctx.respond(user_info_msg)
    return

# ping-test

@bot.command()
@commands.has_role(admin_role)
async def ping(ctx):
    """生存確認用"""
    raw_ping = bot.latency
    ping = round(raw_ping * 1000)
    await ctx.send(f'Pong!\nPing is {ping}ms')
    return

# receive-dm

@bot.listen('on_message')
async def on_message_dm(message):
    avoid_dm_list = ['/check', '/remove-member']
    if type(message.channel) == DMChannel and bot.user == message.channel.me:
        if message.author.bot:
            return
        else:
            for x in avoid_dm_list:
                if message.content.startswith(x):
                    return
            channel = bot.get_channel(dm_box_channel)
            sent_messages = []
            if message.content or message.attachments:
                embed = await compose_embed_dm_box(message)
                sent_messages.append(embed)
                for attachment in message.attachments[1:]:
                    embed = discord.Embed()
                    embed.set_image(
                        url=attachment.proxy_url
                    )
                    sent_messages.append(embed)
            for embed in message.embeds:
                sent_messages.append(embed)
            await channel.send(embeds=sent_messages)
            return
    else:
        return

# send-message

@bot.command(name='send-message')
@commands.has_role(admin_role)
async def _messagesend(ctx, channel_id: int, *, arg):
    """メッセージ送信用"""
    # channel:送信先
    channel = bot.get_channel(channel_id)
    # role:承認可能ロール
    role = ctx.guild.get_role(admin_role)
    confirm_msg = f'【メッセージ送信確認】\n以下のメッセージを{channel.mention}へ送信します。'
    exe_msg = f'{channel.mention}にメッセージを送信しました。'
    non_exe_msg = f'{channel.mention}へのメッセージ送信をキャンセルしました。'
    confirm_arg = f'\n{arg}\n------------------------'
    turned = await confirm(ctx, confirm_arg, role, confirm_msg)
    if turned:
        msg = exe_msg
        m = await channel.send(arg)
        desc_url = m.jump_url
        await ctx.send('Sended!')
        await send_exe_log(ctx, msg, desc_url)
        return
    elif turned is False:
        msg = non_exe_msg
        desc_url = ''
        await ctx.send('Cancelled!')
        await send_exe_log(ctx, msg, desc_url)
        return
    else:
        return

# send-dm

@bot.command(name='send-dm')
@commands.has_role(admin_role)
async def _send_dm(ctx, user: Member, *, arg):
    """DM送信用"""
    role = ctx.guild.get_role(admin_role)
    confirm_msg = f'【DM送信確認】\n以下のDMを{user.mention}へ送信します。'
    exe_msg = f'{user.mention}にDMを送信しました。'
    non_exe_msg = f'{user.mention}へのDM送信をキャンセルしました。'
    confirm_arg = f'\n{arg}\n------------------------'
    turned = await confirm(ctx, confirm_arg, role, confirm_msg)
    if turned:
        msg = exe_msg
        m = await user.send(arg)
        desc_url = m.jump_url
        await ctx.send('Sended!')
        await send_exe_log(ctx, msg, desc_url)
        return
    elif turned is False:
        msg = non_exe_msg
        desc_url = ''
        await ctx.send('Cancelled!')
        await send_exe_log(ctx, msg, desc_url)
        return
    else:
        return

# edit-message

@bot.command(name='edit-message')
@commands.has_role(admin_role)
async def _editmessage(ctx, channel_id: int, message_id: int, *, arg):
    """メッセージ編集用"""
    channel = bot.get_channel(channel_id)
    role = ctx.guild.get_role(admin_role)
    edit_target = await channel.fetch_message(message_id)
    msg_url = f'https://discord.com/channels/{ctx.guild.id}/{channel_id}/{message_id}'
    confirm_msg = f'【メッセージ編集確認】\n{channel.mention}のメッセージ\n{msg_url}\nを以下のように編集します。'
    exe_msg = f'{channel.mention}のメッセージを編集しました。'
    non_exe_msg = f'{channel.mention}のメッセージの編集をキャンセルしました。'
    confirm_arg = f'\n{arg}\n------------------------'
    turned = await confirm(ctx, confirm_arg, role, confirm_msg)
    if turned:
        msg = exe_msg
        await edit_target.edit(content=arg)
        desc_url = msg_url
        await ctx.send('Edited!')
        await send_exe_log(ctx, msg, desc_url)
        return
    elif turned is False:
        msg = non_exe_msg
        desc_url = ''
        await ctx.send('Cancelled!')
        await send_exe_log(ctx, msg, desc_url)
        return
    else:
        return

# deal-member
# deal:対処。ban/kick
deal = None
# add_dm:デフォルトDMに追加で送信するコンテンツ
add_dm = None

# timeout-member

@bot.user_command(guild_ids=[guild_id], name='緊急タイムアウト')
@permissions.has_role(mod_role)
# user commands return the member
async def _emergency_timeout(ctx, member: Member):
    await member.timeout_for(duration=timedelta(days=1), reason='Emergency Timeout')
    await ctx.respond(f'{member.mention}を緊急タイムアウトしました。', ephemeral=True)
    msg = f'{member.mention}を緊急タイムアウトしました。'
    desc_url = ''
    until = discord.utils.utcnow().astimezone(jst) + timedelta(days=1)
    until_str = until.strftime('%Y/%m/%d/%H:%M')
    await send_context_timeout_log(ctx, msg, desc_url, until_str)

@bot.command(name='timeout')
@commands.has_role(mod_role)
async def _timeout(ctx, member: Member, input_until: str, if_dm: str = 'True'):
    '''メンバーをタイムアウト'''
    until = datetime.strptime(input_until, '%Y%m%d')
    until_jst = until.replace(tzinfo=jst)
    role = ctx.guild.get_role(mod_role)
    valid_if_dm_list = ['True', 'False']
    until_str = until_jst.strftime('%Y/%m/%d/%H:%M')
    if if_dm not in valid_if_dm_list:
        await ctx.reply(content='不明な引数を検知したため処理を終了しました。\nDM送信をOFFにするにはFalseを指定してください。', mention_author=False)
        msg = '不明な引数を検知したため処理を終了しました。'
        desc_url = ''
        await send_exe_log(ctx, msg, desc_url)
        return
    else:
        deal = 'timeout'
        add_dm = f'あなたは{until_str}までサーバーでの発言とボイスチャットへの接続を制限されます。'
        DM_content = await make_deal_dm(ctx, deal, add_dm)
        if if_dm == 'False':
            DM_content = ''
        else:
            pass
        confirm_msg = f'【timeout実行確認】\n実行者:{ctx.author.display_name}(アカウント名:{ctx.author},ID:{ctx.author.id})\n対象者:\n　{member}(ID:{member.id})\n期限:{until_str}\nDM送信:{if_dm}\nDM内容:{DM_content}'
        exe_msg = f'{member.mention}をタイムアウトしました。'
        non_exe_msg = f'{member.mention}のタイムアウトをキャンセルしました。'
        confirm_arg = ''
        turned = await confirm(ctx, confirm_arg, role, confirm_msg)
        if turned:
            msg = exe_msg
            if if_dm == 'True':
                m = await member.send(DM_content)
                desc_url = m.jump_url
                await member.timeout(until_jst.astimezone(utc), reason=None)
                await ctx.send('timeouted!')
                await send_timeout_log(ctx, msg, desc_url, until_str)
                return
            elif if_dm == 'False':
                desc_url = ''
                await member.timeout(until_jst.astimezone(utc), reason=None)
                await ctx.send('timeouted!')
                await send_timeout_log(ctx, msg, desc_url, until_str)
                return
            else:
                return
        elif turned is False:
            msg = non_exe_msg
            desc_url = ''
            await send_exe_log(ctx, msg, desc_url)
            await ctx.send('Cancelled!')
            return
        else:
            return

# untimeout-member

@bot.command(name='untimeout')
@commands.has_role(admin_role)
async def _untimeout(ctx, member: Member):
    '''メンバーのタイムアウトを解除'''
    role = ctx.guild.get_role(admin_role)
    confirm_msg = f'【untimeout実行確認】\n実行者:{ctx.author.display_name}(アカウント名:{ctx.author},ID:{ctx.author.id})\n対象者:\n　{member}(ID:{member.id})'
    exe_msg = f'{member.mention}のタイムアウトの解除をしました。'
    non_exe_msg = f'{member.mention}のタイムアウトの解除をキャンセルしました。'
    confirm_arg = ''
    turned = await confirm(ctx, confirm_arg, role, confirm_msg)
    if turned:
        msg = exe_msg
        desc_url = ''
        await member.timeout(None, reason=None)
        await ctx.send('untimeouted!')
        await send_exe_log(ctx, msg, desc_url)
        return
    elif turned is False:
        msg = non_exe_msg
        desc_url = ''
        await send_exe_log(ctx, msg, desc_url)
        await ctx.send('Cancelled!')
        return
    else:
        return

# kick-member

@bot.command(name='kick')
@commands.has_role(admin_role)
async def _kick_user(ctx, member: Member, if_dm: str = 'True'):
    '''メンバーをキック'''
    role = ctx.guild.get_role(admin_role)
    valid_if_dm_list = ['True', 'False']
    if if_dm not in valid_if_dm_list:
        await ctx.reply(content='不明な引数を検知したため処理を終了しました。\nDM送信をOFFにするにはFalseを指定してください。', mention_author=False)
        msg = '不明な引数を検知したため処理を終了しました。'
        desc_url = ''
        await send_exe_log(ctx, msg, desc_url)
        return
    else:
        deal = 'kick'
        add_dm = ''
        DM_content = await make_deal_dm(ctx, deal, add_dm)
        if if_dm == 'False':
            DM_content = ''
        else:
            pass
        confirm_msg = f'【kick実行確認】\n実行者:{ctx.author.display_name}(アカウント名:{ctx.author},ID:{ctx.author.id})\n対象者:\n　{member}(ID:{member.id})\nDM送信:{if_dm}\nDM内容:{DM_content}'
        exe_msg = f'{member.mention}をキックしました。'
        non_exe_msg = f'{member.mention}のキックをキャンセルしました。'
        confirm_arg = ''
        turned = await confirm(ctx, confirm_arg, role, confirm_msg)
        if turned:
            msg = exe_msg
            if if_dm == 'True':
                m = await member.send(DM_content)
                desc_url = m.jump_url
                await member.kick(reason=None)
                await ctx.send('kicked!')
                await send_exe_log(ctx, msg, desc_url)
                return
            elif if_dm == 'False':
                desc_url = ''
                await member.kick(reason=None)
                await ctx.send('kicked!')
                await send_exe_log(ctx, msg, desc_url)
                return
            else:
                return
        elif turned is False:
            msg = non_exe_msg
            desc_url = ''
            await send_exe_log(ctx, msg, desc_url)
            await ctx.send('Cancelled!')
            return
        else:
            return

# ban-member

@bot.command(name='ban')
@commands.has_role(admin_role)
async def _ban_user(ctx, member: Member, if_dm: str = 'True'):
    '''メンバーをBAN'''
    role = ctx.guild.get_role(admin_role)
    valid_if_dm_list = ['True', 'False']
    if if_dm not in valid_if_dm_list:
        await ctx.reply(content='不明な引数を検知したため処理を終了しました。\nDM送信をOFFにするにはFalseを指定してください。', mention_author=False)
        msg = '不明な引数を検知したため処理を終了しました。'
        desc_url = ''
        await send_exe_log(ctx, msg, desc_url)
        return
    else:
        deal = 'BAN'
        add_dm = '''
今後、あなたはクロヱ水族館に参加することはできません。

BANの解除を希望する場合は以下のフォームをご利用ください。
クロヱ水族館BAN解除申請フォーム
https://forms.gle/mR1foEyd9JHbhYdCA
'''
        DM_content = await make_deal_dm(ctx, deal, add_dm)
        if if_dm == 'False':
            DM_content = ''
        else:
            pass
        confirm_msg = f'【BAN実行確認】\n実行者:{ctx.author.display_name}(アカウント名:{ctx.author},ID:{ctx.author.id})\n対象者:\n　{member}(ID:{member.id})\nDM送信:{if_dm}\nDM内容:{DM_content}'
        exe_msg = f'{member.mention}をBANしました。'
        non_exe_msg = f'{member.mention}のBANをキャンセルしました。'
        confirm_arg = ''
        turned = await confirm(ctx, confirm_arg, role, confirm_msg)
        if turned:
            msg = exe_msg
            if if_dm == 'True':
                m = await member.send(DM_content)
                desc_url = m.jump_url
                await member.ban(reason=None)
                await ctx.send('baned!')
                await send_exe_log(ctx, msg, desc_url)
                return
            elif if_dm == 'False':
                desc_url = ''
                await member.ban(reason=None)
                await ctx.send('baned!')
                await send_exe_log(ctx, msg, desc_url)
                return
            else:
                return
        elif turned is False:
            msg = non_exe_msg
            desc_url = ''
            await ctx.send('Cancelled!')
            await send_exe_log(ctx, msg, desc_url)
            return
        else:
            return

# Unban-member

@bot.command(name='unban')
@commands.has_role(admin_role)
async def _unban_user(ctx, id: int):
    '''ユーザーのBANを解除'''
    user = await bot.fetch_user(id)
    banned_users = await ctx.guild.bans()
    role = ctx.guild.get_role(admin_role)
    for ban_entry in banned_users:
        if ban_entry.user.id == user.id:
            confirm_msg = f'【Unban実行確認】\n実行者:{ctx.author.display_name}(アカウント名:{ctx.author},ID:{ctx.author.id})\n対象者:\n　{user}(ID:{user.id})'
            exe_msg = f'{user.mention}のBANを解除しました。'
            non_exe_msg = f'{user.mention}のBANの解除をキャンセルしました。'
            confirm_arg = ''
            turned = await confirm(ctx, confirm_arg, role, confirm_msg)
            if turned:
                msg = exe_msg
                desc_url = ''
                await ctx.guild.unban(user)
                await ctx.send('Unbaned!')
                await send_exe_log(ctx, msg, desc_url)
                return
            elif turned is False:
                msg = non_exe_msg
                desc_url = ''
                await ctx.send('Cancelled!')
                await send_exe_log(ctx, msg, desc_url)
                return
            else:
                return
        else:
            await ctx.reply('BANリストにないユーザーを指定したため処理を停止します。', mention_author=False)
            msg = 'BANリストにないユーザーを指定したため処理を停止しました。'
            desc_url = ''
            await send_exe_log(ctx, msg, desc_url)
            return

# check-member

@bot.command(name='check')
@commands.dm_only()
async def _check_member(ctx):
    '''メンバーシップ認証用'''
    if ctx.message.attachments == []:
        await ctx.reply(content='画像が添付されていません。画像を添付して送り直してください。', mention_author=False)
        msg = 'メンバー認証コマンドに画像が添付されていなかったため処理を停止しました。'
        desc_url = ''
        await send_exe_log(ctx, msg, desc_url)
        return
    else:
        await ctx.reply(content='認証要求を受理しました。\nしばらくお待ちください。', mention_author=False)
        channel = bot.get_channel(member_check_channel)
        guild = bot.get_guild(guild_id)
        exe_msg = f'{ctx.message.author.mention}のメンバーシップ認証を承認しました。'
        non_exe_msg = f'{ctx.message.author.mention}のメンバーシップ認証を否認しました。'
        future = asyncio.Future()
        view = membership_button.MemberConfView(future, ctx)
        tracker = ViewTracker(view, timeout=None)
        await tracker.track(MessageProvider(channel))
        await future
        if future.done():
            if future.result():
                msg = exe_msg
                desc_url = tracker.message.jump_url
                member = guild.get_member(ctx.message.author.id)
                membership_role_object = guild.get_role(yt_membership_role)
                await member.add_roles(membership_role_object)
                await ctx.reply(content='メンバーシップ認証を承認しました。\nメンバー限定チャンネルをご利用いただけます!', mention_author=False)
                log_channel_object = bot.get_channel(log_channel)
                embed = discord.Embed(
                    title='実行ログ',
                    color=3447003,
                    description=msg,
                    url=f'{desc_url}',
                    timestamp=discord.utils.utcnow()
                )
                embed.set_author(
                    name=bot.user,
                    icon_url=bot.user.display_avatar.url
                )
                embed.add_field(
                    name='実行日時',
                    value=f'{discord.utils.utcnow().astimezone(jst):%Y/%m/%d %H:%M:%S}'
                )
                await log_channel_object.send(embed=embed)
                return
            else:
                msg = non_exe_msg
                desc_url = tracker.message.jump_url
                await tracker.message.reply(content='DMで送信する不承認理由を入力してください。', mention_author=False)

                def check(message):
                    return message.content is not None and message.channel == channel and message.author != bot.user
                message = await bot.wait_for('message', check=check)
                reply_msg = f'メンバーシップ認証を承認できませんでした。\n理由:\n　{message.content}'
                await ctx.reply(content=reply_msg, mention_author=False)
                log_channel_object = bot.get_channel(log_channel)
                embed = discord.Embed(
                    title='実行ログ',
                    color=3447003,
                    description=msg,
                    url=f'{desc_url}',
                    timestamp=discord.utils.utcnow()
                )
                embed.set_author(
                    name=bot.user,
                    icon_url=bot.user.display_avatar.url
                )
                embed.add_field(
                    name='実行日時',
                    value=f'{discord.utils.utcnow().astimezone(jst):%Y/%m/%d %H:%M:%S}'
                )
                await log_channel_object.send(embed=embed)
                return

# remove-member

@bot.command(name='remove-member')
@commands.dm_only()
async def _remove_member(ctx):
    await ctx.reply(content='メンバーシップ継続停止を受理しました。\nしばらくお待ちください。', mention_author=False)
    channel = bot.get_channel(member_check_channel)
    guild = bot.get_guild(guild_id)
    exe_msg = f'{ctx.message.author.mention}のメンバーシップ継続停止を反映しました。'
    future = asyncio.Future()
    view = membership_button.MemberRemoveView(future, ctx)
    tracker = ViewTracker(view, timeout=None)
    await tracker.track(MessageProvider(channel))
    await future
    if future.done():
        if future.result():
            msg = exe_msg
            desc_url = tracker.message.jump_url
            member = guild.get_member(ctx.message.author.id)
            membership_role_object = guild.get_role(yt_membership_role)
            await member.remove_roles(membership_role_object)
            await ctx.reply(content='メンバーシップ継続停止を反映しました。\nメンバーシップに再度登録された際は`/check`で再登録してください。', mention_author=False)
            log_channel_object = bot.get_channel(log_channel)
            embed = discord.Embed(
                title='実行ログ',
                color=3447003,
                description=msg,
                url=f'{desc_url}',
                timestamp=discord.utils.utcnow()
            )
            embed.set_author(
                name=bot.user,
                icon_url=bot.user.display_avatar.url
            )
            embed.add_field(
                name='実行日時',
                value=f'{discord.utils.utcnow().astimezone(jst):%Y/%m/%d %H:%M:%S}'
            )
            await log_channel_object.send(embed=embed)
            return

# member-update-dm

@bot.command(name='update-member')
@commands.has_role(admin_role)
async def _update_member(ctx, *update_member: Member):
    role = ctx.guild.get_role(admin_role)
    update_member_mention = [x.mention for x in update_member]
    update_member_str = '\n'.join(update_member_mention)
    confirm_msg = f'【DM送信確認】\nメンバーシップ更新DMを\n{update_member_str}\nへ送信します。'
    exe_msg = f'{update_member_str}にメンバーシップ更新DMを送信しました。'
    non_exe_msg = f'{update_member_str}へのメンバーシップ更新DM送信をキャンセルしました。'
    DM_content = '【メンバーシップ更新のご案内】\n沙花叉のメンバーシップの更新時期が近づいた方にDMを送信させていただいております。\nお支払いが完了して次回支払日が更新され次第、以前と同じように\n`/check`\nで再認証を行ってください。\n\nメンバーシップを継続しない場合は\n`/remove-member`\nと送信してください。(__**メンバー限定チャンネルの閲覧ができなくなります。**__)'
    confirm_arg = f'\n{DM_content}\n------------------------'
    turned = await confirm(ctx, confirm_arg, role, confirm_msg)
    if turned:
        for x in update_member:
            await x.send(DM_content)
        await ctx.send('Sended!')
        msg = exe_msg
        desc_url = ''
        await send_exe_log(ctx, msg, desc_url)
        return
    elif turned is False:
        msg = non_exe_msg
        desc_url = ''
        await send_exe_log(ctx, msg, desc_url)
        await ctx.send('Cancelled!')
        return
    else:
        return

# Deal-DM

async def make_deal_dm(ctx, deal, add_dm):
    DM_content = f'''【あなたは{deal}されました】
クロヱ水族館/Chloeriumの管理者です。

あなたのサーバーでの行為がサーバールールに違反していると判断し、{deal}しました。
{add_dm}'''
    return DM_content

# confirm-system

async def confirm(ctx, confirm_arg, role, confirm_msg) -> bool:
    send_confirm_msg = f'{confirm_msg}\n------------------------{confirm_arg}\nコマンド承認:{role.mention}\n実行に必要な承認人数: 1\n中止に必要な承認人数: 1'
    m = await ctx.send(send_confirm_msg)
    await m.add_reaction(accept_emoji)
    await m.add_reaction(reject_emoji)
    valid_reactions = [accept_emoji, reject_emoji]
    # wait-for-reaction

    def check_confirm(payload):
        return role in payload.member.roles and str(payload.emoji) in valid_reactions and payload.message_id == m.id
    payload = await bot.wait_for('raw_reaction_add', check=check_confirm)
    # exe
    if str(payload.emoji) == accept_emoji:
        return True
    else:
        return False

# create_log_send_embed_base

async def create_base_log_embed(ctx, msg, desc_url):
    embed = discord.Embed(
        title='実行ログ',
        color=3447003,
        description=msg,
        url=f'{desc_url}',
        timestamp=discord.utils.utcnow()
    )
    embed.add_field(
        name='実行者',
        value=f'{ctx.author.mention}'
    )
    embed.add_field(
        name='実行コマンド',
        value=f'[コマンドリンク]({ctx.message.jump_url})'
    )
    embed.add_field(
        name='実行日時',
        value=f'{discord.utils.utcnow().astimezone(jst):%Y/%m/%d %H:%M:%S}'
    )
    return embed.copy()

# send-exe-log

async def send_exe_log(ctx, msg, desc_url):
    embed = await create_base_log_embed(ctx, msg, desc_url)
    channel = bot.get_channel(log_channel)
    await channel.send(embed=embed)
    return

# send-timeout-log

async def send_timeout_log(ctx, msg, desc_url, until_str):
    embed = await create_base_log_embed(ctx, msg, desc_url)
    channel = bot.get_channel(log_channel)
    embed.insert_field_at(
        2,
        name='解除日時',
        value=f'{until_str}'
    )
    await channel.send(embed=embed)
    return

# context-embed
async def create_base_context_log_embed(ctx, msg, desc_url):
    embed = discord.Embed(
        title='Context Menu 実行ログ',
        color=3447003,
        description=msg,
        url=f'{desc_url}',
        timestamp=discord.utils.utcnow()
    )
    embed.add_field(
        name='実行者',
        value=f'{ctx.author.mention}'
    )
    embed.add_field(
        name='実行日時',
        value=f'{discord.utils.utcnow().astimezone(jst):%Y/%m/%d %H:%M:%S}'
    )
    return embed.copy()

# send-context-log

async def send_context_log(ctx, msg, desc_url):
    embed = await create_base_context_log_embed(ctx, msg, desc_url)
    channel = bot.get_channel(log_channel)
    await channel.send(embed=embed)
    return

# send-context-tiemout-log

async def send_context_timeout_log(ctx, msg, desc_url, until_str):
    embed = await create_base_context_log_embed(ctx, msg, desc_url)
    channel = bot.get_channel(log_channel)
    embed.insert_field_at(
        2,
        name='解除日時',
        value=f'{until_str}'
    )
    await channel.send(embed=embed)
    return

# compose-embed

async def compose_embed_dm_box(message):
    embed = discord.Embed(
        title='DMを受信しました。',
        url=message.jump_url,
        color=3447003,
        description=message.content,
        timestamp=message.created_at
    )
    embed.set_author(
        name=message.author.display_name,
        icon_url=message.author.avatar.url,
    )
    embed.add_field(
        name='送信者',
        value=f'{message.author.mention}'
    )
    embed.add_field(
        name='受信日時',
        value=f'{message.created_at.astimezone(jst):%Y/%m/%d %H:%M:%S}'
    )
    if message.attachments and message.attachments[0].proxy_url:
        embed.set_image(
            url=message.attachments[0].proxy_url
        )
    return embed

# YoutubeAPI
API_KEY = os.environ['GOOGLE_API_KEY']
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'

# create-event-slash

@bot.slash_command(guild_ids=[guild_id], default_permission=False, name='make-event')
@permissions.has_role(mod_role)
async def _newcreateevent(ctx,
                          event_name: Option(str, '配信の名前(例:マリカ,歌枠,など)'),
                          stream_url: Option(str, '配信のURL'),
                          start_time: Option(str, '配信開始時間(202205182100または2100(当日))'),
                          duration: Option(float, '予想される配信の長さ(単位:時間)(例:1.5)'),
                          ):
    '''配信を簡単にイベントに登録できます。'''
    guild = ctx.guild
    if len(start_time) == 4:
        todate = datetime.now(timezone.utc).astimezone(jst)
        start_time_object = datetime.strptime(start_time, '%H%M')
        true_start_jst = start_time_object.replace(
            year=todate.year, month=todate.month, day=todate.day, tzinfo=jst)
    elif len(start_time) == 12:
        true_start = datetime.strptime(start_time, '%Y%m%d%H%M')
        true_start_jst = true_start.replace(tzinfo=jst)
    else:
        await ctx.respond(content='正しい時間を入力してください。\n有効な時間は\n```202205182100(2022年5月18日21:00)もしくは\n2100(入力した日の21:00)です。```', mention_author=False)
        return
    true_duration = timedelta(hours=duration)
    true_end = true_start_jst + true_duration
    await guild.create_scheduled_event(name=f'{event_name}',
                                       description='',
                                       start_time=true_start_jst.astimezone(
                                           utc),
                                       end_time=true_end,
                                       location=stream_url,
                                       )
    await ctx.respond('配信を登録しました。')
    return

# get_stream

@bot.command(name='stream')
@commands.has_role(admin_role)
async def get_stream(ctx):
    await get_stream_method()
    return

@tasks.loop(minutes=1)
async def _get_stream():
    await bot.wait_until_ready()
    await get_stream_method()

# stream-body

api_key = os.environ['HOLODEX_KEY']
headers = {
    'X-APIKEY': f'{api_key}'
}

async def get_stream_method():
    async with HolodexClient(aiohttp.ClientSession(headers=headers)) as client:
        ch_id = os.environ['STREAM_YT_ID']
        lives = await client.live_streams(channel_id=ch_id)
        archives = await client.videos_from_channel(channel_id=ch_id, type='videos')
        yt_channel = await client.channel(channel_id=ch_id)
        lives_list = [x for x in lives.contents if x.status ==
                      'upcoming' and x.type == 'stream']
        nowgoing_list = [x for x in lives.contents if x.status ==
                         'live' and x.type == 'stream']
        ended_list = [x for x in archives.contents if x.status ==
                      'past' and x.type == 'stream']
        weekday_dic = {0: '月', 1: '火', 2: '水',
                       3: '木', 4: '金', 5: '土', 6: '日'}
        for x in lives_list:
            result = conn.get(x.id)
            if result is not None:
                print('配信が重複していたためスキップします。')
                continue
            else:
                set_data = conn.set(f'{x.id}', 'notified', ex=604800)
                if set_data:
                    live_created_stamp = x.published_at.replace(
                        'Z', '+00:00')
                    live_created = datetime.fromisoformat(
                        live_created_stamp).astimezone(jst)
                    created_str = live_created.strftime(
                        '%Y/%m/%d %H:%M:%S')
                    fixed_start_scheduled = x.start_scheduled.replace(
                        'Z', '+00:00')
                    live_start = datetime.fromisoformat(
                        fixed_start_scheduled).astimezone(jst)
                    live_start_timestamp = int(live_start.timestamp())
                    live_start_str_date = datetime.strftime(
                        live_start, '%Y年%m月%d日')
                    live_start_str_time = datetime.strftime(
                        live_start, '%H時%M分')
                    weekday = datetime.date(live_start).weekday()
                    weekday_str = weekday_dic[weekday]
                    embed = discord.Embed(
                        title=f'{x.title}',
                        description='**待機所が作成されました**',
                        url=f'https://youtu.be/{x.id}',
                        color=16711680,
                    )
                    embed.set_author(
                        name=f'{yt_channel.name}',
                        url=f'https://www.youtube.com/channel/{ch_id}'
                    )
                    embed.add_field(
                        name='**配信予定日(JST)**',
                        value=f'{live_start_str_date}({weekday_str})',
                    )
                    embed.add_field(
                        name='**配信予定時刻(JST)**',
                        value=f'{live_start_str_time}',
                    )
                    embed.add_field(
                        name='**配信予定時刻(Timestamp)**',
                        value=f'<t:{live_start_timestamp}:f>',
                        inline=False
                    )
                    embed.set_image(
                        url=f'https://i.ytimg.com/vi/{x.id}/maxresdefault.jpg'
                    )
                    embed.set_footer(
                        text=f'{yt_channel.name} ({created_str})',
                        icon_url=f'{yt_channel.photo}'
                    )
                    channel = bot.get_channel(stream_channel)
                    await channel.send(embed=embed)
                    continue
        for x in nowgoing_list:
            result = conn.get(x.id)
            if result == 'notified':
                reset_data = conn.set(f'{x.id}', 'started', ex=604800)
                if reset_data:
                    stamp_actual_start = x.start_actual.replace(
                        'Z', '+00:00')
                    actual_start = datetime.fromisoformat(
                        stamp_actual_start).astimezone(jst)
                    actual_start_str = actual_start.strftime(
                        '%Y/%m/%d %H:%M:%S')
                    embed = discord.Embed(
                        title=f'{x.title}',
                        description='**ライブストリーミングが開始されました**',
                        url=f'https://youtu.be/{x.id}',
                        color=16711680,
                    )
                    embed.set_author(
                        name=f'{yt_channel.name}',
                        url=f'https://www.youtube.com/channel/{ch_id}'
                    )
                    embed.set_footer(
                        text=f'{yt_channel.name} ({actual_start_str})',
                        icon_url=f'{yt_channel.photo}'
                    )
                    embed.set_image(
                        url=f'https://i.ytimg.com/vi/{x.id}/maxresdefault.jpg'
                    )
                    channel = bot.get_channel(stream_channel)
                    await channel.send(embed=embed)
            else:
                continue
        for x in ended_list:
            result = conn.get(x.id)
            if result == 'started':
                end_date = conn.set(f'{x.id}', 'ended', ex=604800)
                if end_date:
                    stamp_actual_start = x.available_at.replace(
                        'Z', '+00:00')
                    actual_end = datetime.fromisoformat(
                        stamp_actual_start).astimezone(jst) + timedelta(seconds=x.duration)
                    end_date_str = actual_end.strftime('%Y年%m月%d日')
                    end_date_time_str = actual_end.strftime('%H時%M分')
                    actual_end_str = actual_end.strftime('%Y/%m/%d %H:%M:%S')
                    ast_m, ast_s = divmod(x.duration, 60)
                    ast_h, ast_m = divmod(ast_m, 60)
                    time_str = f'{ast_h}時間{ast_m}分{ast_s}秒'
                    weekday = datetime.date(actual_end).weekday()
                    weekday_str = weekday_dic[weekday]
                    embed = discord.Embed(
                        title=f'{x.title}',
                        description='**ライブストリーミングが終了しました**',
                        url=f'https://youtu.be/{x.id}',
                        color=16711680,
                    )
                    embed.set_author(
                        name=f'{yt_channel.name}',
                        url=f'https://www.youtube.com/channel/{ch_id}'
                    )
                    embed.set_footer(
                        text=f'{yt_channel.name} ({actual_end_str})',
                        icon_url=f'{yt_channel.photo}'
                    )
                    embed.add_field(
                        name='**配信終了日(JST)**',
                        value=f'{end_date_str}({weekday_str})',
                    )
                    embed.add_field(
                        name='**配信終了時刻(JST)**',
                        value=f'{end_date_time_str}',
                    )
                    embed.add_field(
                        name='**総配信時間**',
                        value=f'{time_str}',
                    )
                    embed.set_image(
                        url=f'https://i.ytimg.com/vi/{x.id}/maxresdefault.jpg'
                    )
                    channel = bot.get_channel(stream_channel)
                    await channel.send(embed=embed)
            else:
                continue

# create a user command for the supplied guilds
# @bot.user_command(guild_ids=[guild_id])
# async def mention(ctx, member: Member):  # user commands return the member
#     await ctx.respond(f"{ctx.author.name} just mentioned {member.mention}!")

start_count.start()
_get_stream.start()

bot.run(token)


#!/usr/bin/env python
# coding: utf-8

# # Filter osemosys_global datapackaged based on user-defined geographic scope

import pandas as pd
import os
import yaml
import shutil
from pathlib import Path
# from osemosys_global.configuration import ConfigFile, ConfigPaths
from configuration import ConfigFile, ConfigPaths
import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)

# CONFIGURATION PARAMETERS

config_paths = ConfigPaths()
config = ConfigFile('config')

scenario_name = config.get('scenario')
geographic_scope = config.get('geographic_scope')
remove_nodes = config.get('nodes_to_remove')

output_data_dir = config_paths.output_data_dir
scenario_dir = config_paths.scenario_dir
scenario_data_dir = config_paths.scenario_data_dir

# FILTERING

if not geographic_scope: # Check for empty list (ie. World run)
    geographic_scope = []
geographic_scope.append('INT') # 'INT' for international fuels added by default
international_fuels = ['COA', 'COG', 'GAS', 'OIL', 'PET', 'OTH', 'URN']

if not os.path.exists(scenario_data_dir):
    os.makedirs(scenario_data_dir)

for each_csv in Path(output_data_dir).glob('*.csv'):
    df = pd.read_csv(os.path.join(output_data_dir, each_csv))

    if not df.empty:
        # Do not filter if only element is international fuels
        if geographic_scope[0] != 'INT':
            if 'TECHNOLOGY' in df.columns:
                df = df.loc[df['TECHNOLOGY'].str[3:6].isin(geographic_scope) |
                            df['TECHNOLOGY'].str[6:9].isin(geographic_scope) |
                            df['TECHNOLOGY'].str[8:11].isin(geographic_scope)]

                # Filter out all international TRN techs
                df = df.loc[~(
                    df['TECHNOLOGY'].str.startswith('TRN') &
                    (~(df['TECHNOLOGY'].str[3:6].isin(geographic_scope)) |
                    ~(df['TECHNOLOGY'].str[8:11].isin(geographic_scope)))
                    )]

                if remove_nodes:
                    df = df.loc[~(df['TECHNOLOGY'].str[3:8].isin(remove_nodes) |
                                df['TECHNOLOGY'].str[6:11].isin(remove_nodes) |
                                df['TECHNOLOGY'].str[8:13].isin(remove_nodes))]

            if 'FUEL' in df.columns:
                df = df.loc[df['FUEL'].str[3:6].isin(geographic_scope) |
                            df['FUEL'].str[6:9].isin(geographic_scope) |
                            df['FUEL'].isin(international_fuels)]

                if remove_nodes:
                    df = df.loc[~(df['FUEL'].str[3:8].isin(remove_nodes) |
                                df['FUEL'].str[6:11].isin(remove_nodes))]

            if str(each_csv).split('/')[-1] == 'FUEL.csv':
                df = df.loc[df['VALUE'].str[3:6].isin(geographic_scope) |
                            df['VALUE'].str[6:9].isin(geographic_scope) |
                            df['VALUE'].isin(international_fuels)]

                if remove_nodes:
                    df = df.loc[~(df['VALUE'].str[3:8].isin(remove_nodes) |
                                df['VALUE'].str[6:11].isin(remove_nodes))]

            if str(each_csv).split('/')[-1] == 'TECHNOLOGY.csv':
                df = df.loc[df['VALUE'].str[3:6].isin(geographic_scope) |
                            df['VALUE'].str[6:9].isin(geographic_scope) |
                            df['VALUE'].str[8:11].isin(geographic_scope)]
                df = df.loc[~(
                    df['VALUE'].str.startswith('TRN') &
                    (~(df['VALUE'].str[3:6].isin(geographic_scope)) |
                    ~(df['VALUE'].str[8:11].isin(geographic_scope)))
                    )]

                if remove_nodes:
                    df = df.loc[~(df['VALUE'].str[3:8].isin(remove_nodes) |
                                df['VALUE'].str[6:11].isin(remove_nodes) |
                                df['VALUE'].str[8:13].isin(remove_nodes))]

    df.to_csv(os.path.join(os.path.join(scenario_data_dir, each_csv.name)), index = None)

logging.info('Geographic Filter Applied')


from datetime import datetime, timezone, timedelta
import requests
import json

class GCoreAccount:
    def __init__(self, api_key):
        self.__api_key = api_key
        self.__default_headers = {
            "Authorization": "APIKey " + self.__api_key
        }

    def get_cdn_service_details(self):
        host = "https://api.gcorelabs.com/cdn/clients/me"
        response = requests.get(host, headers=self.__default_headers)
        return json.loads(response.text)

    def get_all_cdn_resources(self):
        host = "https://api.gcorelabs.com/cdn/resources"
        response = requests.get(host, headers=self.__default_headers)
        return json.loads(response.text)

    def get_cdn_id_by_domain(self, domain):
        all_resources = self.get_all_cdn_resources()
        for resource in all_resources:
            if resource["cname"] == domain:
                return resource["id"]
        return None

    def get_cdn_30_days_traffic(self, resource_id=None):
        host = "https://api.gcorelabs.com/cdn/statistics/aggregate/stats"
        utc_time_now = datetime.now(timezone.utc)
        start_time = (utc_time_now - timedelta(days=30)).strftime("%Y-%m-%dT%H:00:00")
        end_time = utc_time_now.strftime("%Y-%m-%dT%H:00:00")
        if resource_id is None:
            response = requests.get(host, headers=self.__default_headers,
                                    params={"service": "CDN",
                                            "from": start_time,
                                            "to": end_time,
                                            "metrics": "sent_bytes",
                                            "group_by": "resource"})
        else:
            response = requests.get(host, headers=self.__default_headers,
                                    params={"service": "CDN",
                                            "from": start_time,
                                            "to": end_time,
                                            "metrics": "sent_bytes",
                                            "group_by": "resource",
                                            "resource": resource_id})
        return json.loads(response.text)

    def get_cdn_30_day_traffic_by_domain(self, domain):
        target_id = self.get_cdn_id_by_domain(domain)
        result = self.get_cdn_30_days_traffic(resource_id=target_id)
        return result

    def get_remaining_traffic(self):
        TOTAL_TRAFFIC = 1099511627776
        total_traffic_bytes = 0
        result = self.get_cdn_30_days_traffic()["resource"]
        for resource_id in result.keys():
            total_traffic_bytes += result[resource_id]["metrics"]["sent_bytes"]
        return TOTAL_TRAFFIC - total_traffic_bytes

    def get_remaining_traffic_percentage(self):
        return round(self.get_remaining_traffic() / 1099511627776, 4)


from django.urls import path # if we want to define path we need to import this

from . import views # We are going to route url's to views's function so we need to import this

urlpatterns=[

    path('',views.index,name='listings'),  # /listings i ifade eder
    path('<int:listing_id>',views.listing,name='listing'),  #/listings/23 gibi olmasını istiyoruz. listing_id parametresine erisiyoruz
    path('search',views.search,name='search'),

    # simdi ana url dosyasina dicezki eger /listings ile baslayan bir sey gorursen gel bu listings app in icindeki urls.py dosyasina bak
]


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def recusive_reverse(self, head):
        if not head or not head.next:
            return head

        p = self.recusive_reverse(head.next)
        head.next.next = head
        head.next = None
        return p

    def reverse(self, head):
        prev = None
        temp = None
        while head:
            temp = head.next

            head.next = prev

            prev = head
            head = temp

        return prev

    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        l1 = self.recusive_reverse(l1)
        l2 = self.recusive_reverse(l2)
        head = None
        carry = 0
        while l1 or l2:
            acc = 0
            if l1:
                acc += l1.val
                l1 = l1.next
            if l2:
                acc += l2.val
                l2 = l2.next

            carry += acc
            head = ListNode(carry % 10, head)
            carry //= 10

        if carry:
            head = ListNode(carry % 10, head)

        return head


import numpy as np
import copy
from functools import reduce
import operator

rows = [[1], [1], [1], [1], [2]]
columns = [[1], [1], [1], [1], [1]]

#rows = [[1, 3], [1, 2], [4], [1], [1]]
#columns = [[4], [1, 1], [1, 1], [3], [2]]

#rows = [[3], [1], [1, 2], [2], [2, 2]]
#columns = [[2], [3], [1], [3, 1], [1, 1, 1]]

# rows = [[2, 2, 1], [2, 1, 3, 1], [2, 2], [1, 4], [2, 1, 1, 1], [5, 2], [1, 1, 3], [2, 3], [1, 1, 1, 1], [1, 3, 4]]
# columns = [[1, 3, 2], [1, 2], [1, 3, 1], [2, 2, 1, 1], [1, 2, 2], [5], [2, 1, 1, 1], [1, 1, 5], [2, 3, 1], [3, 1, 3]]

# rows = [[13],[1,1,1,3],[1,3,9],[2,5,1],[1,4,4,1],[4,3,3],[1,2,1,1],[2,1,1,1],[2,7,1],[1,3,5],[2,3,3],[1,2,5],[1,6],[9,1,2],[3,8,1]]
# columns = [[1,1,1,1,1],[2,2,3,2],[1,1,2,1,2],[1,1,2,1,1,2],[6,2,1,1],[1,2,2,3],[3,2,2,2],[1,3,1,4,2],[7,3,2],[6,2,2,1],[6,7],[1,2,2,4,1],[1,1,1,3,1],[1,2,2,3],[1,2,2,3]]

def sequence_value(seq, size):
    if len(seq) == 0:
        return 0
    if seq[0] == 1:
        return 2 ** (size - 1) + sequence_value(seq[1:], size - 2)
    else:
        seq[0] -= 1
        return 2 ** (size - 1) + sequence_value(seq, size - 1)

def sequence(bits, seq=None, new_stream=True):
    if seq is None:
        seq = []
    if len(bits) == 0:
        return seq
    elif bits[0] == '0':
        return sequence(bits[1:], seq, True)
    elif new_stream:
        seq.append(1)
    else:
        seq[-1] += 1
    return sequence(bits[1:], seq, False)

def possible_solutions(n_solution, n_actual):
    solutions = []
    n_min = n_actual if n_actual != 0 else int(bin(n_solution)[2:].rstrip('0'), 2)
    solution_seq = sequence(list(bin(n_solution)[2:]))
    for number in range(n_min, n_solution + 1):
        number_seq = sequence(list(bin(number)[2:]))
        if np.array_equal(number_seq, solution_seq) and number | n_actual == number and number not in solutions:
            solutions.append(number)
    return solutions

def cross_update(n_updated, n_to_update, index_updated, index_to_update):
    arr_updated = list(bin(n_updated)[2:].zfill(len(rows)))
    arr_to_update = list(bin(n_to_update)[2:].zfill(len(rows)))
    arr_to_update[index_to_update] = arr_updated[index_updated]
    return int(''.join(arr_to_update), 2)

rows_decimal = [sequence_value(value, len(columns)) for value in copy.deepcopy(rows)]
columns_decimal = [sequence_value(value, len(rows)) for value in copy.deepcopy(columns)]
rows_solution = [bin(value) for value in copy.deepcopy(rows_decimal)]
columns_solution = [bin(value) for value in copy.deepcopy(columns_decimal)]

def is_solved(solution):
    for i in range(len(solution[0])):
        if bin(solution[0][i]).count('1') != rows_solution[i].count('1'):
            return False
        if bin(solution[1][i]).count('1') != columns_solution[i].count('1'):
            return False
    return True

def print_solution(solution):
    biggest_row = len(max(rows, key=len))
    biggest_col = len(max(columns, key=len))
    for i in range(biggest_col, 0, -1):
        row = '   ' * biggest_row + '\t  '
        for j in range(len(columns)):
            row += (str(columns[j][len(columns[j]) - i]) if len(columns[j]) >= i else " ") + "   "
        print(row)
    for i in range(len(solution[0])):
        row_sequence = [str(element) for element in rows[i]]
        row_sequence = [' '] * (biggest_row - len(row_sequence)) + row_sequence
        row = '   '.join(str(element) for element in row_sequence)
        row += "\t| "
        binary_row = list(bin(solution[0][i])[2:].zfill(len(rows)))
        for j in range(len(binary_row)):
            row += ("X" if binary_row[j] == '1' else " ") + " | "
        print(row)

def solve(solution = np.zeros((2, len(rows))).astype(int), last_solution = None):

    for i in range(len(columns)):
        possibilities = possible_solutions(columns_decimal[i], solution[1][i])
        if len(possibilities) == 0:
            return False
        solution[1][i] = reduce(operator.and_,possibilities)
        for j in range(len(rows)):
            solution[0][j] = cross_update(solution[1][i], solution[0][j], j, i)

    for i in range(len(rows)):
        possibilities = possible_solutions(rows_decimal[i], solution[0][i])
        if len(possibilities) == 0:
            return False
        solution[0][i] = reduce(operator.and_,possibilities)
        for j in range(len(columns)):
            solution[1][j] = cross_update(solution[0][i], solution[1][j], j, i)

    if is_solved(solution):
        return solution

    if np.all(solution == last_solution):
        for i in range(len(columns)):
            possibilities = possible_solutions(columns_decimal[i], solution[1][i])
            if len(possibilities) > 1:
                for p in range(len(possibilities)):
                    solution[1][i] = possibilities[p]
                    new_solution = solve(copy.deepcopy(solution), copy.deepcopy(solution))
                    if np.any(new_solution):
                        return new_solution
        return False

    return solve(solution, copy.deepcopy(solution))

solution = solve()
if np.any(solution):
    print_solution(solution)
else:
    print("Unsolvable")


"""
Compare USGS events to events in catalogue:

1) do the big events match?
2) if they match
3) if they don't match,

I can see the future: i have to write the association script mysef

"""
import pandas as pd
import numpy as np
from obspy.geodetics import gps2dist_azimuth

def parse_station_info(input_file):
	# 'reusing functions is bad practice' yes haha

	station_info = {}

	with open(input_file, 'r') as f:
		for line in f:
			#print(line)
			try:
				sta, lon, lat = [x for x in line.strip().split("\t") if x != ""]
			except:
				sta, lon, lat = [x for x in line.strip().split(" ") if x != ""]
			station_info[sta] = {"lon": float(lon), "lat": float(lat)}
	return station_info
def dx(X1, X2):
	"""

	takes in two coordinate tuples (lon, lat), (lon, lat) returning their distance in kilometres
	gps2dist_azimuth also returns the azimuth but i guess i don't need that yet
	it also returns distances in metres so i just divide by 1000

	the order doesn't matter

	:param      X1:   The x 1
	:type       X1:   { type_description }
	:param      X2:   The x 2
	:type       X2:   { type_description }

	"""

	#print(X1, X2)
	return gps2dist_azimuth(X1[1], X1[0], X2[1], X2[0])[0] / 1000

def ip(X, Y):
	if len(X) == 3:

		# arithmetic average of in between gradients to approximate gradient at midpoint

		return 0.5 * ((Y[2] - Y[1])/(X[2] - X[1]) + (Y[1] - Y[0])/(X[1] - X[0]))

	if len(X) == 2:
		return (Y[1] - Y[0])/(X[1] - X[0])

station_info = parse_station_info('../new_station_info.dat')

# usgs catalogue
udf = pd.read_csv("usgs_aceh.csv")
udf.rename(columns = {"time": "timestamp", "latitude": "LAT", "longitude": "LON", "depth":"DEPTH", "mag": "MAG"}, inplace = True)
# print(udf.columns)
udf['timestamp'] = pd.to_datetime(udf['timestamp']).dt.tz_localize(None)

# detections
# eqt_df = pd.read_csv("../master_csv/eqtfiles/merged_customfilter_jan20-may21.csv")
eqt_df = pd.read_csv("../master_csv/eqtfiles/merged_merged_jan20-may21.csv")
eqt_df['p_arrival_time'] = pd.to_datetime(eqt_df['p_arrival_time'])
eqt_df['s_arrival_time'] = pd.to_datetime(eqt_df['s_arrival_time'])

# association file
# real_df = pd.read_csv("../master_csv/master_merged_REAL_mags.csv")
real_df = pd.read_csv("../imported_figures/all_rereal_events.csv")
real_df.drop("MAG", 1, inplace = True)
real_df['timestamp'] = pd.to_datetime(real_df['timestamp'])

# want to know rate of seismicity before the 'mainshock'
# i.e. want no. of detections 1 hr, 4 hrs, 24 hrs before + 1hr, 4 hrs, and 24 hrs after

match_counter = 0

for index, row in udf.iterrows():
	ASSOC_WINDOW = 4 # symmetric over left and right side

	real_df['dt'] = (real_df['timestamp'] - row.timestamp).astype('timedelta64[s]') # this does rounding to seconds, which is not the same as .total_seconds() in python datetime
	window_real_df = (real_df[(real_df['dt'] < ASSOC_WINDOW ) & (real_df['dt'] > -ASSOC_WINDOW)] )
	if len(window_real_df):

		#print(row.timestamp)
		match_counter += 1

		udf.at[index, 'matched'] = True

		headers = ["ID", "timestamp", "dt", "LON", "LAT", "DEPTH", "m_l", "station_gap", "residual_time", "n_picks"]

		for h in headers:
			try:
				udf.at[index, 'matched_' + h] = window_real_df[h].to_list()[0]
			except:
				pass

mudf = (udf[udf['matched'] == 1])# matched usgs dataframe

# for index, row in mudf.iterrows():
# 	mudf.at[index, 'delta_depth'] = row.matched_DEPTH - row.DEPTH
# 	mudf.at[index, 'delta_dist_km'] = dx((row.LON, row.LAT), (row.matched_LON, row.matched_LAT))
# 	mudf.at[index, 'delta_timestamp'] = (row.matched_timestamp - row.timestamp).total_seconds()

# print("mean:")
# print(mudf[["delta_depth", "delta_dist_km", "delta_timestamp"]].mean())
# print("std:")
# print(mudf[["delta_depth", "delta_dist_km", "delta_timestamp"]].std())

# mudf.to_csv("usgs_match.csv", index = False)

# load look up table lol
with open("../gridsearch/model_dlange2_451km-60km.npy", "rb") as f:
	tt = np.load(f)

TT_DX = 1
TT_DZ = 1
TT_NX = tt.shape[0]
TT_NZ = tt.shape[1]
matched_phases = []

for index, row in udf[udf["DEPTH"] < 80].iterrows(): # the travel time table goes up to 90km but uh
	print(row.timestamp)

	station_dist = {}

	# key ordering is immutable: throw into array and let numpy handle it

	dists = []
	depths = []

	# compute station distances for all stations
	for sta in station_info:
		_dist = dx((row.LON, row.LAT), (station_info[sta]["lon"], station_info[sta]["lat"]))
		dists.append(_dist)

		_depth = row.DEPTH
		depths.append(_depth)

	dists = np.array(dists)
	depths = np.array(depths)

	dist_indices = np.array([int(round(x)) for x in dists/TT_DX]) # for table interpolation
	depth_indices = np.array([int(round(x)) for x in depths/TT_DZ]) # for table interpolation

	dist_deltas = dists - dist_indices * TT_DX
	depth_deltas = depths - depth_indices * TT_DZ

	# compute theoretical travel time and do interpolation for distance and depth

	dist_grad_P = []
	dist_grad_S = []

	depth_grad_P = []
	depth_grad_S = []

	tt_P = []
	tt_S = []

	for _c, _i in enumerate(dist_indices):
		if _i + 1 > TT_NX:
			_indices = np.array([_i - 1, _i])
		elif _i - 1 < 0:
			_indices = np.array([_i, _i + 1])
		else:
			_indices = np.array([_i - 1, _i, _i + 1])

		_Y_P = [tt[_x][depth_indices[_c]][0] for _x in _indices]
		_Y_S = [tt[_x][depth_indices[_c]][1] for _x in _indices]

		tt_P.append(tt[_i][depth_indices[_c]][0])
		tt_S.append(tt[_i][depth_indices[_c]][1])

		dist_grad_P.append(ip((_indices) * TT_DX, _Y_P))
		dist_grad_S.append(ip((_indices) * TT_DX, _Y_S))

	for _c, _i in enumerate(depth_indices):
		if _i + 1 > TT_NZ:
			_indices = np.array([_i - 1, _i])
		elif _i - 1 < 0:
			_indices = np.array([_i, _i + 1])
		else:
			_indices = np.array([_i - 1, _i, _i + 1])

		depth_grad_P.append(ip((_indices) * TT_DZ, _Y_P))
		depth_grad_S.append(ip((_indices) * TT_DZ, _Y_S))

	dist_grad_P = np.array(dist_grad_P)
	dist_grad_S = np.array(dist_grad_S)
	depth_grad_P = np.array(depth_grad_P)
	depth_grad_S = np.array(depth_grad_S)

	tt_P = np.array(tt_P)
	tt_S = np.array(tt_S)

	tt_P += dist_grad_P * dist_deltas
	tt_S += dist_grad_S * dist_deltas
	tt_P += depth_grad_P * depth_deltas
	tt_S += depth_grad_S * depth_deltas

	for c, sta in enumerate(station_info):
		station_info[sta]["tt_P"] = tt_P[c]
		station_info[sta]["tt_S"] = tt_S[c]

	# given a set of theoretical arrival times, compare with the detection folder for each station

	P_WINDOW = 4 # SYMMETRIC
	S_WINDOW = 8 # SYMMETRIC
	c = 0

	for sta in station_info:
		_eqt_df = eqt_df[eqt_df['station'] == sta].copy()

		_eqt_df['dt_p'] = ((_eqt_df['p_arrival_time'] - row.timestamp).dt.total_seconds() - station_info[sta]["tt_P"])
		_eqt_df['dt_s'] = ((_eqt_df['s_arrival_time'] - row.timestamp).dt.total_seconds() - station_info[sta]["tt_S"])
		_eqt_df['absdt_p'] = ((_eqt_df['p_arrival_time'] - row.timestamp).dt.total_seconds() - station_info[sta]["tt_P"]).abs()
		_eqt_df['absdt_s'] = ((_eqt_df['s_arrival_time'] - row.timestamp).dt.total_seconds() - station_info[sta]["tt_S"]).abs()

		if (_eqt_df['absdt_p'].min()) < P_WINDOW and (_eqt_df['absdt_s'].min()) < S_WINDOW:
			if (_eqt_df['absdt_p'].idxmin()) == (_eqt_df['absdt_s'].idxmin()):
				target_index = _eqt_df['absdt_p'].idxmin()
				matched_phases.append(target_index)

				for h in ["id", "matched_ID" ]:
					eqt_df.at[target_index, h] = row[h]

				eqt_df.at[target_index, "dt_p"] = _eqt_df.at[target_index, "dt_p"]
				eqt_df.at[target_index, "dt_s"] = _eqt_df.at[target_index, "dt_s"]

print(matched_phases)

m_df = eqt_df.iloc[matched_phases]
m_df.to_csv("rereal_matched_usgs_eqt.csv", index = False)

		#_eqt_df['dt_p'] = (_eqt_df['p_arrival_time'] - (row.timestamp + tt_P[c])).abs()
		# _eqt_df['dt_p'] = ((_eqt_df['p_arrival_time'] - (row.timestamp)).dt.total_seconds() - tt_P[c]).abs()
		# _eqt_df['dt_s'] = ((_eqt_df['s_arrival_time'] - (row.timestamp)).dt.total_seconds() - tt_S[c]).abs()

		#_eqt_df['dt_s'] = (_eqt_df['s_arrival_time'] - (row.timestamp + tt_S[c])).abs()

		# if len(_eqt_df[(_eqt_df['dt_p'] < P_WINDOW)]):
		# #if len(_eqt_df[(_eqt_df['dt_p'] < P_WINDOW) & (_eqt_df['dt_s'] < S_WINDOW)]):
		# 	print("match: ", sta)

# you know, the big brain gridsearch is to simulate events (pre compute them) and compute arrival times for all stations
# and then match
# the only problem is that the stored model has like 4 dimensions, it will have 2e9 entries
# so that's probably why it's not done yet
# there's probably an efficient way to do it involving interpolation so you don't have to recalculate every single entry

# if the event is deeper than 60km, it's probably plate interface
# my travel time table goes up to

# get average difference in location, magnitude, and time

	# match by timestamp, then check
	# arrival time window? p wave velocity ~ 5km, maybe 1 minute?
	# there would be a way to calculate theoretical travel times using the look up table + interpolation


from flask import Flask, render_template, request, redirect, url_for, flash
from flask_mysqldb import MySQL

app = Flask(__name__)
app.secret_key = 'ajay'

app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'rootroot0305'
app.config['MYSQL_DB'] = 'crud'

mysql = MySQL(app)

@app.route('/')
def Index():
    cur = mysql.connection.cursor()
    cur.execute("SELECT  * FROM students")
    data = cur.fetchall()
    return render_template('index2.html', students=data )

@app.route('/course')
def course():
    cur = mysql.connection.cursor()
    cur.execute("SELECT * FROM course")
    data = cur.fetchall()
    return render_template('course.html', course=data)

@app.route('/college')
def college():
    cur = mysql.connection.cursor()
    cur.execute("SELECT * FROM college")
    data = cur.fetchall()
    return render_template('college.html', college=data)

# @app.route('/index')
# def index():
#     return render_template('index2.html')

@app.route('/insert', methods = ['POST'])
def insert():

    if request.method == "POST":
        flash("Data Inserted Successfully")
        ID = request.form['id']
        Firstname = request.form['firstname']
        Lastname = request.form['lastname']
        Course = request.form['course']
        Year = request.form['year']
        Gender = request.form['gender']
        cur = mysql.connection.cursor()
        cur.execute("INSERT students (Id, firstname, lastname, course, year, gender) VALUES (%s, %s, %s, %s, %s,%s)", (ID, Firstname, Lastname, Course, Year, Gender ))
        mysql.connection.commit()
        return redirect(url_for('Index'))

@app.route('/insert_course', methods = ['POST'])
def insert_course():

    if request.method == 'POST':
        flash("Data Inserted Successfully")
        code = request.form['code']
        name = request.form['name']
        college = request.form['college']
        cur = mysql.connection.cursor()
        cur.execute("INSERT INTO course (code, name, college) VALUES (%s, %s, %s)", (code, name, college))
        mysql.connection.commit()
        return redirect(url_for('course'))

@app.route('/insert_college', methods = ['POST'])
def insert_college():

    if request.method == 'POST':
        flash("Data Inserted Successfully")
        code = request.form['code']
        name = request.form['name']
        cur = mysql.connection.cursor()
        cur.execute("INSERT INTO course (code, name) VALUES (%s, %s)", (code, name))
        mysql.connection.commit()
        return redirect(url_for('college'))

@app.route('/delete/<string:id_data>', methods = ['GET'])
def delete(id_data):
    flash("Record Has Been Deleted Successfully")
    cur = mysql.connection.cursor()
    cur.execute("DELETE FROM students WHERE id=%s", (id_data,))
    mysql.connection.commit()
    return redirect(url_for('Index'))

@app.route('/delete_course', methods = ['GET'])
def delete_course(id_course_data):
    flash("Record Has Been Deleted Successfully")
    cur = mysql.connection.cursor()
    cur.execute("DELETE FROM course WHERE id_course=%s", (id_course_data,))
    mysql.connection.commit()
    return redirect(url_for('course'))

@app.route('/delete_college/<string:id_college_data>', methods = ['GET'])
def delete_college(id_college_data):
    flash("Record Has Been Deleted Successfully")
    cur = mysql.connection.cursor()
    cur.execute("DELETE FROM college WHERE id_college=%s", (id_college_data))
    mysql.connection.commit()
    return redirect(url_for('college'))

@app.route('/update',methods=['POST','GET'])
def update():

    if request.method == 'POST':
        ID = request.form['id']
        Firstname = request.form['firstname']
        Lastname = request.form['lastname']
        Course = request.form['course']
        Year = request.form['year']
        Gender = request.form['gender']
        cur = mysql.connection.cursor()
        flash("Data Updated Successfully")
        cur.execute("""
               UPDATE students
               SET firstname=%s,
                Lastname=%s,
                 Course=%s,
                 Year=%s,
                 gender=%s
               WHERE id=%s
            """, (Firstname, Lastname, Course, Year,  Gender, ID ))
        mysql.connection.commit()
        return redirect(url_for('Index'))

@app.route('/update_course',  methods=['POST', 'GET'])
def update_course(id_course):

    if request.method == 'POST':
        Id_course = request.form['id_course']
        code = request.form['code']
        name = request.form['name']
        college = request.form['college']
        cur = mysql.connection.cursor()
        flash("Data Updated Successfully")

        cur.execute("""
               UPDATE course
               SET code=%s,
                name=%s,
                 college=%s,
               WHERE id_course=%s
            """, (code, name, college, id_course))
        mysql.connection.commit()
        return redirect(url_for('Course'))

@app.route('/update_college',methods=['POST', 'GET'])
def update_college(id_college):

    if request.method == 'POST':
        id = request.form['id_college']
        code = request.form['code']
        name = request.form['name']
        cur = mysql.connection.cursor()
        flash("Data Updated Successfully")

        cur.execute("""
               UPDATE college
               SET code=%s,
                name=%s,
               WHERE id_college=%s
            """, (code, name, id_college))
        mysql.connection.commit()
        return redirect(url_for('College'))

if __name__ == "__main__":
    app.run(debug=True)


try:
    import LatLon
except Exception as ex:
    from django_mvc.utils import object_not_imported
    LatLon = object_not_imported("LatLon",ex)

from .widgets import DisplayWidget

class DmsCoordinateDisplay(DisplayWidget):
    def render(self,name,value,attrs=None,renderer=None):
        if value:
            c=LatLon.LatLon(LatLon.Longitude(value.get_x()), LatLon.Latitude(value.get_y()))
            latlon = c.to_string('d% %m% %S% %H')
            lon = latlon[0].split(' ')
            lat = latlon[1].split(' ')

            # need to format float number (seconds) to 1 dp
            lon[2] = str(round(eval(lon[2]), 1))
            lat[2] = str(round(eval(lat[2]), 1))

            # Degrees Minutes Seconds Hemisphere
            lat_str = lat[0] + u'\N{DEGREE SIGN} ' + lat[1].zfill(2) + '\' ' + lat[2].zfill(4) + '\" ' + lat[3]
            lon_str = lon[0] + u'\N{DEGREE SIGN} ' + lon[1].zfill(2) + '\' ' + lon[2].zfill(4) + '\" ' + lon[3]

            return 'Lat/Lon ' + lat_str + ', ' + lon_str
        else:
            return ""


import gym
import numpy as np

from gym.spaces import Box, Discrete
from unityagents import UnityEnvironment

# keys for the environment config
EXECUTABLE = "executable"
TRAIN_MODE = "train_mode"

class BananaEnv(gym.Env):
    """An OpenAI Gym for the Banana Unity environment.
    """
    def __init__(self, env_config):
        """Create a new BananaEnv pointing at the compiled
        Unity environment.

        Args:
            env_config (dict): Environment configuration
        """
        executable = env_config[EXECUTABLE]
        train_mode = env_config[TRAIN_MODE]
        self._env = UnityEnvironment(file_name=executable)
        self._brain_name = self._env.brain_names[0]
        self._brain = self._env.brains[self._brain_name]
        self._train_mode = train_mode
        high = np.tile(np.array([np.finfo(np.float32).max,]), [37,])
        self.observation_space = Box(-high, high, dtype=np.float32)
        self.action_space = Discrete(4)

    def step(self, action):
        """Transition from one state to the next with
        the given action.

        Args:
            action (int): the action to perform

        Returns:
            tuple: next_state, reward, done, additional_info
        """
        env_info = self._env.step(int(action))[self._brain_name]
        next_state = env_info.vector_observations[0]
        reward = env_info.rewards[0]
        done = env_info.local_done[0]
        return next_state, reward, done, {}

    def reset(self):
        """Reset the Unity environment.

        Returns:
            int: the starting state
        """
        env_info = self._env.reset(train_mode=self._train_mode)[self._brain_name]
        return env_info.vector_observations[0]

    def close(self):
        """Close the Unity environment and shut down
        the process.
        """
        self._env.close()