#!/usr/bin/env python3
import time
import rclpy
from enum import Enum
from threading import Thread
from rclpy.node import Node
from rclpy.task import Future
from typing import NamedTuple
from geometry_msgs.msg import Twist

from hiwin_interfaces.srv import RobotCommand
# from YoloDetector import YoloDetectorActionClient

from Ax12 import Ax12
import ax12move as mm
import order

'''
    read axis
'''
axis = []
with open('src/competition/axis_calibration.txt') as A:
    for eachline in A:
        tmp = eachline.split()
        # print(tmp)
        axis.append(tmp)

tea_cali = list(map(float,axis[0]))
puf_cali = list(map(float,axis[1]))
egg_cali = list(map(float,axis[2]))

print(tea_cali)
print(puf_cali)
print(egg_cali)

"""
    start order
"""
order.start_order()
forder = order.final_order
print(forder)

"""
    start motor setting
"""
# e.g 'COM3' windows or '/dev/ttyUSB0' for Linux
Ax12.DEVICENAME = '/dev/ttyUSB0'
# Ax12.DEVICENAME = '/dev/ttyUSB1'

Ax12.BAUDRATE = 1_000_000
Ax12.DEBUG = True
# sets baudrate and opens com port
Ax12.connect()

# create AX12 instance with ID 10
motor_id = 1
my_dxl = Ax12(motor_id)
my_dxl.set_moving_speed(700)
my_dxl.print_status
"""
    end motor setting
"""

DEFAULT_VELOCITY = 100
DEFAULT_ACCELERATION = 100
WORK_BASE = 4
ITEM_BASE = 6

VACUUM_PIN = 2
START_BUTTON = 2

PHOTO_POSE = [0.00, 0.00, 0.00, 0.00, -90.00, 0.00]
# OBJECT_POSE = [20.00, 0.00, 0.00, 0.00, -90.00, 0.00]
OBJECT_POSE = [-67.517, 361.753, 293.500, 180.00, 0.00, 100.572]
PLACE_POSE = [-20.00, 0.00, 0.00, 0.00, -90.00, 0.00]

dis_place = [0.0, 368.0, 15.22, -180.0, 0.0, 90.0]
home =  [0.0, 368.0, 293.50, -180.0, 0.0, 90.0]

home_high =  [0.0, 368.0, 310.50, -180.0, 0.0, 90.0]
dis_point = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
joint_dis = [0.00, 0.00, 0.00, 0.00, -90.00, 0.00]
# 開袋點位
open_bag =  [
                # BASE 6
                [  76.328,  197.461,  219.289,  179.678,    0.603, -178.988], # 到袋子上方
                [  76.328,  197.461,  130.071,  179.678,    0.603, -178.988], # 吸袋子
                [  76.328,  136.196,  404.095,  179.678,    0.603, -178.988], # 提起袋子
                # BASE 4
                [ 355.036,  296.641,  416.286,  179.764,    0.585, 90.4], # 移到卡扣上方
                [ 355.036,  296.641,  170.834,  179.775,    0.605, 90.503], # 降低至卡扣前
                [ 544.536,  296.641,  175.834,  179.775,    0.605, 90.503], # 進卡扣
                [ 544.536,  296.641,  424.883,  179.775,    0.605, 90.503], # 抬起打開紙袋
                [ 544.536,  296.641,  291.289,  179.775,    0.605, 90.503], # 放下一點
                [ 332.086,  296.641,  286.289,  179.775,    0.605, 90.503], # 抽出紙袋
                [196.584, 184.122, 422.637, 141.523, 0.448, 90.397], # 到旋轉袋子位置
                [196.584, 184.122, 276.912, 141.523, 0.448, 90.397], # 放下袋子
                [ 124.936,  184.124,  295.388,  179.775,    0.605, 90.503], # x減一點
                [ 124.936,  184.124,  126.208,  179.775,    0.605, 90.503], # z減一點
                [ 325.595,  184.124,  126.208,  179.775,    0.605, 90.503], # 往牆推
                [ 224.995,  184.124,  470.873,  179.775,    0.605, 90.503], # 歸位
                [ 374.995,  200.124,  470.873,  179.775,    0.605, 90.503]  # 歸位
            ]
# 準備位置
standby =   [  70.817,  -29.797,  411.262,  179.678,    0.603, -178.988]
passby  =   [ 640.230,   90.990,  500.517,  179.775,    0.605,   90.503]
# 五盒紅茶
tea =   [
                # BASE 6
                [  39.134,  200.654,  272.428,  160.573,    0.731, -179.217], # 到紅茶上方
                [  39.134,  200.654,  180.666,  160.573,    0.731, -179.217], # 吸紅茶
                [  39.134,  155.840,  325.078,  160.573,    0.731, -179.217], # 提起袋子
        ]

# 泡芙點位
puf =   [
                # BASE 6
                [ 217.278,   88.715,  129.438,  158.236,    0.608,  178.428], # 到泡芙袋口
                [ 217.278,  153.438,  129.438,  158.236,    0.608,  178.428], # 往前推取泡芙
                [ 217.278,   72.528,  317.904,  158.236,    0.608,  178.428], # 抬起泡芙
                # BASE 4
                [ 490.482,   62.006,  448.429,  158.383,    0.650,   90.042], # pass
                [ 300.383+puf_cali[0],  208.606+puf_cali[1],  448.429+puf_cali[2],  158.383,    0.650,   90.042], # 到袋口
                [ 300.383+puf_cali[0],  208.606+puf_cali[1],  358.429+puf_cali[2],  158.383,    0.650,   90.042], # 進去袋口
        ]

# 奇趣蛋點位
egg =   [
                #BASE 6
                [355.411, 111.404, 255.954, 179.405, 0.635, 179.179],
                [355.411, 111.404, 198.755, 179.405, 0.635, 179.179],
                [345.411,  91.404, 314.938, 179.405, 0.635, 179.179],
                #BASE 4
                [ 408.668+egg_cali[0],  184.124+egg_cali[1],  500.211+egg_cali[2],  179.775,    0.605,    10.503], #
                [ 408.668+egg_cali[0],  184.124+egg_cali[1],  356.440+egg_cali[2],  179.775,    0.605,    10.503]
        ]
# 袋口
bag_mouth   =   [ 408.668+tea_cali[0],  184.124+tea_cali[1],  500.211+tea_cali[2],  179.775,    0.605, 90.503]
tea_enter   =   [
                    [ 398.668+tea_cali[0],  184.124+tea_cali[1],  280.224+tea_cali[2],  179.775,    0.605, 90.503], # 1st
                    [ 428.668+tea_cali[0],  184.124+tea_cali[1],  310.440+tea_cali[2],  179.775,    0.605, 90.503],
                    [ 398.668+tea_cali[0],  184.124+tea_cali[1],  356.440+tea_cali[2],  179.775,    0.605, 90.503] # 3rd
                ]
enbgpf      =   [ -18.763,  344.999,  250.608, -180.000,    0.000, 90.0]
put_tea     =   [  45.989,  345.148,  116.915, -180.000,    0.000, 90.0]
# 出貨
out_bag =   [
                [ 549.459,   45.836,  437.308,  179.775,    0.605, 90.503], # 到木板上方
                [ 549.459,   45.836,  220.865,  179.775,    0.605, 90.503], # PTP下去
                [ 549.459,   45.836,  147.412,  179.775,    0.605, 90.503], # 碰木板
                [ 549.459,   45.836,  200.865,  179.775,    0.605, 90.503], # 抬起一點
                [ 549.459,  179.124,  209.797,  179.775,    0.605, 90.503], # 至推戴位置
                [ 549.459,  179.124,  189.797,  179.775,    0.605, 90.503], # 至推戴位置
                [
                    [-148.746,  179.124,  189.797,  179.775,    0.605, 90.503],
                    [ -23.520,  179.124,  189.797,  179.775,    0.605, 90.503],
                    [  74.746,  179.124,  189.797,  179.775,    0.605, 90.503],
                    [ 187.227,  179.124,  189.797,  179.775,    0.605, 90.503]
                ], # 推袋子
                [ 549.459,   40.443,  220.865,  179.775,    0.605, 90.503], # 回木板位置
                [ 549.459,   40.443,  147.858,  179.775,    0.605, 90.503], # 放下木板
                [ 549.459,   40.443,  220.865,  179.775,    0.605, 90.503], # 回木板位置
            ]
# only for example as we don't use yolo here
# assume NUM_OBJECTS=5, then this process will loop 5 times
NUM_OBJECTS = 0

tea_used = 0
puf_used = 0
egg_used = 0

class States(Enum):
    INIT = 0
    FINISH = 1
    MOVE_TO_PHOTO_POSE = 2
    YOLO_DETECT = 9
    MOVE_TO_OPJECT_TOP = 4
    PICK_OBJECT = 5
    MOVE_TO_PLACE_POSE = 6
    CHECK_POSE = 7
    CLOSE_ROBOT = 8
    WATING_START = 3

class ExampleStrategy(Node):

    def __init__(self):
        super().__init__('example_strategy')
        self.hiwin_client = self.create_client(RobotCommand, 'hiwinmodbus_service')
        self.object_pose = None
        self.object_cnt = 0

    def move_PTP(self,dis,hold = True,base = WORK_BASE,tool = 1):
        pose = Twist()
        [pose.linear.x, pose.linear.y, pose.linear.z] = dis[0:3]
        [pose.angular.x, pose.angular.y, pose.angular.z] = dis[3:6]
        req = self.generate_robot_request(
            cmd_mode=RobotCommand.Request.PTP,
            holding=hold,
            tool=tool,
            base=base,
            pose=pose,
        )
        res = self.call_hiwin(req)
        if hold:
            if res.arm_state == RobotCommand.Response.IDLE:
                return res
            else:
                res = self.call_hiwin(req)
                return res
        else:
            return res

    def slow_move_PTP(self,dis,hold = True,base = WORK_BASE,tool = 1):
        pose = Twist()
        [pose.linear.x, pose.linear.y, pose.linear.z] = dis[0:3]
        [pose.angular.x, pose.angular.y, pose.angular.z] = dis[3:6]
        req = self.generate_robot_request(
            cmd_mode=RobotCommand.Request.PTP,
            holding=hold,
            tool=tool,
            base=base,
            pose=pose,
            velocity = 50,
            acceleration = 50
        )
        res = self.call_hiwin(req)

        return res

    def move_LIN(self,dis,hold = True,base = WORK_BASE,tool = 1):
        pose = Twist()
        [pose.linear.x, pose.linear.y, pose.linear.z] = dis[0:3]
        [pose.angular.x, pose.angular.y, pose.angular.z] = dis[3:6]
        req = self.generate_robot_request(
            cmd_mode=RobotCommand.Request.LINE,
            digital_output_cmd=RobotCommand.Request.DIGITAL_OFF,
            velocity=255,
            acceleration=255,
            tool = tool,
            base = base,
            holding=hold,
            pose=pose
        )
        res = self.call_hiwin(req)
        if hold:
            if res.arm_state == RobotCommand.Response.IDLE:
                return res
            else:
                res = self.call_hiwin(req)
                return res
        else:
            return res

    def slow_move_LIN(self,dis,hold = True,base = WORK_BASE,tool = 1):
        pose = Twist()
        [pose.linear.x, pose.linear.y, pose.linear.z] = dis[0:3]
        [pose.angular.x, pose.angular.y, pose.angular.z] = dis[3:6]
        req = self.generate_robot_request(
            cmd_mode=RobotCommand.Request.LINE,
            digital_output_cmd=RobotCommand.Request.DIGITAL_OFF,
            velocity=60,
            acceleration=60,
            tool = tool,
            base = base,
            holding=hold,
            pose=pose
        )
        res = self.call_hiwin(req)

        return res

    def move_Angle(self,joint_dist,hold = True,base = WORK_BASE,tool = 1):
        pose = Twist()
        req = self.generate_robot_request(
            cmd_type=RobotCommand.Request.JOINTS_CMD,
            joints=joint_dist,
            holding=hold,
            tool = tool,
            base = base,
            pose=pose
            )
        res = self.call_hiwin(req)
        if res.arm_state == RobotCommand.Response.IDLE:
            return res
        else:
            res = self.call_hiwin(req)

    def take_item(self,base = WORK_BASE,tool = 1):
        pose = Twist()
        req = self.generate_robot_request(
            cmd_mode=RobotCommand.Request.DIGITAL_OUTPUT,
            digital_output_cmd=RobotCommand.Request.DIGITAL_ON,
            digital_output_pin=VACUUM_PIN,
            holding=False,
            tool = tool,
            base = base,
            pose=pose
        )
        res = self.call_hiwin(req)
        return res

    def put_item(self,base = WORK_BASE,tool = 1):
        pose = Twist()

        req = self.generate_robot_request(
            cmd_mode=RobotCommand.Request.DIGITAL_OUTPUT,
            digital_output_cmd=RobotCommand.Request.DIGITAL_OFF,
            digital_output_pin=VACUUM_PIN,
            holding=False,
            tool = tool,
            base = base,
            pose=pose
        )
        res = self.call_hiwin(req)
        return res

    def Stop(self):
        con = input("Continue(Y/N)? :")
        if con == 'Y' or con == 'y':
            pass
        else:
            return

    def _state_machine(self, state: States) -> States:
        global tea_used,puf_used,egg_used
        if state == States.INIT:
            self.get_logger().info('INIT')
            nest_state = States.WATING_START

        elif state == States.WATING_START:
            self.get_logger().info('WATING_START')
            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.READ_DI,
                digital_input_pin=START_BUTTON,
                holding=False
            )
            res = self.call_hiwin(req)
            if res.digital_state:
                nest_state = States.MOVE_TO_PHOTO_POSE
            else:
                nest_state = States.WATING_START

        elif state == States.MOVE_TO_PHOTO_POSE:
            self.get_logger().info('START_WORK')
            for i in range(0,4):
                if forder[i][0] == 0 and forder[i][1] == 0 and forder[i][2] == 0:
                    continue
                else:
                    pass
                # 開袋子====================
                # res = self.move_PTP(standby,False,ITEM_BASE)
                # mm.motor_move(my_dxl,525)
                # res = self.move_PTP(open_bag[0],False,ITEM_BASE) # 到袋子上方
                # res = self.move_LIN(open_bag[1],True,ITEM_BASE) # 吸袋子
                # res = self.take_item()
                # res = self.move_LIN(open_bag[2],True,ITEM_BASE) # 提起袋子
                # res = self.slow_move_PTP(open_bag[3],False) # 移到卡扣上方1
                # res = self.move_LIN(open_bag[4],False) # 降低至卡扣前
                # res = self.move_LIN(open_bag[5],False) # 進卡扣
                # res = self.move_LIN(open_bag[6],False) # 抬起打開紙袋
                # res = self.move_LIN(open_bag[7],False) # 放下一點
                # res = self.move_LIN(open_bag[8],False) # 抽出紙袋
                # res = self.move_PTP(open_bag[9]) # 到旋轉袋子位置
                # mm.motor_move(my_dxl,204)
                # time.sleep(1)
                # res = self.move_LIN(open_bag[10]) # 放下袋子
                # time.sleep(0.1)
                # res = self.put_item()
                # res = self.move_PTP(open_bag[11],False) # x減一點
                # res = self.move_PTP(open_bag[12],False) # z減一點
                # res = self.move_LIN(open_bag[13],False) # 往牆推
                # res = self.move_PTP(open_bag[14],False) # 歸位
                # res = self.move_PTP(open_bag[15]) # 歸位

                # 取物品=====================
                print("start order",i+1)
                # take tea
                for tea_num in range (0,forder[i][0]):
                    res = self.move_PTP(standby,False,ITEM_BASE)
                    mm.motor_move(my_dxl,525)
                    print("taketime.sleep(1) tea",tea_num+1)
                    # 拿取物品
                    res = self.move_PTP(tea[2],False,ITEM_BASE)
                    res = self.move_LIN(tea[1],True,ITEM_BASE)
                    res = self.take_item()
                    res = self.move_LIN(tea[2],False,ITEM_BASE)
                    res = self.slow_move_PTP(passby,False)
                    # 放置物品
                    res = self.slow_move_PTP(bag_mouth,False) # 移動到袋口
                    self.Stop
                    res = self.move_LIN(tea_enter[tea_num]) # 放下紅茶
                    res = self.put_item()
                    res = self.move_LIN(bag_mouth,False)# 回到袋口
                tea_used = tea_used + forder[i][0]

                # take puff========================================
                for puf_num in range (0,forder[i][1]):
                    res = self.move_PTP(standby,False,ITEM_BASE)
                    print("take puff",puf_num+1)
                    # 拿取物品
                    res = self.move_PTP(puf[0],False,ITEM_BASE)
                    mm.motor_move(my_dxl,254)
                    res = self.move_LIN(puf[1],True,ITEM_BASE)
                    res = self.take_item()
                    res = self.move_LIN(puf[2],False,ITEM_BASE)
                    # 放置物品
                    res = self.slow_move_PTP(puf[3],False) # 移動到袋口
                    res = self.slow_move_PTP(puf[4],False) # 移動到袋口
                    res = self.move_LIN(puf[5])
                    res = self.put_item()
                    res = self.move_LIN(puf[4],False)

                puf_used = puf_used + forder[i][1]
                # if forder[i][1] != 0:
                #     mm.motor_move(my_dxl,525)
                # else:
                #     pass

                # take egg
                for egg_num in range (0,forder[i][2]):
                    mm.motor_move(my_dxl,525)
                    res = self.move_PTP(standby,False,ITEM_BASE)
                    print("take egg",egg_num+1)
                    # 拿取物品
                    res = self.move_PTP(egg[0],False,ITEM_BASE)
                    res = self.move_LIN(egg[1],True,ITEM_BASE)
                    res = self.take_item()
                    res = self.move_LIN(egg[2],False,ITEM_BASE)
                    # 放置物品
                    res = self.slow_move_PTP(egg[3],False)# 移動到袋口
                    res = self.move_LIN(egg[4])# 放進袋子內
                    res = self.put_item()
                    res = self.move_LIN(bag_mouth,False)# 回到袋口
                egg_used =egg_used + forder[i][2]

                # 出貨=====================
                mm.motor_move(my_dxl,525)
                res = self.move_PTP(out_bag[0],False) # 到木板上方
                res = self.move_PTP(out_bag[1],False) # PTP下去
                res = self.move_LIN(out_bag[2]) # 碰木板
                res = self.take_item()
                res = self.move_LIN(out_bag[3],False) # 抬起一點
                res = self.move_PTP(out_bag[4],False) # 至推戴位置1
                res = self.move_PTP(out_bag[5],False) # 至推戴位置2
                res = self.move_LIN(out_bag[6][i],False) # 推袋子
                res = self.move_PTP(out_bag[6][i],False) # 回木板位置
                res = self.move_PTP(out_bag[7],False) # 放下木板
                res = self.move_LIN(out_bag[8]) # 回木板位置
                res = self.put_item()
                res = self.move_LIN(out_bag[9],False) # 回木板位置

            res = self.move_Angle(PHOTO_POSE)
            if res.arm_state == RobotCommand.Response.IDLE:
                nest_state = States.FINISH
            else:
                nest_state = None

        elif state == States.YOLO_DETECT:
            self.get_logger().info('YOLO_DETECT')
            res = self.call_yolo()
            # OBJECT_POSE here for example, should get obj pose according to yolo result
            if res.has_object:
                self.object_pose = res.object_pose
                nest_state = States.MOVE_TO_OPJECT_TOP
            else:
                nest_state = States.CHECK_POSE
                # nest_state = States.CLOSE_ROBOT

        elif state == States.MOVE_TO_OPJECT_TOP:
            self.get_logger().info('MOVE_TO_OPJECT_TOP')
            pose = Twist()
            [pose.linear.x, pose.linear.y, pose.linear.z] = self.object_pose[0:3]
            [pose.angular.x, pose.angular.y, pose.angular.z] = self.object_pose[3:6]
            # pose.linear.z += 10
            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.PTP,
                pose=pose)
            res = self.call_hiwin(req)
            if res.arm_state == RobotCommand.Response.IDLE:
                nest_state = States.PICK_OBJECT
            else:
                nest_state = None

        elif state == States.PICK_OBJECT:
            self.get_logger().info('PICK_OBJECT')
            pose = Twist()
            [pose.linear.x, pose.linear.y, pose.linear.z] = self.object_pose[0:3]
            [pose.angular.x, pose.angular.y, pose.angular.z] = self.object_pose[3:6]
            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.LINE,
                holding=False,
                velocity=5,
                pose=pose
            )
            res = self.call_hiwin(req)

            print(res)
            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.DIGITAL_OUTPUT,
                digital_output_cmd=RobotCommand.Request.DIGITAL_ON,
                digital_output_pin=VACUUM_PIN,
                holding=False,
                pose=pose
            )
            res = self.call_hiwin(req)

            pose.linear.z -= 30
            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.LINE,
                pose=pose
            )
            res = self.call_hiwin(req)

            if res.arm_state == RobotCommand.Response.IDLE:
                nest_state = States.MOVE_TO_PLACE_POSE
            else:
                nest_state = None

        elif state == States.MOVE_TO_PLACE_POSE:
            self.get_logger().info('MOVE_TO_PLACE_POSE')
            pose = Twist()
            req = self.generate_robot_request(
                cmd_type=RobotCommand.Request.JOINTS_CMD,
                joints=PLACE_POSE,
                pose=pose)
            res = self.call_hiwin(req)

            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.DIGITAL_OUTPUT,
                digital_output_cmd=RobotCommand.Request.DIGITAL_OFF,
                digital_output_pin=VACUUM_PIN,
                holding=True,
                pose=pose
            )
            res = self.call_hiwin(req)
            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.WAITING
            )
            res = self.call_hiwin(req)
            if res.arm_state == RobotCommand.Response.IDLE:
                nest_state = States.MOVE_TO_PHOTO_POSE
            else:
                nest_state = None

        elif state == States.CHECK_POSE:
            self.get_logger().info('CHECK_POSE')
            req = self.generate_robot_request(
                cmd_mode=RobotCommand.Request.CHECK_JOINT)
            res = self.call_hiwin(req)
            print(res.current_position)
            if res.arm_state == RobotCommand.Response.IDLE:
                nest_state = States.CLOSE_ROBOT
            else:
                nest_state = None

        elif state == States.CLOSE_ROBOT:
            self.get_logger().info('CLOSE_ROBOT')
            req = self.generate_robot_request(cmd_mode=RobotCommand.Request.CLOSE)
            res = self.call_hiwin(req)
            nest_state = States.FINISH

        else:
            nest_state = None
            self.get_logger().error('Input state not supported!')
            # return
        return nest_state

    def _main_loop(self):
        state = States.INIT
        while state != States.FINISH:
            state = self._state_machine(state)
            if state == None:
                break
        self.destroy_node()

    def _wait_for_future_done(self, future: Future, timeout=-1):
        time_start = time.time()
        while not future.done():
            time.sleep(0.01)
            if timeout > 0 and time.time() - time_start > timeout:
                self.get_logger().error('Wait for service timeout!')
                return False
        return True

    def generate_robot_request(
            self,
            holding=True,
            cmd_mode=RobotCommand.Request.PTP,
            cmd_type=RobotCommand.Request.POSE_CMD,
            velocity=DEFAULT_VELOCITY,
            acceleration=DEFAULT_ACCELERATION,
            tool=1,
            base=0,
            digital_input_pin=START_BUTTON,
            digital_output_pin=0,
            digital_output_cmd=RobotCommand.Request.DIGITAL_OFF,
            pose=Twist(),
            joints=[float('inf')]*6,
            circ_s=[],
            circ_end=[],
            jog_joint=6,
            jog_dir=0
            ):
        request = RobotCommand.Request()
        request.digital_input_pin = digital_input_pin
        request.digital_output_pin = digital_output_pin
        request.digital_output_cmd = digital_output_cmd
        request.acceleration = acceleration
        request.jog_joint = jog_joint
        request.velocity = velocity
        request.tool = tool
        request.base = base
        request.cmd_mode = cmd_mode
        request.cmd_type = cmd_type
        request.circ_end = circ_end
        request.jog_dir = jog_dir
        request.holding = holding
        request.joints = joints
        request.circ_s = circ_s
        request.pose = pose
        return request

    def call_hiwin(self, req):
        while not self.hiwin_client.wait_for_service(timeout_sec=2.0):
            self.get_logger().info('service not available, waiting again...')
        future = self.hiwin_client.call_async(req)
        if self._wait_for_future_done(future):
            res = future.result()
        else:
            res = None
        return res

    def call_yolo(self):
        class YoloResponse(NamedTuple):
            has_object: bool
            object_pose: list
        has_object = True if self.object_cnt < 5 else False
        object_pose = OBJECT_POSE
        res = YoloResponse(has_object,object_pose)
        # res.has_object = True if self.object_cnt < 5 else False
        # res.object_pose = OBJECT_POSE
        self.object_cnt += 1
        return res

    def start_main_loop_thread(self):
        self.main_loop_thread = Thread(target=self._main_loop)
        self.main_loop_thread.daemon = True
        self.main_loop_thread.start()

def main(args=None):
    rclpy.init(args=args)

    stratery = ExampleStrategy()
    stratery.start_main_loop_thread()

    rclpy.spin(stratery)
    rclpy.shutdown()

if __name__ == "__main__":
    main()


#!/usr/bin/python3
def print_matrix_integer(matrix=[[]]):
    if len(matrix[0]) != 0:
        column_length = len(matrix[0])
        row_length = len(matrix)
        for row in range(row_length):
            for column in range(column_length):
                print("{:d}".format(matrix[row][column]), end="")
                if column < (column_length - 1):
                    print(" ", end="")
                else:
                    print()
    else:
        print()


# -*- coding: utf-8 -*-
"""
Created on Sat Apr 10 15:11:49 2021

@author: Vishal Shah
"""

import yfinance as yf
import time
import streamlit as st
import plotly.graph_objs as go
import numpy as np
import pandas as pd
import statistics

st.set_page_config(
    page_title="Stock Analysis App",
    page_icon="ðŸ¤‘",
    layout="wide",
    initial_sidebar_state="expanded",
)

st.title('STOCK VOLATILITY')

st.sidebar.image('https://media.captrust.com/wordpress-content/2019/03/volatility.jpg', width=290)
st.sidebar.write('')

st.sidebar.info(
        "**Volatility is a measure of risk and shows how values are spread out around the average price. A stock with a price that fluctuates wildly hits new highs and lows or moves erratically, is taken under consideration as highly volatile. A highly volatile stock is inherently riskier, but that risk cuts both ways.**"
    )

months = st.selectbox(
    'SELECT NUMBER OF MONTHS',
    ('1','2', '3', '4', '5', '6', '7','8','9', '10', '11', '12', '13', '14')
)

tf = months + 'mo'
#st.write(periods)

st.markdown(
    """
    <style>
    .selector-rect {
        fill: grey  !important;
    }
    p{
    color: black;
    text-align: justify;
    }
    img{
    margin-top: 10px;
    margin-left: 7px;
    }
    div.row-widget.stRadio > div[role='radiogroup'] > label[data-baseweb='radio'] > div:first-child{
    background-color: #5e829f;
    }
    .st-av{
    background-color: #5e829f ;
    }
    .st-ch {
    background-color: #5e829f;
    }
    .st-cc{
       font-size: 17px;
       font-weight: 510;
       }
    .css-vfskoc{
    font-size: 20px;
    color: white;
    font-weight: 900;
    }
    .st-bj {
    margin-top: 11px;
    }
    #MainMenu {visibility : hidden;}
    footer {visibility : hidden;}
    footer:after {
    font-size: 15px;
    content: 'Made By: Namrata Patel, Bansi Shah, Darshi Shah, Vishal Shah';
    visibility: visible;
    position: relative;
    display: block;
    background-color: #5e829f;
    color: white;
    text-align: center;
    }
    h3{
    font-size: 18px;
    font-weight: 700;
    color: #2cab18;
    }
    h1{
      text-align: center;}
    </style>
    """,
    unsafe_allow_html=True
)

data_apple = yf.download(tickers='AAPL', period=tf, interval="1d")
data_google = yf.download(tickers='GOOG', period=tf, interval="1d")
data_microsoft = yf.download(tickers='MSFT', period=tf, interval="1d")
data_tesla = yf.download(tickers='TSLA', period=tf, interval="1d")
data_accenture = yf.download(tickers='ACN', period=tf, interval="1d")
data_ibm = yf.download(tickers='IBM', period=tf, interval="1d")
var_apple = statistics.pstdev(data_apple['Close'])
var_google = statistics.pstdev(data_google['Close'])
var_microsoft = statistics.pstdev(data_microsoft['Close'])
var_tesla = statistics.pstdev(data_tesla['Close'])
var_accenture = statistics.pstdev(data_accenture['Close'])
var_ibm = statistics.pstdev(data_ibm['Close'])
arr = [['Apple', var_apple], ['Google', var_google], ['Microsoft', var_microsoft], ['Tesla', var_tesla], ['Accenture', var_accenture], ['IBM', var_ibm]]
for i in range(6):
    for j in range(0, 6-i-1):
        if arr[j][1] > arr[j+1][1] :
          arr[j], arr[j+1] = arr[j+1], arr[j]

col1, col2, col3 = st.beta_columns((7,1,7))

x_values = ['Apple', 'Google', 'Microsoft', 'Tesla', 'IBM', 'Accenture']
y_values = [var_apple, var_google, var_microsoft, var_tesla, var_ibm, var_accenture]

fig = go.Figure(data=[go.Bar(
    x=x_values,
    y=y_values,
    marker={'color': 'darkorange'},
    )
])
fig.update_layout(
        barmode='group',
        width=550,
        margin=dict(l=0),
        plot_bgcolor='darkgrey')
col1.plotly_chart(fig)

v = np.array(arr)

df = pd.DataFrame()

df["Stock"], df["Volatility"] = v.T

#st.write(data_google)

fig = go.Figure(data=[go.Table(
    header=dict(values=['Stock', 'Volatility'],
                line_color='darkslategray',
                fill_color='#085089',
                align='center',
                font=dict(color='black', size=25),
                height=40),
    cells=dict(values=[df["Stock"],
                      df["Volatility"]],
                line_color='darkslategray',
                fill=dict(color=['#b7d3e9', '#b7d3e9']),
                align='center',
                font=dict(color='black', size=20),
                height=35))
])

fig.update_layout(

        width=550,
        margin=dict(l=0))

col3.plotly_chart(fig)


#!/usr/bin/env python3
import os

import click
import sys
import shelve
import operator

import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.lines as mlines

from math import ceil
from numpy import sqrt
from collections import Counter, OrderedDict
from topotool.graphs import Graph
from copy import deepcopy
from copy import Error
from jinja2 import Template
from itertools import chain

MAX_REPL_AGREEMENTS = 4
MIN_REPL_AGREEMENTS = 2

GRAPH_DATA = "graph_input_data"
GRAPH_OBJECT = "graph_object"
GRAPH_NX = "graph_networkx"
MASTER = "master_node"

DB_KEYS = [
    GRAPH_DATA,
    GRAPH_OBJECT,
    GRAPH_NX,
    MASTER,
]

SCALING_DEFAULTS = os.path.join(os.path.dirname(__file__), "./data/")

def load_context(ctx, storage):
    ctx.ensure_object(dict)
    if not ctx.obj:
        for db_key in DB_KEYS:
            try:
                with shelve.open(storage) as db:
                    ctx.obj[db_key] = db[db_key]
            except KeyError:
                pass

    return ctx

def save_data(path, data):
    """
    Writes data with file.write() to file specified by path
    if path is not specified use stdout
    """
    with open(path, "w") as data_file:
        data_file.write(data)

def load_jinja_template(path):
    with open(path) as file_:
        template = Template(file_.read())
    return template

def gen_metadata(
    topo_nodes,
    node_os,
    destination,
    project,
    run,
    job,
    template,
    tool_repo,
    tool_branch,
):
    metadata = template.render(
        topo_nodes=topo_nodes,
        node_os=node_os,
        project=project,
        run=run,
        job=job,
        tool_repo=tool_repo,
        tool_branch=tool_branch,
    )
    output_file = os.path.join(destination, "scaling_metadata.yaml")
    save_data(output_file, metadata)

def circle_topology(node_cnt, master="y0"):
    """Experimental approach we need to half the topology"""
    nodes = []
    for i in range(node_cnt):
        # FIXME master is not y0 ?
        nodes.append(f"{master[0]}{i}")

    G = nx.cycle_graph(nodes)

    # this connects the most distant node

    res = nx.single_source_dijkstra_path_length(G, master)

    max_dct = {"dist": 0, "node": master}

    for node, distance in res.items():
        # print("Node", node, "distance from master", distance)
        if distance > max_dct["dist"]:
            max_dct["dist"] = distance
            max_dct["node"] = node
        # print(max_dct["dist"])

    G.add_edge(max_dct["node"], master)

    degrees = dict(G.degree())
    sum_of_edges = sum(degrees.values())
    avg_degree = sum_of_edges / len(G)

    # print(max_dct["dist"], max_dct["node"])

    # use distance that is the most distant node
    #     and half the index for next node
    # eg. first is circle of 10 then 0-5
    # dist is 5
    # we take 5/2 which is 2.5
    # we round it,
    # connect 3 with 3+max dst

    # produce_output_image(G)

    node_to_back = []
    node_master = []
    master_found = False
    for node in list(G):
        if node == master:
            master_found = True

        if master_found:
            node_master.append(node)
        else:
            node_to_back.append(node)

    node_list = node_master + node_to_back

    possible_edges = []
    act_index = max_dct["dist"] / 2
    # dist = max_dct["dist"]
    # print(node_list)
    # print(dist / 2)

    while act_index > 1:
        idx = ceil(act_index)
        indexes = [i for i in range(idx, ceil(len(node_list) / 2), idx)]

        for node_pos in indexes:

            possible_edges.append(
                (node_list[node_pos], node_list[node_pos + max_dct["dist"]])
            )

        # print("RES", possible_edges)
        act_index = act_index / 2

    # to have more symmetric result we reverse order of edges
    possible_edges.reverse()
    # print(possible_edges)

    # 2.4 works fine with 10 nodes but we use 2.6
    while possible_edges and avg_degree < 2.6:
        next_edge = possible_edges.pop()
        G.add_edge(*next_edge)
        degrees = dict(G.degree())
        sum_of_edges = sum(degrees.values())
        avg_degree = sum_of_edges / len(G)
        # produce_output_image(G)

    return G

def create_level(level, max_levels, level_width):
    level_base = f"y{level}"
    # if the actual level is first or last return only level base (one server)
    if level == max_levels - 1 or level == 0:
        return [level_base]

    level = []
    # otherwise create level with number of servers equal to max_level_width
    for pos in range(level_width):
        level.append(f"{level_base}x{pos}")

    return level

def create_basic_topo(max_width, max_levels):
    topo = {
        "levels": {},
        "predecessors": {},
        "edges": [],
        "backbone_edges": [],
    }

    predecessors = {}
    for level in range(max_levels):
        topo["levels"][level] = create_level(level, max_levels, max_width)
        pred_idx = 0
        if level:
            for replica in topo["levels"][level]:
                predecessor = topo["levels"][level - 1][pred_idx]
                predecessors[replica] = predecessor

                if level != max_levels - 1:
                    topo["edges"] = topo["edges"] + [(predecessor, replica)]
                else:  # if max level then add all from previous level as pred
                    topo["edges"] = topo["edges"] + [
                        (pred, replica) for pred in topo["levels"][level - 1]
                    ]

                if level - 1 != 0 and level != max_levels - 1:
                    pred_idx += 1

    topo["backbone_edges"] = [(value, key) for key, value in predecessors.items()]
    topo["predecessors"] = predecessors

    return topo

@click.group(chain=True)
@click.option(
    "-s",
    "--storage",
    default="./.graph_storage.db",
    help="Location of the context db file",
    show_default=True,
)
@click.pass_context
def graphcli(ctx, storage):
    """
    topotool - a tool for tweaking and deploying FreeIPA replication topology
    """
    ctx = load_context(ctx, storage)

def levels_from_fist_successors(successors):
    return [[successors[0][0]], list(successors[0][1])]

def predecessors_from_first_levels(levels):
    res = {}
    for node in levels[-1]:
        res[node] = levels[0][0]
    return res

@graphcli.command()
@click.argument("input_file")
@click.option(
    "-m", "--master", default="y0", help="Specify a master node", show_default=True
)
@click.option(
    "-s",
    "--storage",
    default="./.graph_storage.db",
    help="Change a location for db file (to use different topology use load command)",
    show_default=True,
)
@click.option(
    "-t",
    "--type",
    "data_format",
    default="ipa",
    help="Switch between input file formats",
    show_default=True,
    type=click.Choice(["ipa", "edges"]),
)
@click.pass_context
def load(ctx, input_file, data_format, master, storage):
    """Loads graph data to context dictionary"""
    ctx.ensure_object(dict)

    with open(input_file, "r") as f:
        ctx.obj[GRAPH_DATA] = f.read().splitlines()

    data = deepcopy(ctx.obj[GRAPH_DATA])
    try:
        graph_data = Graph(data, data_format=data_format)
    except ValueError:
        sys.stderr.write(
            "Loading topology from file failed, please check the input file\n"
        )
        sys.exit(1)
    except Error as e:
        sys.stderr.write(f"Input error: {e}\n")
        sys.exit(1)
    except Exception:
        sys.stderr.write("Unexpected input file error\n")
        sys.exit(9)

    ctx.obj[GRAPH_OBJECT] = graph_data

    G = nx.Graph()
    G.name = f"Loaded graph of topology from {input_file}"
    for vertex in graph_data.vertices:
        G.add_node(vertex)

    if master not in G:
        sys.stderr.write(
            f"Loaded topology does not contain {master} master node,"
            " please specify master node using option `--master`\n"
        )
        sys.exit(1)

    G.add_edges_from(graph_data.edge_list)

    ctx.obj[GRAPH_NX] = G
    ctx.obj[MASTER] = master

    if nx.number_connected_components(G) != 1:
        sys.stderr.write(
            "Loaded topology is disconnected, please "
            "check your topology and load it again.\n"
        )
        sys.exit(1)

    with shelve.open(storage) as db:
        for key in ctx.obj:
            db[key] = ctx.obj[key]

    print("Topology has been loaded to shelve storage")

@graphcli.command()
@click.option(
    "-s",
    "--storage",
    default="./.graph_storage.db",
    help="Change a location for db file (to use different topology use load command)",
    show_default=True,
)
@click.option(
    "--branches",
    "-x",
    type=click.IntRange(3, 20),
    help="Topology branches <3-20> (x-axis) ignored when option '--nodes' is used",
)
@click.option(
    "--length",
    "-y",
    type=click.IntRange(3, 20),
    help="Topology length <3-20> (y-axis) ignored when option '--nodes' is used",
)
@click.option(
    "--nodes",
    "-n",
    type=click.IntRange(4, 60),
    help="Needed number of topology nodes <3-60> (server count)",
)
@click.option(
    "--master", "-m", default="y0", help="Master node name", show_default=True
)
@click.pass_context
def generate(ctx, storage, branches, length, nodes, master):
    """
    Generate one of two topology graphs based on given options

    --nodes and pair --branches --length trigger generation of the
    different type topologies (for more information see the documentation)
    """
    topo = {}
    if nodes:
        if branches or length:
            print(
                "Info: option --nodes has been passed, ignoring options "
                "--branches and --length"
            )
        G = circle_topology(nodes, master)
    else:
        if not branches:
            branches = 3
        if not length:
            length = 3
        topo = create_basic_topo(max_width=branches, max_levels=length)

        G = nx.Graph()
        G.add_edges_from(topo["edges"])

    if master not in G:
        sys.stderr.write(
            f"Generated topology does not contain {master} master node,"
            " please specify correct master node using option `--master`\n"
        )
        sys.exit(1)

    ctx.obj[GRAPH_NX] = G
    ctx.obj[GRAPH_DATA] = None
    ctx.obj[MASTER] = master

    with shelve.open(storage) as db:
        for key in ctx.obj:
            db[key] = ctx.obj[key]

    print("Topology has been generated to shelve storage")

def produce_backbone_image(G, backbone_edges, levels, circular=False, filename=None):
    G = deepcopy(G)

    plt.close()

    if not circular:
        pos = nx.spring_layout(G, k=0.3 * 1 / sqrt(len(G.nodes())), iterations=150)
    else:
        pos = nx.circular_layout(G)

    # https://stackoverflow.com/questions/50453043/networkx-drawing-label-partially-outside-the-box
    plt.figure(figsize=(11, 7))
    x_values, _y_values = zip(*pos.values())
    x_max = max(x_values)
    x_min = min(x_values)
    x_margin = (x_max - x_min) * 0.2
    plt.xlim(x_min - x_margin, x_max + x_margin)

    for edge in G.edges:
        G.remove_edge(*edge)
        if edge in backbone_edges:
            G.add_edge(*edge, color="magenta")
        else:
            G.add_edge(*edge, color="cyan")

    fixed_colors = [
        "#66FF00",
        "#1974D2",
        "#00CC99",
        "#E68FAC",
        "#DFFF00",
        "#00B7EB",
        "#D891EF",
        "#C32148",
        "#58427C",
        "#FFD300",
        "#F56FA1",
        "#666699",
        "#D4AF37",
        "#6F00FF",
        "#BF00FF",
        "#CCFF00",
        "#85754E",
        "#B2EC5D",
        "#ED2939",
        "#4C516D",
        "#FFD700",
    ]

    draw_edges = list(G.edges)

    for edge in G.edges:
        draw_edges.append((edge[1], edge[0]))

    DiG = nx.DiGraph(draw_edges)

    for level in levels:
        new_node_color = fixed_colors[level]
        nx.draw_networkx_nodes(
            G,
            pos,
            nodelist=levels[level],
            node_color=new_node_color,
            node_size=700,
            alpha=0.85,
        )

    colors = [G[u][v]["color"] for u, v in DiG.edges()]

    nx.draw_networkx_edges(
        DiG,
        pos,
        edge_color=colors,
        edgelist=DiG.edges,
        width=3.0,
        arrows=True,
        arrowstyle="-|>",
        arrowsize=20,
    )  # this HAX (DiGraph) is needed for arrows to be in drawings

    labels = {}

    for node in G:
        labels[node] = node

    nx.draw_networkx_labels(G, pos, labels)

    patches = []
    for level in levels:
        label = f"Level {level} replica" if level else "FreeIPA master"
        patches.append(
            mlines.Line2D(
                [0],
                [0],
                marker="o",
                alpha=0.8,
                markersize=15,
                color="w",
                markerfacecolor=fixed_colors[level],
                label=label,
                # https://matplotlib.org/gallery/text_labels_and_annotations/custom_legends.html#sphx-glr-gallery-text-labels-and-annotations-custom-legends-py
            )
        )

    patches.append(
        mlines.Line2D(
            [0],
            [0],
            linewidth=4.0,
            alpha=0.8,
            color="magenta",
            label="Backbone replication agreement",
        )
    )

    patches.append(
        mlines.Line2D(
            [0],
            [0],
            linewidth=4.0,
            alpha=0.8,
            color="cyan",
            label="Additional replication agreement",
        )
    )

    plt.legend(handles=patches)

    plt.axis("off")
    # plt.show()
    plt.savefig(os.path.abspath(filename), dpi=100)

def produce_output_image(G, filename=None, circular=False):
    plt.close()
    art_points = list(nx.articulation_points(G))

    draw_edges = list(G.edges)

    for edge in G.edges:
        draw_edges.append((edge[1], edge[0]))

    # this HAX (DiGraph) is needed for arrows to be in drawings
    DiG = nx.DiGraph(draw_edges)

    # https://stackoverflow.com/questions/49121491/issue-with-spacing-nodes-in-networkx-graph
    if not circular:
        pos = nx.spring_layout(G, k=0.3 * 1 / sqrt(len(G.nodes())), iterations=150)
    else:
        pos = nx.circular_layout(G)

    # https://stackoverflow.com/questions/50453043/networkx-drawing-label-partially-outside-the-box
    plt.figure(figsize=(11, 7))
    x_values, _y_values = zip(*pos.values())
    x_max = max(x_values)
    x_min = min(x_values)
    x_margin = (x_max - x_min) * 0.2
    plt.xlim(x_min - x_margin, x_max + x_margin)

    plot_nodes = {
        "Complying replicas": {"nodes": [], "color": "green"},
        "One replication agreement": {"nodes": [], "color": "pink"},
        "Articulation point": {"nodes": [], "color": "yellow"},
        "Overloaded replica": {"nodes": [], "color": "red"},
        "Overloaded articulation point": {"nodes": [], "color": "orange"},
    }

    for node in G:
        node_degree = nx.degree(G, node)
        if node in art_points and node_degree > 4:
            plot_nodes["Overloaded articulation point"]["nodes"].append(node)
        elif node in art_points:
            plot_nodes["Articulation point"]["nodes"].append(node)
        elif node_degree > 4:
            plot_nodes["Overloaded replica"]["nodes"].append(node)
        elif node_degree == 1 and not len(G) == 2:
            plot_nodes["One replication agreement"]["nodes"].append(node)
        else:
            plot_nodes["Complying replicas"]["nodes"].append(node)

    for node_type in plot_nodes:
        nx.draw_networkx_nodes(
            G,
            pos,
            nodelist=plot_nodes[node_type]["nodes"],
            node_color=plot_nodes[node_type]["color"],
            node_size=700,
            alpha=0.85,
        )

    # set colors for the edges to draw
    colors = []
    for u, v in DiG.edges():
        try:
            colors.append(G[u][v]["color"])
        except KeyError:
            colors.append("#ff7f0e")  # orange from FreeIPA

    nx.draw_networkx_edges(
        DiG,
        pos,
        edge_color=colors,
        edgelist=DiG.edges,
        width=3.0,
        arrows=True,
        arrowstyle="-|>",
        arrowsize=20,
    )

    labels = {}

    for node in G:
        labels[node] = node

    nx.draw_networkx_labels(G, pos, labels)

    patches = []
    for node_type in plot_nodes:
        patches.append(
            mlines.Line2D(
                [0],
                [0],
                marker="o",
                alpha=0.8,
                markersize=15,
                color="w",
                markerfacecolor=plot_nodes[node_type]["color"],
                label=node_type,
                # https://matplotlib.org/gallery/text_labels_and_annotations/custom_legends.html#sphx-glr-gallery-text-labels-and-annotations-custom-legends-py
            )
        )

    patches.append(
        mlines.Line2D(
            [0],
            [0],
            linewidth=4.0,
            alpha=0.8,
            color="#90ee90",
            label="Added replication agreement",
        )
    )

    patches.append(
        mlines.Line2D(
            [0],
            [0],
            linewidth=4.0,
            alpha=0.8,
            color="#0277bd",
            label="Forced replication agreement",
        )
    )

    patches.append(
        mlines.Line2D(
            [0],
            [0],
            linewidth=4.0,
            alpha=0.8,
            color="#ff7f0e",
            label="Original replication agreement",
        )
    )

    loc = 10 if circular else 0

    plt.legend(loc=loc, handles=patches)
    plt.axis("off")

    if filename:
        plt.savefig(os.path.abspath(filename), dpi=100)
    else:
        plt.show()

@graphcli.command()
@click.option(
    "-s",
    "--storage",
    default="./.graph_storage.db",
    help="Change a location for db file (to use different topology use load command)",
    show_default=True,
)
@click.option(
    "-i",
    "--interactive",
    is_flag=True,
    help="Open a mathplotlib window with the topology graph",
)
@click.option(
    "-c", "--circular", is_flag=True, help="Force the nodes figure to circle shape",
)
@click.option(
    "-f",
    "--filename",
    help="Change path of saving the file. [default: ./topology_drawing.png]",
)
@click.pass_context
def draw(ctx, storage, interactive, filename, circular):
    """Create a topology graph picture"""
    ctx = load_context(ctx, storage)

    try:
        G = ctx.obj[GRAPH_NX]
    except KeyError:
        sys.stderr.write("Please load or generate the topology first\n")
        sys.exit(1)

    if interactive:
        produce_output_image(G, circular=circular)

    if not interactive and not filename:
        produce_output_image(G, filename="topology_drawing.png", circular=circular)
        print("Image stored to file topology_drawing.png")

    if filename:
        produce_output_image(G, filename=filename, circular=circular)
        print(f"Image stored to file {filename}")

def get_segments(edges):
    segments = {}
    nodes = list(chain(*edges))
    most_common_nodes = Counter(nodes).most_common()
    node_iter = iter(most_common_nodes)
    processed = set()
    not_processed = set(edges)

    while not_processed:
        left = set()
        right = set()
        node, _ = next(node_iter)
        for a, b in not_processed:
            if node == a:
                left.add(b)
                processed.add((a, b))
            elif node == b:
                right.add(a)
                processed.add((a, b))

        not_processed -= processed

        if right.union(left):
            segments.update({node: right.union(left)})

    return segments

def print_topology(topology):
    # in general the topology second level (y1x*) should be there always
    topo_width = len(topology[1])
    for level in topology:
        if len(topology[level]) == 1:
            ws = "".join(["\t" for _ in range(int(topo_width / 2))])
            line = f"{ws}{topology[level][0]}"
        else:
            line = "\t".join(topology[level])

        print(line)

@graphcli.command()
@click.option(
    "-s",
    "--storage",
    default="./.graph_storage.db",
    help="Change a location for db file (to use different topology use load command)",
    show_default=True,
)
@click.option(
    "-j",
    "--jenkins-template",
    type=click.Path(exists=True),
    default=os.path.join(SCALING_DEFAULTS, "jenkinsjob.j2"),
    help="Jenkinsfile jinja2 template",
    show_default=True,
)
@click.option(
    "--base-metadata",
    type=click.Path(exists=True),
    default=os.path.join(SCALING_DEFAULTS, "metadata_template.j2"),
    help="Base metadata jinja2 template",
    show_default=True,
)
@click.option(
    "--inventory",
    type=click.Path(exists=True),
    default=os.path.join(SCALING_DEFAULTS, "inventory_template.j2"),
    help="Base inventory jinja2 template",
    show_default=True,
)
@click.option(
    "--ansible-install",
    type=click.Path(exists=True),
    default=os.path.join(SCALING_DEFAULTS, "install_primary_replicas.j2"),
    help="Ansible-freeipa install jinja2 template",
    show_default=True,
)
@click.option(
    "--out-dir",
    "-o",
    type=str,
    default="DEPLOYMENT_FILES",
    help="Output directory to store generated data",
)
@click.option(
    "--metadata-storage",
    type=str,
    default="idm-artifacts.usersys.redhat.com",
    help="Metadata storage server",
)
@click.option(
    "--idm-ci",
    type=str,
    default="https://gitlab.cee.redhat.com/identity-management/idm-ci.git",  # FIXME
    help="IDM-CI repo",
)
@click.option("--repo-branch", type=str, default="master", help="IDM-CI repo branch")
@click.option(
    "--tool-repo",
    type=str,
    default=(
        "https://gitlab.cee.redhat.com/identity-management/idm-performance-testing.git"
    ),  # FIXME
    help="SCALE tool repo",
)
@click.option(
    "--tool-branch", type=str, default="master", help="SCALE tool repo branch"
)
@click.option("--node-os", type=str, default="fedora-33", help="Host operating system")
@click.option(
    "--project",
    "-p",
    type=str,
    default="trigger_performance_scale/large-scale",
    help="Pipeline project for storing data",
)
@click.option(
    "--run", "-r", type=str, default="RUNNING", help="Pipeline run for storing data"
)
@click.option(
    "--job", "-j", type=str, default="JOB", help="Pipeline job for storing data"
)
@click.option("--freeipa-upstream-copr", type=str, help="freeipa-upstream-copr")
@click.option("--freeipa-downstream-copr", type=str, help="freeipa-downstream-copr")
@click.option("--freeipa-custom-repo", type=str, help="freeipa-custom-repo")
@click.option(
    "--ansible-freeipa-upstream-copr", type=str, help="ansible-freeipa-upstream-copr"
)
@click.option(
    "--ansible-freeipa-downstream-copr",
    type=str,
    help="ansible-freeipa-downstream-copr",
)
@click.option(
    "--ansible-freeipa-custom-repo", type=str, help="ansible-freeipa-custom-repo"
)
@click.option(
    "-c", "--circular", is_flag=True, help="Force the nodes figure to circle shape",
)
@click.pass_context
def deployment(
    ctx,
    jenkins_template,
    out_dir,
    storage,
    node_os,
    idm_ci,
    repo_branch,
    tool_repo,
    tool_branch,
    project,
    run,
    job,
    metadata_storage,
    base_metadata,
    inventory,
    ansible_install,
    freeipa_upstream_copr,
    freeipa_downstream_copr,
    freeipa_custom_repo,
    ansible_freeipa_upstream_copr,
    ansible_freeipa_downstream_copr,
    ansible_freeipa_custom_repo,
    circular,
):
    """
    Generate deployment files for the Jenkins automation
    """
    ctx = load_context(ctx, storage)

    try:
        G = ctx.obj[GRAPH_NX]
        master = ctx.obj[MASTER]
    except KeyError:
        sys.stderr.write("Please load or generate the topology first\n")
        sys.exit(1)

    all_successors = list(nx.bfs_successors(G, master))
    levels = levels_from_fist_successors(all_successors[:1])
    successors = all_successors[1:]

    new = []
    predecessors = predecessors_from_first_levels(levels)

    for s, f in successors:
        # print("------------------------------------")
        # print("s:", s, "f:", f, "level:", levels[-1])
        if s in levels[-1]:
            # print(s, f)
            new = new + f
        else:
            # print(s, f)
            levels.append(new)
            new = f
        for pred in f:
            predecessors[pred] = s

    if new:  # add level at the end of for cycle so last is added
        levels.append(new)

    key = 0
    levels_dict = {}
    for level in levels:
        levels_dict[key] = level
        key += 1

    backbone_edges = compatible_backbone_edges(G, master)
    levels = levels_dict

    print(f"Create temporary {out_dir} folder")
    os.makedirs(out_dir, exist_ok=True)

    print("Generate Jenkinsfile for whole topology")
    output_jenkins_job = os.path.join(out_dir, "Jenkinsfile")

    pipeline_template = load_jinja_template(jenkins_template)

    jenkinsfile = pipeline_template.render(
        levels=levels,
        node_os=node_os,
        idm_ci=idm_ci,
        repo_branch=repo_branch,
        metadata_storage=metadata_storage,
        project=project,
        run=run,
        job=job,
        freeipa_upstream_copr=freeipa_upstream_copr,
        freeipa_downstream_copr=freeipa_downstream_copr,
        freeipa_custom_repo=freeipa_custom_repo,
        ansible_freeipa_upstream_copr=ansible_freeipa_upstream_copr,
        ansible_freeipa_downstream_copr=ansible_freeipa_downstream_copr,
        ansible_freeipa_custom_repo=ansible_freeipa_custom_repo,
    )
    save_data(output_jenkins_job, jenkinsfile)

    print_topology(levels)  # to the terminal
    produce_backbone_image(
        G,
        backbone_edges,
        levels,
        circular=circular,
        filename=os.path.join(out_dir, "backbone_topology.png"),
    )
    # merge dictionary items to one list containing all nodes
    topo_nodes = list(chain(*[levels[level] for level in levels]))

    print("Generate metadata for topology nodes")
    # Load jinja template
    metadata_template = load_jinja_template(base_metadata)

    # Generate the metadata file for all nodes inside of FILES directory
    gen_metadata(
        topo_nodes,
        node_os,
        out_dir,
        project,
        run,
        job,
        metadata_template,
        tool_repo,
        tool_branch,
    )

    print("Generate ansible-freeipa inventory file")
    # Load jinja template
    job_inventory = load_jinja_template(inventory)

    # Generate ansible-freeipa inventory file
    output_job_inventory = os.path.join(out_dir, "perf-inventory")
    inventoryfile = job_inventory.render(
        master_server=topo_nodes[0], levels=levels, predecessors=predecessors
    )
    save_data(output_job_inventory, inventoryfile)

    print("Generate ansible-freeipa install file")
    missing_edges = set(G.edges) - set(backbone_edges)

    segments = get_segments(missing_edges)
    # Load jinja template
    ansible_install = load_jinja_template(ansible_install)
    # Generate ansible-freeipa install file
    output_install = os.path.join(out_dir, "perf-install.yml")
    installfile = ansible_install.render(levels=levels, missing=segments)
    save_data(output_install, installfile)

    produce_output_image(G, os.path.join(out_dir, "topology.png"))

def compatible_backbone_edges(G, master):
    """Return edges not based on direction of bfs walk"""
    all_edges = set(G.edges)
    b_edges = set(nx.bfs_edges(G, master))

    backbone_edges = set()
    for e in b_edges:
        if e in all_edges:
            backbone_edges.add(e)
        else:
            backbone_edges.add((e[1], e[0]))

    return backbone_edges

def add_list_item(record, item):
    if record is None:
        record = []

    record.append(item)
    return record

def sort_by_degree(G, nodes=None, reverse=False):
    sorted_nodes = OrderedDict()
    nodes_by_degree = {}

    if not nodes:
        nodes = G

    for node in nodes:
        cnt = nx.degree(G, node)
        try:
            record = nodes_by_degree[cnt]
        except KeyError:
            record = []

        nodes_by_degree[cnt] = add_list_item(record, node)

    if reverse:
        for degree in range(max(nodes_by_degree, key=int), 0, -1):
            try:
                sorted_nodes[degree] = sorted(nodes_by_degree[degree], reverse=reverse)
            except KeyError:
                pass
    else:
        for degree in range(max(nodes_by_degree, key=int) + 1):
            try:
                sorted_nodes[degree] = sorted(nodes_by_degree[degree], reverse=reverse)
            except KeyError:
                pass

    return sorted_nodes

@graphcli.command()
@click.option(
    "-s",
    "--storage",
    default="./.graph_storage.db",
    help="Change a location for db file (to use different topology use load command)",
    show_default=True,
)
@click.pass_context
def analyze(ctx, storage):
    """
    Print information about processed topology graph
    """
    ctx = load_context(ctx, storage)

    try:
        G = ctx.obj[GRAPH_NX]
        # master = ctx.obj[MASTER]
    except KeyError:
        sys.stderr.write("Please load or generate the topology first")
        sys.exit(1)

    print("----------------------------------------")
    print("General graph information:\n" + str(nx.info(G)))
    eccentricity = nx.eccentricity(G)
    print(
        "Maximum hop count distance from two random replicas:",
        max(eccentricity.items(), key=operator.itemgetter(1))[1],
    )
    print()
    print("Number of connected components:\t" + str(nx.number_connected_components(G)))
    print("----------------------------------------")
    print("Analyzing replicas as standalone entity")
    print("--------------------")

    issues = []
    for node in G:
        repl_agreements = nx.degree(G, node)

        if repl_agreements < 2:
            issues.append(f"One replication agreement\t| {node}")

        if repl_agreements > MAX_REPL_AGREEMENTS:
            issues.append(
                f"More than {MAX_REPL_AGREEMENTS} " f"replication agreements\t| {node}"
            )

    if issues and not len(G) == 2:
        print("ISSUE\t\t\t\t| REPLICA\n-----\t\t\t\t| ----")
        for issue in issues:
            print(issue)

    else:
        print("No issues found for the replicas")

    print("----------------------------------------")
    print("Analyzing topology connectivity")
    print("--------------------")
    print("Looking for articulation points")
    print(
        "Note: Optimal FreeIPA replication topology should "
        "not contain any articulation point"
    )

    art_points = sorted(list(nx.articulation_points(G)), reverse=True)
    components = list(nx.biconnected_components(G))

    if art_points or components:
        for art_point in art_points:
            print(f"Articulation point\t| {art_point}")

        print(f"Articulation point(s) found: {len(art_points)}")
        print("--------------------")
        print("Looking for biconnected components")
        print(
            "Note: Optimal FreeIPA replication topology should "
            "contain one biconnected component"
        )

        for comp in components:
            print(f"Biconected component: {comp}")

        print(f"Biconected component(s) found: {len(components)}")

    print("----------------------------------------")

def remove_articulation_points(G, step, omit_max, max_repl_agreements):

    # trying to remove articulation points
    added_edges = []
    can_not_add = []
    to_add = ""

    color = "#90ee90"  # default light green added edge color

    one_repl_agreement_replicas = []
    # we build list of candidates from 1 repl agreement nodes and connect them
    for node in G:
        if nx.degree(G, node) == 1:
            one_repl_agreement_replicas.append(node)

    # while there is more than 2 replicas to connect we connect them
    while len(one_repl_agreement_replicas) >= 2:
        left = one_repl_agreement_replicas.pop()
        right = one_repl_agreement_replicas.pop()
        edge_to_add = (left, right)
        step += 1
        G.add_edges_from([edge_to_add], color=color)
        produce_output_image(G, f"fixup_step_{step}_add_{left}_{right}.png")
        # produce_output_image(G)
        added_edges.append(edge_to_add)
        print("Adding replication agreement:", edge_to_add)

    # a case that only one replica has one repl agreement may occur after first fixup
    # however we will fix that with following algorithm

    all_components = list(nx.biconnected_components(G))

    while list(nx.articulation_points(G)):
        art_points = sorted(list(nx.articulation_points(G)), reverse=True)

        if len(list(nx.articulation_points(G))) == 0:
            # if there are no articulation points we stop
            sys.stdout.write("Info: Will not add more replication agreements\n")
            break

        # pick articulation point to solve issue for FIXME
        art_point = art_points.pop()
        color = "#90ee90"  # default light green added edge color

        connected_by_art_point = []
        remaining_components = []
        for comp in all_components:
            if art_point in comp:
                connected_by_art_point.append(comp)
            else:
                remaining_components.append(comp)

        candidates = []
        for comp in connected_by_art_point:
            sorted_nodes = sort_by_degree(G, nodes=comp, reverse=False)

            # get dictionary with keys corresponding to degrees and each key
            # will have a list of node with following degree as value
            sorted_nodes = sort_by_degree(G, nodes=comp, reverse=False)

            # we would choose a node with the fewest connection count
            # therefore the lowerest degree of the node

            to_pick_from = []
            # list with nodes, first in the list are with the lower degree
            for _degree, nodes in sorted_nodes.items():
                to_pick_from += nodes

            start = 0
            to_add = None
            while not to_add and (start < len(to_pick_from)):
                to_add = to_pick_from[start]
                start += 1

            # if the node is articulation point so it connets other biconnected
            # components we choose to connect lowest of the remaining nodes

            if to_add in art_points:
                remaining_nodes = list(tuple(chain(*(remaining_components))))
                # print(remaining_nodes)
                sorted_remaining = sort_by_degree(
                    G, nodes=remaining_nodes, reverse=False
                )
                new_pick = []
                for _degree, nodes in sorted_remaining.items():
                    new_pick += nodes

                for pick in new_pick:
                    if pick not in art_points:
                        to_add = pick

            min_degree = nx.degree(G, to_add)

            if min_degree < max_repl_agreements:
                candidates.append(to_add)
                color = "#90ee90" if len(candidates) > 2 else color
            else:
                added = False
                if omit_max:
                    candidates.append(to_add)
                    color = "#0277bd"  # blue edge color
                    added = True
                else:
                    can_not_add.append(to_add)

                added_str = "Added" if added else "Did not add"
                sys.stdout.write(
                    f"Warning: {added_str} replication agreement"
                    " to connect component "
                    f"({comp}) with articulation point {art_point} "
                    f"{to_add} has {max_repl_agreements} "
                    "or more replication agreements\n"
                )

                if not added and to_add in can_not_add:
                    sys.stdout.write(
                        "Hint: Try adding --omit-max option to add "
                        "replication agreement even when maximum "
                        " degree of node is reached\n"
                    )

            if to_add in can_not_add:
                neighs = G.neighbors(to_add)
                sys.stderr.write(
                    f"Unable to add more replication agreements to: {to_add}\n"
                )
                for nei in neighs:
                    sys.stderr.write(f"{to_add} has neighbor: {nei}\n")
                sys.exit(3)

        if len(candidates) >= 2:
            left = candidates[-2]
            right = candidates[-1]
            edge_to_add = (left, right)
            step += 1
            G.add_edges_from([edge_to_add], color=color)
            produce_output_image(G, f"fixup_step_{step}_add_{left}_{right}.png")
            # produce_output_image(G)
            added_edges.append(edge_to_add)
            print("Adding replication agreement:", edge_to_add)

        all_components = list(nx.biconnected_components(G))

    return G, added_edges, step

def remove_overloaded_nodes_edges(
    G, step, add_while_removing, omit_max, max_repl_agreements
):
    removed = []
    can_not_remove = []
    remove = set()
    added_edges = []  # used when add_while_removing is set to true

    sorted_nodes = sort_by_degree(G)
    max_degree = max(sorted_nodes, key=int)

    while max_degree > max_repl_agreements:
        # sort the G nodes by degree once!
        sorted_max_degree_nodes = iter(sorted_nodes[max_degree])

        # for max_degree_node in sorted_max_degree_nodes:
        max_degree_node = next(sorted_max_degree_nodes)

        if max_degree <= max_repl_agreements:
            sys.stdout.write("Info: Will not remove more replication agreements\n")
            break

        neighbors = sorted([n for n in G.neighbors(max_degree_node)])

        sort_neig_dict = sort_by_degree(G, nodes=neighbors)
        sort_neig = sorted(sort_neig_dict, reverse=True)

        remove = removed[-1] if removed else remove

        for neig in sort_neig_dict[sort_neig[0]]:
            to_remove = (max_degree_node, neig)

            if to_remove not in can_not_remove:
                remove = to_remove
                break

        if remove in can_not_remove:
            sys.stderr.write(
                "Error: Can not remove any replication agreement without "
                "creating articulation points\n"
            )
            sys.stderr.write(
                "Try to use option '--add-while-removing' to remove "
                "articulation points created by this removal\n"
            )
            sys.exit(4)

        if remove in removed:
            sys.stderr.write(
                f"Error: Replication agreement already removed - {remove}\n"
            )
            sys.exit(4)

        if remove:
            step += 1
            G.remove_edge(*remove)

        produce_output_image(
            G, filename=f"fixup_step_{step}_rm_{remove[0]}_{remove[1]}.png"
        )

        sorted_nodes = sort_by_degree(G)
        max_degree = max(sorted_nodes, key=int)

        removed.append(remove)
        print("Removing replication agreement:", remove)

        if len(list(nx.articulation_points(G))) == 0:
            # if there are no articulation points we continue removing
            continue
        else:
            sys.stdout.write(
                f"Warning: Removal of the {removed[-1]} replication "
                "agreement created articulation point\n"
            )
            if add_while_removing:
                sys.stdout.write("Info: Trying to fix new articulation points\n")
                G, added, step = remove_articulation_points(
                    G,
                    step=step,
                    omit_max=omit_max,
                    max_repl_agreements=max_repl_agreements,
                )
                added_edges += added

                for new in added:
                    if (new[0], new[1]) in removed or (new[1], new[0]) in removed:
                        sys.stderr.write(
                            f"Error Added replication agreement {new} has been"
                            f" found in already removed list: {removed}\n"
                        )
                        sys.exit(4)

            else:
                sys.stdout.write(
                    f"Info: Adding back replication agreement: {removed[-1]} \n"
                )
                G.add_edge(removed[-1][0], removed[-1][1])
                can_not_remove.append(removed[-1])
                sys.stdout.write(
                    "List of replication agreements "
                    f"which we can not remove: {can_not_remove}\n"
                )

    return G, removed, added_edges, step

@graphcli.command()
@click.option(
    "-m",
    "--max",
    "max_repl_agreements",
    default=MAX_REPL_AGREEMENTS,
    type=click.IntRange(2, 10),
    help="Change maximum number <2-10> of replication agreements per replica",
)
@click.option(
    "--omit-max",
    is_flag=True,
    default=False,
    help="Do not check the maximum number of the replication agreements"
    " while adding a new replication agreement to connect a topology",
)
@click.option(
    "--add-while-removing",
    is_flag=True,
    default=False,
    help="When removal of the replication agreement creates a articulation "
    "point add edge to fix the issue",
)
@click.option(
    "-s",
    "--storage",
    default="./.graph_storage.db",
    help="Change a location for db file (to use different topology use load command)",
    show_default=True,
)
@click.option(
    "--no-fqdn",
    is_flag=True,
    help="Change a templates from fqdn to non-fqdn",
    default=False,
)
@click.pass_context
def fixup(ctx, storage, max_repl_agreements, omit_max, add_while_removing, no_fqdn):
    """
    Generate an Ansible automation to remove topology weak spots
    """
    ctx = load_context(ctx, storage)

    try:
        G = ctx.obj[GRAPH_NX]
        master = ctx.obj[MASTER]
    except KeyError:
        sys.stderr.write("Please load or generate the topology first\n")
        sys.exit(1)

    print("========================================")

    step = 0
    produce_output_image(G, f"fixup_step_{step}_start.png")

    G, added_edges, step = remove_articulation_points(
        G, step=step, omit_max=omit_max, max_repl_agreements=max_repl_agreements,
    )

    G, removed, added_with_removal, step = remove_overloaded_nodes_edges(
        G,
        step=step,
        add_while_removing=add_while_removing,
        omit_max=omit_max,
        max_repl_agreements=max_repl_agreements,
    )

    added_edges += added_with_removal
    missing_segments = get_segments(added_edges)
    redundant_segments = get_segments(removed)

    print("Summary:")

    if missing_segments or redundant_segments:
        print(f"Added replication agreement(s) [{len(added_edges)}]:")
        for edge in added_edges:
            print(edge)

        print(f"Removed replication agreement(s) [{len(removed)}]:")
        for edge in removed:
            print(edge)

        print(
            "Replication agreement intersection:",
            set(added_edges).intersection(set(removed)),
        )
        print("----------------------------------------")
        print("Saving result graph image")
        produce_output_image(G, "fixup_result.png")
        print("----------------------------------------")
        print("Saving result graph data")
        # save new graph to context db
        ctx.obj[GRAPH_NX] = G
        with shelve.open(storage) as db:
            for key in ctx.obj:
                db[key] = ctx.obj[key]

        print("----------------------------------------")
        print("Generating fixup playbook")
        fqdn = "_fqdn" if not no_fqdn else ""
        fixup_playbook = load_jinja_template(
            os.path.join(SCALING_DEFAULTS, f"fixup_topology_segments{fqdn}.j2")
        )
        fixup = "fixup_result.yml"
        fixup_data = fixup_playbook.render(
            missing=missing_segments, redundant=redundant_segments,
        )
        save_data(fixup, fixup_data)

        print("Generating fixup inventory")
        fixup_inv = load_jinja_template(
            os.path.join(SCALING_DEFAULTS, f"fixup_inventory{fqdn}.j2")
        )
        inv = "fixup_result.inv"

        replicas = set(tuple(chain(*(removed + added_edges))))

        try:
            replicas.remove(master)
        except KeyError:
            pass  # when master node is not in list of replicas it is okay

        inv_data = fixup_inv.render(ipa_master=master, ipa_replicas=replicas,)

        save_data(inv, inv_data)
    else:
        print("Nothing to do")

    print("========================================")

if __name__ == "__main__":
    sys.exit(graphcli())


import cv2
import numpy as np
from os.path import join

if __name__=="__main__":
    root_dir = "/Users/sfhan/Dropbox/stereoRepoj/largeThreshold"

    img_file= join(root_dir, "right_frame130_16.png")
    img2_file = join(root_dir, "right_frame130_100.png")
    gt_file = join(root_dir, "gt_right_frame130.png")
    out_file = join(root_dir, "largethreshold.png")

    I1 = cv2.imread(img_file)
    I2 = cv2.imread(img2_file)
    gtI = cv2.imread(gt_file)

    x1, y1 = 356, 146
    x2, y2 = 580, 290
    width = x2- x1
    height = y2 - y1

    x3, y3 = 384, 163
    x4, y4 = 519, 246

    mark_I1 = cv2.rectangle(I1, (x3, y3), (x4, y4), color=(0, 255, 00), thickness=3)
    mark_I2 = cv2.rectangle(I2, (x3, y3), (x4, y4), color=(0, 255, 00), thickness=3)

    crop_I1 = mark_I1[y1:y2, x1:x2, :]
    crop_I2 = mark_I2[y1:y2, x1:x2, :]
    crop_gtI = gtI[y1:y2, x1:x2, :]

    outI = np.concatenate((crop_I1, crop_I2, crop_gtI), axis=1)
    cv2.imwrite(out_file, outI.astype(np.uint8))


from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time

class NcafeWriteAtt:
    # 초기화 실행(webdriver 설정)
    def __init__(self):
        # chrome_options = Options()
        # chrome_options.add_argument("--headless")
        # self.driver = webdriver.Chrome(chrome_options=chrome_options, executable_path='/Users/soo/PycharmProjects/crawling/driver/chrome/chromedriver')
        self.driver = webdriver.Chrome('/Users/soo/PycharmProjects/crawling/driver/chrome/chromedriver')
        self.driver.implicitly_wait(5)

        # 네이버 카페 로그인 및 출석 체크
    def writeAttendCheck(self):
        self.driver.get('https://nid.naver.com/nidlogin.login')
        self.driver.find_element_by_name('id').send_keys('suyohnee')
        self.driver.find_element_by_name('pw').send_keys('password')
        self.driver.find_element_by_xpath('//*[@id="frmNIDLogin"]/fieldset/input').click()
        self.driver.implicitly_wait(30)
        self.driver.get('https://cafe.naver.com/AttendanceView.nhn?search.clubid=25997591&search.menuid=56')
        self.driver.implicitly_wait(10)
        self.driver.switch_to_frame('cafe_main')
        self.driver.find_element_by_id('cmtinput').send_keys('출석합니다')
        self.driver.find_element_by_xpath('//*[@id="btn-submit-attendance"]').click()
        time.sleep(3)

if __name__ == '__main__':
    a = NcafeWriteAtt()
    start_time = time.time()
    a.writeAttendCheck()
    end_time = time.time()

    print("------ Total %s seconds ------" %(end_time - start_time))


import numpy as np
import subprocess as sp

# need to do this to all parameters that will be time series for pfields...
# do they all need to have the index? (seems like it)
def make_pf_string(pf_array):
    indexes = np.arange(len(pf_array))
    #print(len(pf_array), len(indexes))
    pf_withind = np.zeros(2*len(pf_array))
    pf_withind[1::2] = pf_array
    pf_withind[0:-1:2] = indexes
    pf_string = str(pf_withind.tolist()).strip('[').strip(']')
    return pf_string

# GENERATE THE RTcmix score !
def writeCmixSco_GRAN(tones_dict):
    # ====================
    base_name = tones_dict['base_name']
    score_name = base_name + '.sco'

    # ===========================================================================
    # add parameters to write out here:

    # fixed single values:
    #pitchjitter = tones_dict['pitchjitter']
    #pitchjitter_cmd = 'pitchjitter = ' + str(pitchjitter) + '\n'

    hopjitter = tones_dict['hopjitter']
    hopjitter_cmd = 'hopjitter = ' + str(hopjitter) + '\n'

    # P-field time series:

    pitch = tones_dict['p1']
    pitch_string = make_pf_string(pitch)
    pitch_cmd = "pitch = maketable(\"line\", \"nonorm\", 1000, " + pitch_string + ')\n'

    dur = tones_dict['dur_sound']
    dur_string = 'dur = ' + str(dur) + '\n'
    print(dur_string)

    amp = tones_dict['amplitude']
    print(len(amp))
    if len(amp)==1:
        amp_string = str(amp.tolist()).strip('[').strip(']')
        amp_cmd = 'amp = ' + amp_string + '  \n'
        print('Amplitude is a fixed value: ' + amp_string )
    elif len(amp)>1:
        amp_string = make_pf_string(amp)
        amp_cmd = 'amp = maketable(\"line\", \"nonorm\", 1000, ' + amp_string + ')\n'
        print('Amplitude is time varying (p-field)')

    pitchjtr = tones_dict['pitchjitter']
    print(len(pitchjtr))
    if len(pitchjtr)==1:
        pitchjtr_string = str(pitchjtr.tolist()).strip('[').strip(']')
        pitchjitter_cmd = 'pitchjitter = ' + pitchjtr_string + '  \n'
        print('Pitchjitter is a fixed value: ' + pitchjtr_string )
    elif len(pitchjtr)>1:
        pitchjtr_string = make_pf_string(pitchjtr)
        #amp_cmd = "pitch = maketable(\"line\", \"nonorm\", 1000, " + amp_string + ')\n'
        pitchjitter_cmd = 'pitchjitter = maketable(\"line\", \"nonorm\", 1000, ' + pitchjtr_string + ')\n'
        print('Pitchjitter is time varying (p-field)')

    # ===========================================================================

    #----------------------CHECK IF CMIX COMMAND IS INSTALLED-----------------------
    #only use rtoutput if CMIX command is found.
    cmixStatus, cmixResult = sp.getstatusoutput("CMIX")
    #the cmixInstalled variable can also be passed from the notebook
    #in that is case, the output of sp.getstatusoutput("CMIX") is overridden
    if 'cmixInstalled' in tones_dict:
        if tones_dict['cmixInstalled']:
            cmixStatus = 0
        else:
            cmixStatus = 127

    if cmixStatus == 0:
        output_string = 'rtoutput(\"' + base_name + '.wav\")\n'
        # don't need the brackets to make it an array !
        print("CMIX found.")
        print(output_string)
        #f_out.write(output_string)
    else:
        print("CMIX not found; rtoutput() will not be used in score.")
    #-------------------------------------------------------------------------------

    # ===========================================================================
    # START WRITING THE SCORE FILE: =========================
    f_out = open("./" + score_name , 'w')
    # MUST DELETE THE SOUND FILE BEFORE RUNNING (either with python or with -clobber )
    f_out.write("set_option(\"clobber = on\")\n") #"\n" added
    f_out.write("rtsetparams(44100, 1)\n") # mono sound !
    # f_out.write("rtsetparams(44100, 2)\n") # stereo sound
    f_out.write("reset(44100)\n")
    f_out.write("load(\"GRANSYNTH\")\n")
    f_out.write(dur_string)

    #output_string = 'rtoutput(\"' + base_name + '.wav\")\n'
    # don't need the brackets to make it an array !
    #print(output_string)
    if cmixStatus == 0:
        f_out.write(output_string)

    #f_out.write("waveform = maketable(\"sine\", 1000, 1.0, 0.4, 0.2)\n")
    #f_out.write("ampenv = maketable(\"window\", 1000, \"hamming\")\n")

    # AMPLITUDE
    #f_out.write("amp = maketable(\"line\", 500, 0,0, 1,1, 2,0.5, 3,1, 4,0)\n")
    #f_out.write("amp = 7000 \n")
    f_out.write(amp_cmd)

    #f_out.write("wave = maketable(\"wave\", 2000, 1, 0, 0.7, 0, 0.4)\n") ?????????? no indexes?
    f_out.write("wave = maketable(\"wave\", 2000, \"sine\")\n")
    f_out.write("granenv = maketable(\"window\", 2000, \"hanning\")\n")
    #f_out.write("hoptime = maketable(\"line\", \"nonorm\", 1000, 0,0.01, 1,0.002, 2,0.05)\n")
    f_out.write("hoptime = 0.01\n") # "maketable(\"line\", \"nonorm\", 1000, 0,0.01, 1,0.002, 2,0.05)\n")
    #f_out.write("hoptime = maketable(\"line\", \"nonorm\", 1000, 0,0.01, 1,0.002, 2,0.05)\n")

    # can these be pfields?
    #f_out.write("hopjitter = 0.0001\n") # randomness around hoptime !
    #f_out.write("hopjitter = 0.00\n")

    f_out.write("mindur = .05\n")
    f_out.write("maxdur = .05\n")
    #f_out.write("mindur = .04\n")
    #f_out.write("maxdur = .06\n")
    f_out.write("minamp = maxamp = 0.9\n")

    # PITCH:
    #f_out.write("pitch = maketable(\"line\", \"nonorm\", 1000, 0,1, 1,20)\n")
    f_out.write(pitch_cmd)

    # TRANSPCOLL (not sure what this does)
    #f_out.write("transpcoll = maketable(\"literal\", \"nonorm\", 0, 0, .02, .03, .05, .07, .10)\n")
    f_out.write("transpcoll = maketable(\"literal\", \"nonorm\", 0, 0)\n")

    # PITCH JITTER
    #f_out.write("pitchjitter = 1\n")
    f_out.write(pitchjitter_cmd)
    # HOP JITTER
    f_out.write(hopjitter_cmd)

    f_out.write("st = 0\n")  # what does this do ?  NEED it there, whatever it is :)
    f_out.write("GRANSYNTH(st, dur, amp, wave, granenv, hoptime, hopjitter, mindur, maxdur, minamp, maxamp, 1.0*pitch, transpcoll, pitchjitter, 14, 1)\n") # mono
    #f_out.write("GRANSYNTH(st, dur, amp, wave, granenv, hoptime, hopjitter, mindur, maxdur, minamp, maxamp, 1.0*pitch, transpcoll, pitchjitter, 14, 0, 1)\n") # stereo
    print("RTcmix score created.")

    return


from collections import Counter, OrderedDict
import heapq
import sys

is_py3 = sys.version_info.major >= 3

class HuffmanNode(object):

    def __init__(self, symbol=None, freq=0):
        self.symbol  = symbol
        self.freq    = freq
        self.left    = None
        self.right   = None

    def __repr__(self):
        return "Node(Symbol:'{}',Freq:{})\n".format(self.symbol,self.freq)

    def __eq__(self, other):
        if isinstance(other, HuffmanNode):
            return self.freq == other.freq
        return False

    def __cmp__(self, other):
        if isinstance(other, HuffmanNode):
            return self.freq > other.freq

    def __lt__(self, other):
        if isinstance(other, HuffmanNode):
            return self.freq < other.freq

    def __add__(self, other):
        if isinstance(other, HuffmanNode):
            node = HuffmanNode(None, self.freq+other.freq)
            node.left = self
            node.right = other
            return node

    def is_leaf(self):
        return self.symbol != None

class HuffmanTree(object):

    SYMBOL_LIMIT = 257

    def __init__(self, text):
        self.heap = self._initialize_heap(text)
        self.root = self._construct_tree()
        self.codes = self._generate_code_tree()

    def __repr__(self):
        return "Huffman Tree\n" + str( self.codes )

    def _initialize_heap(self, byte_string):
        """
        Initializes a binary search tree with the symbols provided in text

        Parameters
        ------------
        text: iterable of text to be compressed
        """

        heap = []

        counter = Counter(b if is_py3 else ord(b) for b in byte_string)
        for symbol, frequency in counter.items():
            node = HuffmanNode(symbol, frequency)
            heapq.heappush(heap, node)
        return heap

    def _construct_tree(self):
        """
        Parameters
        ------------
        text: iterable of text to be compressed
        """
        root = None

        while(len(self.heap) > 1):
            node_a, node_b = heapq.heappop(self.heap), heapq.heappop(self.heap)
            merged_node = node_a + node_b
            heapq.heappush(self.heap, merged_node)

        root = heapq.heappop(self.heap)

        return root

    def _generate_code_tree(self):
        if self.root is not None:
            codes = [0] * self.SYMBOL_LIMIT
            starting_code = ""
            self._generate_codes(self.root, starting_code, codes)

        return codes

    def _generate_codes(self, current_node, current_code, codes):

        if current_node is not None:
            if current_node.symbol is not None:
                codes[current_node.symbol] = current_code
            self._generate_codes(current_node.left, current_code+"0", codes)
            self._generate_codes(current_node.right, current_code+"1", codes)

class CanonicalHuffmanTree(HuffmanTree):

    def __init__(self, text=None, code_lengths=None):
        self.canon_codes = [0] * self.SYMBOL_LIMIT
        if text:
            self.code_lengths_by_symbols = [0]*self.SYMBOL_LIMIT
            self.generate_code_length_per_symbol(text)
            self.root = self.generate_code_tree(self.code_lengths_by_symbols)

        elif code_lengths:
            if len(code_lengths) != self.SYMBOL_LIMIT:
                raise Exception("Code lengths not equal to SYMBOL LIMIT")
            self.root = self.generate_code_tree(code_lengths)

        else:
            raise Exception("Must provide either text or an array of code lengths")

    def generate_code_length_per_symbol(self, text):
        naive_huff_tree = HuffmanTree(text)
        symbol_codes = naive_huff_tree.codes
        self._get_symbols_by_code_length(symbol_codes)

    def generate_code_tree(self, code_length_by_symbols):
        """ Generates the canonical code tree """
        nodes = []

        #iterate through the code lengths, from largest to smallest
        for code_length in range(max(code_length_by_symbols), -1, -1):
            leaves = []
            if code_length > 0:
                for symbol, j in enumerate(code_length_by_symbols):
                    # create leaf nodes for all symbols of the current code length, in ascii order
                    if code_length == j:
                        leaves.append(HuffmanNode(symbol))

            #create intermediate nodes, which point to the leaf nodes
            #from the previous level
            for i in range(0, len(nodes), 2):
                leaves.append(nodes[i] + nodes[i+1])
            nodes = leaves

        root = nodes[0]
        self._generate_codes(root, "", self.canon_codes)
        return root

    def _get_symbols_by_code_length(self, symbol_codes):
        for symbol, naive_huff_code in enumerate(symbol_codes):
            if isinstance(naive_huff_code, str):
                code_length = len(naive_huff_code)
                self.code_lengths_by_symbols[symbol] = code_length

    def get_code_length(self, symbol):
        return self.code_lengths_by_symbols[symbol]

    def get_huffman_code(self, symbol):
        if symbol < self.SYMBOL_LIMIT:
            return self.canon_codes[symbol]

class Reader(object):

    def __init__(self, input_file_path, output_file_path=None):
        self.infile_path = input_file_path
        if output_file_path is None:
            output_file_path = 'output.txt'
        self.outfile_path = output_file_path
        self.bit_buffer = []

    def convert_to_bytes(self, num, bytelength=1, endian='big'):
        return num.to_bytes(bytelength, endian)

class HuffmanEncoder(Reader):

    def write_huffman_table(self, huffman_table, output_file_handler):
        for code_len in huffman_table:
            bin_repr = self.convert_to_bytes(code_len)
            output_file_handler.write(bin_repr)

    def compress(self):

        with open(self.infile_path, 'rb') as fh:
            text = fh.read()

        chc = CanonicalHuffmanTree(text)
        huffman_table = chc.code_lengths_by_symbols
        huff_codes = chc.canon_codes

        output_file_handler = open(self.outfile_path, 'wb+')
        try:
            self.write_huffman_table(huffman_table, output_file_handler)
            self.write_compressed_text(text, huff_codes, output_file_handler)
        except Exception as e:
            raise e
        finally:
            output_file_handler.close()

    def write_compressed_text(self, text, codes, output_file_handler):
        eof_marker = 256
        for symbol in text:
            huff_code = codes[symbol]
            code_length = len(huff_code)
            for bit in huff_code:
                self.write_current_bits(output_file_handler, bit)

        #write any remaining bits to the file
        self.flush_bit_buffer(output_file_handler)

    def write_current_bits(self, fh, bit):
        self.bit_buffer.append(bit)
        if len(self.bit_buffer) == 8:
            bit_str = "".join(self.bit_buffer)
            fh.write(bytes((int(bit_str, 2), )))
            self.bit_buffer = []

    def flush_bit_buffer(self, fh):
        while (len(self.bit_buffer) > 0):
            self.write_current_bits(fh, "0")

class HuffmanDecoder(Reader):

    def read_huffman_table(self, fh):

        huffman_table = [0] * 257
        for symbol in range(257):
            byte_string = fh.read(1)
            code_length = int.from_bytes(byte_string, 'big')
            huffman_table[symbol] = code_length
        return huffman_table

    def decompress(self):
        in_file = open(self.infile_path, 'rb')
        out_file = open(self.outfile_path, 'wb+')

        try:
            symbol_codes = self.read_huffman_table(in_file)
            canonical_huff_tree = CanonicalHuffmanTree(code_lengths=symbol_codes)
            while True:
                symbol = self.read_symbols(in_file, canonical_huff_tree.root)
                if symbol is None:
                    break
                else:
                    out_file.write(bytes((symbol,)))
        except Exception as e:
            raise e
        finally:
            in_file.close()
            out_file.close()

    def read_symbols(self, fh, root):
        node = root
        while True:
            # If the node is a leaf node
            # get the symbol
            if node.is_leaf():
                return node.symbol

            bit, end_of_file = self.read_input_bit(fh)
            if end_of_file:
                return

            if bit == "0":
                node = node.left
            elif bit == "1":
                node = node.right
            else:
                raise Exception("Invalid bit")

    def read_input_bit(self, fh):
        eof = False

        if len(self.bit_buffer) == 0:
            byte_string = fh.read(1)

            #End of file
            if len(byte_string) == 0:
                eof = True
                return "", eof

            bit_string = self.to_bit_string(byte_string)
            for b in bit_string:
                self.bit_buffer.append(b)

        top = self.bit_buffer.pop(0)
        return top, eof

    def to_bit_string(self, byte_string):
        b = bin(int.from_bytes(byte_string, 'big')).replace('b', '').lstrip('0')
        while len(b)<8:
            b = '0' + b
        return b


# -*- coding: utf-8 -*-
from __future__ import annotations

from collections import Counter
from pprint import pformat
from types import ModuleType

import pytest

import shellfish

from shellfish import _types as shellfish_types, aios, dotenv, fs, process, sh
from shellfish.aios import _path as aiospath
from shellfish.fs import promises as fsp

modules = [shellfish, fs, sh, dotenv, process, fsp, aiospath, aios, fsp]

def _test_module_all_tuple(mod: ModuleType, check_sorted: bool = True) -> None:
    assert hasattr(mod, "__all__"), f"{mod} has no __all__"
    mod_all = getattr(mod, "__all__")  # noqa: B009
    mod_name = mod.__name__
    assert isinstance(mod_all, tuple), "__all__ should be tuple"

    members_count = Counter(mod_all)
    duplicates = [el for el, count in members_count.items() if count > 1]
    assert not duplicates, f"duplicates in __all__: {duplicates}"
    assert len(set(mod_all)) == len(mod_all), "all should be " + str(
        tuple(sorted(set(mod_all)))
    )
    if check_sorted:
        sorted_all_tuple = tuple(sorted(mod_all))
        try:
            assert sorted_all_tuple == mod_all
        except AssertionError as e:
            print(f"{mod_name} should be:")  # noqa: T201
            if len(sorted_all_tuple) > 10:
                print("__all__ = " + str(sorted_all_tuple))  # noqa: T201
            else:
                print("__all__ = " + pformat(sorted_all_tuple))  # noqa: T201
            raise e

@pytest.mark.parametrize("mod", modules)
def test_module_exports(mod: ModuleType) -> None:
    check_sorted = mod.__name__ not in {
        "shellfish.sh",
    }

    _test_module_all_tuple(mod, check_sorted=check_sorted)

@pytest.mark.parametrize("member", fs.__all__)
def test_sh_exports_fs_member(member: str) -> None:
    member_val = getattr(sh, member)
    assert member_val is not None, f"{member} is not exported by sh"

@pytest.mark.parametrize("member", shellfish_types.__all__)
def test_shellfish_exports_types_member(member: str) -> None:
    member_val = getattr(shellfish, member)
    assert member_val is not None, f"{member} is not exported by shellfish"


# encoding: utf-8
# Created on 2014-6-12
# @author: binge
import logging

logger = logging.getLogger("bookshelf")

logger.setLevel(logging.DEBUG)

fh = logging.FileHandler("bookshelf.log")
fh.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)

formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch.setFormatter(formatter)
fh.setFormatter(formatter)

logger.addHandler(fh)
logger.addHandler(ch)