from flask import request, jsonify, Blueprint
from flask_jwt_extended import jwt_required, current_user
from models.Containers_model import Container
from models.Image_model import Image
from models.db import db
from controller import Containers_controller as containerctl
from utils import check_authorization

container_bp = Blueprint('container_bp', __name__)

@container_bp.route('/images', methods=["GET"])
@jwt_required()
def get_images_by_user():
    images = Image.query.filter_by(author_id=current_user.id).all()
    print(images)
    responses = [{
            "docker_id": image.docker_id,
            "name": image.name,
            "createdAt": image.createdAt,
            "author": image.author.name
        } for image in images]

    return jsonify(responses), 200

@container_bp.route('/image/<id>', methods=["GET"])
@jwt_required()
def get_image_by_id(id):
    image = Image.query.filter_by(docker_id=id).first()
    response = {
            "docker_id": image.docker_id,
            "name": image.name,
            "createdAt": image.createdAt,
            "author": image.author.name
        }
    return jsonify(response), 200

@container_bp.route('/image/<id>', methods=["DELETE"])
@jwt_required()
def delete_img(id):
    image = Image.query.filter_by(docker_id=id).first()
    if not image:
        return jsonify({"message": "Image with the given ID is not found"}), 404
    auth_check = check_authorization(image.author_id, current_user.id)
    if not auth_check:
        return jsonify({"success": False, "message": "Unauthorized"}), 401
    ok, message = containerctl.delete_image(id, True)
    if not ok:
        return jsonify({"success": ok, "message": message}), 500
    db.session.delete(image)
    db.session.commit()
    return jsonify({"success": ok, "message": message}), 200

@container_bp.route('/image', methods=["POST"])
@jwt_required()
def create_image():
    body = request.get_json()
    print(current_user)
    result = containerctl.build_image(
        current_user.email.split("@")[0].lower(),
        body['baseImg'],
        body['packages'],
        body['desktop'],
        body['username'],
        body['password'],
        body['timezone'],
        body.get('memory', 4096)
    )
    # Insert into Image Table
    new_image = Image(
        name=result[1],
        docker_id=result[2],
        author_id=current_user.id
    )
    try:
        db.session.add(new_image)
        db.session.commit()
        return jsonify({"success": result[0], "image_id": result[2], "image_name": result[1]}), 201
    except Exception as e:
        db.session.rollback()
        return jsonify({"success": False, "message": str(e)}), 400

@container_bp.route('/containers', methods=["GET"])
@jwt_required()
def get_container_by_user():
    containers = Container.query.filter_by(author_id=current_user.id).all()
    responses = [{
        "container_name": container.container_name,
        "docker_id": container.docker_id,
        "author": container.author.name,
        "subdomain": container.subdomain,
        "vnc_port": container.vnc_port,
        "vnc_host": container.vnc_host,
        "vnc_url": container.vnc_url,
        "ssh_port": container.ssh_port,
        "ws_port": container.ws_port,
        "state": container.state,
        "image_name": container.image.name,
        "image_author": container.image.author.name,
        "createdAt": container.createdAt
    } for container in containers]
    print(containers)
    return jsonify(responses), 200

@container_bp.route('/container', methods=["POST"])
@jwt_required()
def create_and_run_container():
    body = request.get_json()
    image_name = body['image_name']
    result = containerctl.run_container(
        current_user.email.split("@")[0].lower(),
        image_name
    )

    hostname = request.host.split(":")[0]
    subdomain = f"{result[3]}.{hostname}"

    # Insert into Container Table
    new_container = Container(
        docker_id=result[1],
        vnc_port=result[2],
        author_id=current_user.id,
        state=result[4],
        container_name=result[3],
        vnc_host=hostname,
        subdomain=subdomain,
        image_id=result[5],
        ssh_port=result[7],
        ws_port=result[6],
        vnc_url=f"ws://{hostname}:{result[6]}"
    )
    try:
        db.session.add(new_container)
        db.session.commit()
        return jsonify({"success": result[0], "container_id": result[1], "vnc_host": request.host_url.replace("http://", "").replace("/:5000", ""), "vnc_port": result[2], "container_name": result[3], "status": result[4], "ssh_port":result[7],
        "ws_port":result[6]}), 201
    except Exception as e:
        db.session.rollback()
        return jsonify({"success": False, "message": str(e)}), 400

@container_bp.route('/container/<container_id>', methods=["GET"])
@jwt_required()
def get_container_by_id(container_id):
    container = Container.query.filter_by(docker_id=container_id).first()
    if not container:
        return jsonify({"message": "Container with the given ID is not found"}), 404
    check = check_authorization(container.author_id, current_user.id)
    if not check:
        return jsonify({"message": "Unauthorized"}), 401

    logs = containerctl.get_logs(container.docker_id)
    response = {
        "container_name": container.container_name,
        "docker_id": container.docker_id,
        "author": container.author.name,
        "subdomain": container.subdomain,
        "vnc_port": container.vnc_port,
        "vnc_host": container.vnc_host,
        "vnc_url": container.vnc_url,
        "ssh_port": container.ssh_port,
        "ws_port": container.ws_port,
        "state": container.state,
        "image_name": container.image.name,
        "image_author": container.image.author.name,
        "createdAt": container.createdAt,
        "logs": logs.decode()
    }
    return jsonify(response), 200

@container_bp.route('/container/<container_id>', methods=["PUT"])
@jwt_required()
def stop_container(container_id):
    container_check = Container.query.filter_by(docker_id=container_id).first()
    if not container_check:
        return jsonify({"message": "Container with the given ID is not found"}), 404
    auth_check = check_authorization(container_check.author_id, current_user.id)
    if not auth_check:
        return jsonify({"message": "Unauthorized"}), 401
    ok = containerctl.stop_container(container_id)
    container = Container.query.filter_by(docker_id=container_id).first()
    container.state = "Stopped"
    db.session.commit()
    return jsonify({"success": ok}), 200

@container_bp.route('/container/<container_id>', methods=["DELETE"])
@jwt_required()
def delete_container(container_id):
    container_check = Container.query.filter_by(docker_id=container_id).first()
    if not container_check:
        return jsonify({"message": "Container with the given ID is not found"}), 404
    auth_check = check_authorization(container_check.author_id, current_user.id)
    if not auth_check:
        return jsonify({"message": "Unauthorized"}), 401
    ok = containerctl.delete_container(container_id)
    container = Container.query.filter_by(docker_id=container_id).first()
    db.session.delete(container)
    db.session.commit()
    return jsonify({"success": ok}), 200


from mako import runtime, filters, cache
UNDEFINED = runtime.UNDEFINED
__M_dict_builtin = dict
__M_locals_builtin = locals
_magic_number = 4
_modified_time = 1224684001.22454
_template_filename='/media/disk/Pylons Book/Code/chapter06/FormExample-01/formexample/templates/derived/form.html'
_template_uri='/derived/form.html'
_template_cache=cache.Cache(__name__, _modified_time)
_source_encoding='utf-8'
from webhelpers.html import escape
_exports = ['person', 'study']

def _mako_get_namespace(context, name):
    try:
        return context.namespaces[(__name__, name)]
    except KeyError:
        _mako_generate_namespaces(context)
        return context.namespaces[(__name__, name)]
def _mako_generate_namespaces(context):
    pass
def _mako_inherit(template, context):
    _mako_generate_namespaces(context)
    return runtime._inherit_from(context, '/base/index.html', _template_uri)
def render_body(context,**pageargs):
    context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)
        h = context.get('h', UNDEFINED)
        def study():
            return render_study(context.locals_(__M_locals))
        __M_writer = context.writer()
        # SOURCE LINE 1
        __M_writer(u'\n\n')
        # SOURCE LINE 19
        __M_writer(u'\n\n')
        # SOURCE LINE 51
        __M_writer(u'\n\n
Create a Study
\n\n')
        # SOURCE LINE 55
        __M_writer(escape(h.form(h.url_for(controller='study', action='process'))))
        __M_writer(u'\n')
        # SOURCE LINE 56
        __M_writer(escape(study()))
        __M_writer(u'\n')
        # SOURCE LINE 57
        __M_writer(escape(h.submit(name="action", value="Save")))
        __M_writer(u'\n')
        # SOURCE LINE 58
        __M_writer(escape(h.submit(name="action", value="Add New Person")))
        __M_writer(u'\n')
        # SOURCE LINE 59
        __M_writer(escape(h.end_form()))
        __M_writer(u'\n\n')
        return ''
    finally:
        context.caller_stack._pop_frame()

def render_person(context,id):
    context.caller_stack._push_frame()
    try:
        h = context.get('h', UNDEFINED)
        __M_writer = context.writer()
        # SOURCE LINE 21
        __M_writer(u'\n    <fieldset><legend>Person</legend>\n\n        Title
\n        ')
        # SOURCE LINE 25
        __M_writer(escape(h.text(name="person-%s.title"%(id), id="person-%s.title"%(id))))
        __M_writer(u'
\n\n        First Name
\n        ')
        # SOURCE LINE 28
        __M_writer(escape(h.text(
            name="person-%s.firstname"%(id),
            id="person-%s.firstname"%(id
        ))))
        # SOURCE LINE 31
        __M_writer(u'
\n\n        Surname
\n        ')
        # SOURCE LINE 34
        __M_writer(escape(h.text(name="person-%s.surname"%(id), id="person-%s.surname"%(id))))
        __M_writer(u'
\n\n        Role
\n        ')
        # SOURCE LINE 37
        __M_writer(escape(h.select(
            "person-%s.role"%(id),
            [],
            [
                ['1', 'Chief Investigator'],
                ['2', 'Assistant'],
                ['3', 'Student'],
            ],
            id="person-%s.role"%(id),
        )))
        # SOURCE LINE 46
        __M_writer(u'
\n\n        ')
        # SOURCE LINE 48
        __M_writer(escape(h.submit(name="action", value="Remove %s"%(id))))
        __M_writer(u'\n\n    </fieldset>
\n')
        return ''
    finally:
        context.caller_stack._pop_frame()

def render_study(context):
    context.caller_stack._push_frame()
    try:
        h = context.get('h', UNDEFINED)
        range = context.get('range', UNDEFINED)
        c = context.get('c', UNDEFINED)
        def person(id):
            return render_person(context,id)
        __M_writer = context.writer()
        # SOURCE LINE 3
        __M_writer(u'\n    <fieldset><legend>Study</legend>\n\n        Title
\n        ')
        # SOURCE LINE 7
        __M_writer(escape(h.text(name="title", id="title")))
        __M_writer(u'
\n\n        Start Date
\n        ')
        # SOURCE LINE 10
        __M_writer(escape(h.text(name="start_date", id="startdate")))
        __M_writer(u'
\n\n        End Date
\n        ')
        # SOURCE LINE 13
        __M_writer(escape(h.text(name="end_date", id="enddate")))
        __M_writer(u'
\n\n    </fieldset>
\n')
        # SOURCE LINE 16
        for id in range(c.number_of_people):
            # SOURCE LINE 17
            __M_writer(u'            ')
            __M_writer(escape(person(id=id)))
            __M_writer(u'\n')
        return ''
    finally:
        context.caller_stack._pop_frame()


# -*- coding: utf-8 -*-
"""
Created on Wed Apr 12 14:13:33 2017
预处理获取小波系数、重构系数、模极大值
对预处理后的序列进行特征提取，包括能量、归一化能量、能量熵、能量矩、排列熵、近似熵、样本熵、最大Lyapunov指数、灰度矩
分形维数、奇异熵
@author: baishuhua
"""

import pywt
import numpy as np
from scipy import stats
import sys
sys.path.append('E:\\大数据\\基础研究\\HilbertHuang变换')
import HilbertHuang
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False

def DWT(signal,wavelet,level):
    # 实数序列进行dwt变换，返回的是不等长的小波系数，[cAn,cDn,cDn-1,...,cD2,cD1]
#    max_level=pywt.dwt_max_level(data_len=len(signal), filter_len=pywt.Wavelet(wavelet).dec_len)
    Coeffs=pywt.wavedec(signal,wavelet=wavelet,mode='smooth',level=level)
    return Coeffs

def DwtRec(signal,wavelet,level):
    # 返回的是每一层的分量，即将原始信号进行了频带分解，时间长度与原始序列signal等长
    # 每一层对应一行
    CoeffsRev=[]
    Coeffs=DWT(signal,wavelet=wavelet,level=level)
    for row in range(level+1):
        if row==0:
            # 重构近似系数，即低频分量
            leveltype=level;part='a'
        else:
            # 重构细节系数，即高频分量
            leveltype=level-row+1;part='d'
        EachLevelRev=pywt.upcoef(part,Coeffs[row],wavelet=wavelet,level=leveltype,take=len(signal))
        CoeffsRev.append(EachLevelRev)
    return np.array(CoeffsRev)

def DwtModMax(signal,wavelet,level):
    # 返回的是细节系数的模极大值序列
    DetailCoeffs=DWT(signal,wavelet=wavelet,level=level)[1:]
    ExtremeX=[];ExtremeY=[]
    for coeff in DetailCoeffs:
        pos=HilbertHuang.findpeaks(coeff)
        neg=HilbertHuang.findpeaks(-coeff)
        xx=np.concatenate((pos,neg))
        yy=np.concatenate((coeff[pos],coeff[neg]))
        index=np.argsort(xx)
        ExtremeX.append(list(xx[index]));ExtremeY.append(list(yy[index]))
    return np.array(ExtremeX),np.array(ExtremeY)

def GetEnergy(coeffs):
    # 能量'Energy'
    Energy=[];EnergyName=[]
    for order,EachLevelCoeff in enumerate(coeffs):
        Energy.append(np.sum(EachLevelCoeff**2))
        EnergyName.append('Energy-L'+str(len(coeffs)-order))
    return np.array(EnergyName),np.array(Energy)

def GetEnergyRatio(coeffs):
    # 归一化能量'EnergyRatio'
    EnergyName,Energy=GetEnergy(coeffs)
    EnergyRatioName=list(map(lambda x:x.replace('Energy','EnergyRatio'),EnergyName))
    EnergyRatio=Energy/np.sum(Energy)
    return np.array(EnergyRatioName),np.array(EnergyRatio)

def Entropy(pk): # 香农熵,pk参数非负代表的是概率
    entropy=stats.entropy(pk,qk=None,base=None)
    return entropy

def GetEnergyEn(coeffs):
    # 能量熵'EnergyEn'
    WEE=[];WEEName=[]
    for order,coeff in enumerate(coeffs):
        pk=coeff**2/np.sum(coeff**2)
        WEE.append(Entropy(pk))
        WEEName.append('EnergyEn-L'+str(len(coeffs)-order))
    return np.array(WEEName),np.array(WEE)

def GetEnergyTq(coeffs):
    # 归一化能量矩'EnergyTq'
    WET=[];WETName=[]
    for order,coeff in enumerate(coeffs):
        weight=np.linspace(1,len(coeff),num=len(coeff))
        WET.append(np.dot(weight,coeff**2))
        WETName.append('EnergyTq-L'+str(len(coeffs)-order))
    WET/=np.sum(WET)
    return np.array(WETName),np.array(WET)

def GetGrayTq(coeffs):
    # 灰度矩'GrayTq'
    m,n=np.shape(coeffs)
    M=0
    for i,j in zip(np.arange(1,m+1),np.arange(1,n+1)):
        M+=abs(coeffs[i-1,j-1])*np.sqrt((i-1)**2+(j-1)**2)
    M/=(m*n)
    return np.array(['GrayTq']),np.array([M])

def GetSingularEn(coeffs):
    # 奇异熵'SingularEn'
    U,s,V=np.linalg.svd(coeffs,full_matrices=True)
    pk=s/np.sum(s)
    WSE=Entropy(pk)
    return np.array(['SingularEn']),np.array([WSE])

def GetFractalDims(coeffs):
    # 分形维数'FractalDims'
    Ref0=np.log2(np.var(np.sum(coeffs,axis=0))) # 原信号方差的对数
    temp=coeffs[1:]
    xx=np.arange(len(coeffs)-1,0,step=-1).ravel()
    yy=np.log2(np.var(temp,axis=1)).ravel()
    yy=np.insert(yy,0,Ref0);xx=np.insert(xx,0,0)
    index=np.argsort(xx)
    xx=xx[index];yy=yy[index]
    params=np.polyfit(xx,yy,1)
    beta=-params[0];D=1.0/2*(5-beta)
    return np.array(['FractalDims']),np.array([D])

def EuclideanDistance(x1,x2):
    return np.sqrt(np.dot(x1-x2,x1-x2))

def DistanceMatrix(X,method=EuclideanDistance):
    Distance=np.zeros((len(X),len(X)))
    for row in np.arange(0,len(X)-1):
        Xi=X[row]
        for col in np.arange(row,len(X)):
            Xj=X[col]
            Distance[row,col]=method(Xi,Xj)
    Distance+=np.transpose(Distance)
    return Distance

def SearchNearest(Distance):
    # 按照行，返回与之距离最近的行编号和距离值
    XPos0=[];YDist0=[]
    for row,dist in enumerate(Distance):
        temp=np.argsort(dist)
        index=temp[temp!=row][0]
        XPos0.append(index)
        YDist0.append(dist[index])
    return XPos0,YDist0

def GetLyapunov(coeffs):
    # 最大Lyapunov指数 'Lyapunov'
    if len(coeffs)==np.size(coeffs):
        coeffs=coeffs[np.newaxis,:]

    m=3;J=5 # 分别对应嵌入维数和延迟参数
    N=np.shape(coeffs)[-1] # 对应时间序列的长度
    M=N-(m-1)*J # 对应相空间重构矩阵的行数
    Lamda=[];LamdaName=[]
    for order,coeff in enumerate(coeffs):
        X=[] # 对应每层小波系数重构的相空间
        for row in range(m):
            X.append(coeff[int(row*J):int(row*J+M)])
        X=np.transpose(np.array(X))

        Distance=DistanceMatrix(X)
        XPos0,YDist0=SearchNearest(Distance)
        # 第i行代表与第i个样本距离最近的样本索引及其最近距离，公式中的j^与dj(0)
        XRef0=np.arange(0,len(Distance))
        # 与XPos0对应的参考样本索引,公式中的j
        lamda=[]
        for indexRef0,indexPos0 in zip(XRef0,XPos0):
            xx=[0];yy=[np.log(YDist0[indexRef0]+1e-10)]
            for bias in range(min(len(XRef0)-indexRef0,len(XRef0)-indexPos0)-1):
                xx.append(bias+1)
                yy.append(np.log(Distance[indexRef0+bias+1,indexPos0+bias+1]+1e-10))

            if len(xx)>1:
                params=np.polyfit(xx,yy,1)
                lamda.append(params[0])
            else:
                continue
        Lamda.append(max(lamda))
        LamdaName.append('Lyapunov-L'+str(len(coeffs)-order))
    return np.array(LamdaName),np.array(Lamda)

def AbsDistance(signal1,signal2):
    return np.max(np.abs((signal1-signal2)))

def GetApEn(coeffs):
    if len(coeffs)==np.size(coeffs):
        coeffs=coeffs[np.newaxis,:]
    ApEn=[]; # 对应近似熵

    PHY=[];
    N=np.shape(coeffs)[-1] # 对应时间序列的长度
    for m in [2,3]: # 对应嵌入维数
        M=N-m+1 # 对应重构矩阵的行数
        PHY_before=[];ApEnName=[]
        for order,coeff in enumerate(coeffs):
            gama=0.2*np.std(coeff) # 对应相似容限
            X=[] # 对应每层小波系数重构的相空间
            for row in range(M):
                X.append(coeff[int(row):int(row+m)])
            Distance=DistanceMatrix(X,method=AbsDistance)

            total=list(map(lambda x:sum(x<=gama)-1,Distance))
            Ci=np.divide(total,float(M-1))
            Ci=Ci[np.nonzero(Ci)]
            PHY_before.append(np.mean(np.log(Ci))) # 对应phy-m
            ApEnName.append('ApEn-L'+str(len(coeffs)-order))
        PHY.append(PHY_before)
    PHY=np.array(PHY)
    ApEn=PHY[0]-PHY[1]

    return np.array(ApEnName),np.array(ApEn)

def GetSampEn(coeffs):
    if len(coeffs)==np.size(coeffs):
        coeffs=coeffs[np.newaxis,:]
    SampEn=[] # 对应样本熵

    B=[]
    N=np.shape(coeffs)[-1] # 对应时间序列的长度
    for m in [2,3]: # 对应嵌入维数
        M=N-m+1 # 对应重构矩阵的行数
        B_before=[];SampEnName=[]
        for order,coeff in enumerate(coeffs):
            gama=0.2*np.std(coeff) # 对应相似容限
            X=[] # 对应每层小波系数重构的相空间
            for row in range(M):
                X.append(coeff[int(row):int(row+m)])
            Distance=DistanceMatrix(X,method=AbsDistance)

            total=list(map(lambda x:sum(x<=gama)-1,Distance))
            Ci=np.divide(total,float(M-1))
            Ci=Ci[np.nonzero(Ci)]
            B_before.append(np.mean(Ci))
            SampEnName.append('SampEn-L'+str(len(coeffs)-order))
        B.append(B_before)
    B=np.array(B)
    SampEn=-np.log(B[1]/B[0]) # 分别对应近似熵和样本熵
    return np.array(SampEnName),np.array(SampEn)

def GetPermuEn(coeffs):
    # 排列熵 'PermutationEn'
    if len(coeffs)==np.size(coeffs):
        coeffs=coeffs[np.newaxis,:]

    m=3;J=5 # 分别对应嵌入维数和延迟参数
    N=np.shape(coeffs)[-1] # 对应时间序列的长度
    M=N-(m-1)*J # 对应相空间重构矩阵的行数
    PermuEn=[];PermuEnName=[]
    for order,coeff in enumerate(coeffs):
        X=[] # 对应每层小波系数重构的相空间
        for row in range(m):
            X.append(coeff[int(row*J):int(row*J+M)])
        X=np.transpose(np.array(X))
        S=list(map(lambda x:np.argsort(x),X))
        S=np.array(S)

        strS=list(map(str,S))
        strCode=list(map(lambda x:'$'.join(x),strS))

        Counts={}
        for each in strCode:
            if each not in Counts.keys():
                Counts[each]=1
            else:
                Counts[each]+=1
        pk=np.divide(list(Counts.values()),len(S))
        PermuEn.append(Entropy(pk))
        PermuEnName.append('PermuEn-L'+str(len(coeffs)-order))
    return np.array(PermuEnName),np.array(PermuEn)

# 综合Dwt变换域指标

def FeatureExtractDwt(signal):
#    FunctionNames=[GetEnergy,GetEnergyRatio,GetEnergyEn,GetEnergyTq,GetGrayTq,GetSingularEn,\
#                   GetFractalDims,GetLyapunov,GetApEn,GetSampEn,GetPermuEn]
    # 仅提取部分特征
    FunctionNames=[GetSingularEn,GetGrayTq,GetFractalDims]
#    if np.size(signal)==len(signal):
#        signal=signal[:,np.newaxis]
    ResultName=[];Result=[]
    for functionname in FunctionNames:
        featurename,feature=functionname(signal)
        ResultName.append(featurename)
        Result.append(feature)
    FeatureValue=np.concatenate(Result)
    FeatureName=np.concatenate(ResultName)
    return FeatureName,FeatureValue

# 示例

def ReadRecord(file): # 读取录波数据
    ReadFlag=open(file)
    ReadFlag.readline();ReadFlag.readline();ReadFlag.readline()
    segnum=ReadFlag.readline().strip().split(':')[1]
    Time=[]
    for seg in range(int(segnum)):
        SampleAttr=ReadFlag.readline().strip().split(' ')
        [fs,start,terminal]=list(map(int,map(float,SampleAttr)))
        if len(Time)<1:
            Time.extend(1/fs*(np.linspace(1,terminal-start+1,num=terminal-start+1)))
        else:
            Time.extend(Time[-1]+1.0/fs*(np.linspace(1,terminal-start+1,num=terminal-start+1)))
    FaultIndex=int(ReadFlag.readline().strip().split('fault_index:')[1])
    FaultTime=Time[FaultIndex]
    SignalNames=ReadFlag.readline().strip().split(' ')
    SignalNames=[name for name in SignalNames if name!='']
    Data=[];ValidIndex=[];row=0
    for record in ReadFlag:
        detail=record.strip().split(' ')
        if len(detail)==len(SignalNames):
            try:
                Data.append(list(map(float,detail)))
                ValidIndex.append(row)
                row=row+1
            except ValueError:
                row=row+1
        else:
            row=row+1
            continue
    ReadFlag.close()
    ValidIndex=np.array(ValidIndex)
    rows=ValidIndex[ValidIndex<len(Time)]
    Time=np.array(Time);Time=Time[rows];Data=np.array(Data);Data=Data[rows];
    return Time,Data,np.array(SignalNames),FaultTime

def Interp(x,y,kind='slinear',axis=0): # 沿axis轴拟合x，y，因变量y沿axis维度应等于x维度
    from scipy import interpolate
    function=interpolate.interp1d(x,y,kind,axis)
    return function

if __name__=='__main__':
    # 模拟数据
    if 0:
#        fs=1000
#        time=np.arange(0,1,step=1/fs)
#        signal=2*np.cos(2*np.pi*50*time)+4+0.25*np.cos(2*np.pi*100*time)+1*np.cos(2*np.pi*150*time)
        fs=1000;f0=50;f1=350;f2=150
        time=np.arange(0,10,step=1/fs);signal=6*np.cos(2*np.pi*f0*time)+2+0.25*np.cos(2*np.pi*f2*time)
        t1=4;t2=6;signal[(time>=t1)&(time<=t2)]+=0.05*np.cos(2*np.pi*f1*time[(time>=t1)&(time<=t2)])

    # 仿真数据
    if 1:
        file = r'E:\关于项目\江苏涌流识别\仿真\bsh\内部故障\5%匝_AB_[0.1 0.12]s.txt'
        ReadFlag = open(file)
        data = []
        for line in ReadFlag:
            attr = line.strip().split(' ')
            attr = list(map(float,attr))
            data.append(attr)
        data = np.array(data)
        signal = data[:,1]
        time = data[:,0]
        fs = 1/(time[1]-time[0])

        coeffs = DwtRec(signal,wavelet='bior3.5',level=4)
        FeatureName,FeatureValue =  FeatureExtractDwt(coeffs)

    # 实测数据
    if 0:
        import os
        file='E:/大数据/线路故障诊断/解压录波/有效集中128/Test-1/异物_1_0_12_港城站_20140510港遂乙线_SSH850.cfg_sub.dat_AN'
        filename=os.path.basename(file)
        attr=filename.split('_')
        FaultCause=attr[0];FaultPhaseNum=attr[1];FaultPhaseType=attr[-1]
        Time,Data,SignalNames,FaultTime=ReadRecord(file)

        SelectRow=(Time>=FaultTime-0.06)&(Time<=FaultTime+0.1) # 截取故障前2个周波，故障后10个周波进行分析
        Time=Time[SelectRow];Data=Data[SelectRow]
        SelectCol=np.array([False,False,False,True,False,False,False,False])
        Data=Data[:,SelectCol] # 选择A相电流
        fitting=Interp(Time,Data,kind='slinear',axis=0)

        fs=1000 # 插值固定的采样率
        TimeNew=np.arange(min(Time),max(Time),step=1.0/fs)[:-1]
        DataNew=fitting(TimeNew)
        time=TimeNew;signal=DataNew.ravel()

    if 1:

        wavelet='bior3.5';level=4
        coeffsRev=DwtRec(signal,wavelet,level)
        fig,axes=plt.subplots(level+1,1,figsize=(6,6))
        for row,ax in enumerate(axes.ravel(),start=1):
            ax.plot(time,coeffsRev[level+1-row]);ax.set_ylabel('尺度'+str(row))
#        axes[0].set_title(FaultCause)
        plt.show()

        if 1:
            plt.plot(time,signal);plt.ylabel('原始信号');
            yrange=max(signal)-min(signal)
            plt.ylim([min(signal)-1/10*yrange,max(signal)+1/10*yrange])
            plt.show()


#! /usr/bin/env python
# -*- coding   : utf-8 -*-
import os
import re
import ujson
from dotmap     import DotMap
from subprocess import check_output
from datetime   import datetime, timedelta

# output = '(Connected)' in check_output( "scutil --nc list".split( ' ' ) ).decode( 'utf-8' )

# print( output )
refresh_delta = timedelta( seconds = 10 )
now           = datetime.utcnow()
refresh_time  = now + refresh_delta

print( now )
print( refresh_time )

now_str          = now.isoformat()
refresh_time_str = refresh_time.isoformat()

print( now_str )
print( refresh_time_str )

now_reconstituted          = datetime.fromisoformat( now_str )
refresh_time_reconstituted = datetime.fromisoformat( refresh_time_str )

print( now_reconstituted )
print( refresh_time_reconstituted )

print( ['DONE'] )


# author: Jeremy Temple, Eli Manning
import enum

class Tokens(object):
    def __init__(self):
        self._data = []

    def __iter__(self):
        return iter(self._data)

    def append(self, token):
        self._data.append(token)

    @property
    def lookahead(self):
        return self._data[0]

    def consume(self):
        self._data.pop(0)

    def __str__(self):
        w = []
        for i in self._data:
            w.append("Kind: {0}\nValue: {1}\n\n".format(i._kind, i._value))
        return "".join(w)
class tokenenum(enum.Enum):
    BEGIN = enum.auto()
    DO = enum.auto()
    END = enum.auto()
    FALSE = enum.auto()
    HALT = enum.auto()
    IF = enum.auto()
    PRINT = enum.auto()
    PROGRAM = enum.auto()
    THEN = enum.auto()
    TRUE = enum.auto()
    VAR = enum.auto()
    WHILE = enum.auto()

class token(object):
    def __init__(self, kind, value):
        self._kind = kind
        self._value = value

    def __str__(self):
        return "{0} {1}".format(self.kind, self.value)

    @property
    def kind(self):
        return self._kind

    @property
    def value(self):
        return self._value

keywords = {"begin" : tokenenum.BEGIN,
           "do" : tokenenum.DO,
           "end" : tokenenum.END,
           "false" : tokenenum.FALSE,
           "halt" : tokenenum.HALT,
           "if" : tokenenum.IF,
            "print" : tokenenum.PRINT,
            "program" : tokenenum.PROGRAM,
            "then" : tokenenum.THEN,
            "true" : tokenenum.TRUE,
            "var" : tokenenum.VAR,
            "while" : tokenenum.WHILE}


import importlib
from pathlib import Path, PurePath
import glob
import os
import logging

import numpy as np
from pydub import AudioSegment
import pandas as pd
import cv2

# TODO: General solution
# TODO: Can string_filtering be called by generate_filenames?
# TODO: output only benign file?
# TODO: think about input output design of generate_filenames
# def generate_filename_list(path, file_key, dir_key='', only_filename=False):
#     input_paths, gt_paths = [], []
#     for root, dirs, files in os.walk(path):
#         for f in files:
#             if not only_filename:
#                 fullpath = os.path.join(root, f)
#             else:
#                 fullpath = f
#             if dir_key in fullpath:
#                 if file_key in fullpath:
#                     gt_paths.append(fullpath)
#                 else:
#                     input_paths.append(fullpath)
#     return input_paths, gt_paths

def get_melspec_from_cpp(wav_list_path, out_dir, sampling_rate=None):
    # Generate output path list (wav -> csv)
    out_path_list = []
    wav_list_dir, wav_list_filename = os.path.split(wav_list_path)
    with open(wav_list_path, 'r') as fw:
        in_path_list = fw.readlines()
    in_path_list = [f if f[-3:] == 'wav' else f[:-1] for f in in_path_list]

    csv_out_dir = os.path.join(out_dir, 'csv', wav_list_filename[:-4])
    os.makedirs(csv_out_dir, exist_ok=True)
    data_pair = {}
    for in_path in in_path_list:
        in_dir, in_file = os.path.split(in_path)
        out_file = in_file.replace('wav', 'csv')
        out_path = os.path.join(csv_out_dir, out_file)
        out_path_list.append(out_path)

        key = out_file[:-4]
        # label = int(os.path.split(in_dir)[1])
        label = 0
        data_pair[key] = {'label': label}

    csv_list_path = os.path.join(csv_out_dir, wav_list_filename)
    with open(csv_list_path, 'w+') as fw:
        for path in out_path_list:
            fw.write(f'{path}\n')

    # Cpp MelSpectrogram
    exe_file = Path(r'compute-mfcc.exe')
    inputlist = Path(wav_list_path)
    outputlist = Path(csv_list_path)
    command = (
        f'{exe_file.as_posix()} '
        f'--inputlist "{inputlist.as_posix()}" '
        f'--outputlist "{outputlist.as_posix()}" '
    )
    if sampling_rate is not None:
        command += f'--samplingrate {sampling_rate} '
    os.system(command)

    # Save CPP feature (csv -> npy)
    # csv_list = glob.glob(os.path.join(csv_out_dir, '*.csv'))
    csv_list = out_path_list
    img_out_dir = os.path.join(out_dir, 'img', wav_list_filename[:-4])
    os.makedirs(img_out_dir, exist_ok=True)
    img_save_paths = []
    for idx, csv_f in enumerate(csv_list):
        # if idx>10:break
        _, filename = os.path.split(csv_f)
        try:
            df = pd.read_csv(csv_f)
            # XXX: wrong data extraction for temp using
            # df = pd.read_csv(csv_f, header=None)
            data = df.to_numpy().T
        except pd.errors.EmptyDataError:
            print(f'- Empty pandas data {csv_f}')
            data = np.zeros(1)

        save_path = os.path.join(img_out_dir, filename.replace('csv', 'npy'))
        img_save_paths.append(save_path)
        data_pair[filename[:-4]]['path'] = save_path
        np.save(save_path, data)

    with open(os.path.join(out_dir, wav_list_filename), 'w+') as fw:
        fw.writelines(img_save_paths)

    inputs, paths, labels = [], [], []
    for k, v in data_pair.items():
        inputs.append(k[:-4])
        if 'path' in v:
            paths.append(v['path'])
        else:
            paths.append('')
        if 'label' in v:
            labels.append(v['label'])
        else:
            labels.append('')
        # labels.append(v['label'])
    df = pd.DataFrame({
        'input': inputs, 'img_path': paths, 'label': labels
    })
    df.to_csv(os.path.join(out_dir, wav_list_filename).replace('txt', 'csv'))

def get_dir_list(data_path, full_path=True):
    dir_list = np.array([], dtype=object)
    for f in os.listdir(data_path):
        folder_path = os.path.join(data_path, f)
        if os.path.isdir(folder_path):
            if full_path:
                dir_list = np.append(dir_list, folder_path)
            else:
                dir_list = np.append(dir_list, os.path.split(folder_path)[1])
    return list(dir_list)

def get_clips_from_audio(y, clip_time, hop_time):
    clips = []
    if y.duration_seconds >= clip_time:
        for t in range(0, int(y.duration_seconds)-clip_time+1, hop_time):
            start_t, end_t = t, t+clip_time
            clip = y[1000*start_t:1000*end_t]
            clips.append(clip)
    return clips

def continuous_split(path, clip_time, hop_time, sr, channels, add_volume, output_format='wav'):
    assert clip_time > 0 and hop_time > 0
    file_name = os.path.basename(path)
    file_format = file_name.split('.')[1]
    y = load_audio_waveform(path, file_format, sr, channels)
    save_path = os.path.join(os.path.split(path)[0], f'clips_{clip_time}_{hop_time}_{add_volume}dB')
    if not os.path.isdir(save_path):
        os.makedirs(save_path)

    y += add_volume
    clips = get_clips_from_audio(y, clip_time, hop_time)
    for idx, clip in enumerate(clips, 1):
        clip.export(os.path.join(save_path, file_name.replace(f'.{file_format}', f'_{idx:03d}.{output_format}')), output_format)
    print('Finish spitting.')
    return save_path

def save_aLL_files_name(path, name='file_names', keyword=[], filtering_mode='in', is_fullpath=True, shuffle=True, save_path=None):
    # file_names = get_file_names(path, keyword, filtering_mode, is_fullpath, shuffle)
    file_names = get_files(path, keys=keyword, return_fullpath=True, sort=True)
    if not save_path: save_path = path
    save_content_in_txt(
        file_names, os.path.join(save_path, f'{name}.txt'), filter_bank=[], access_mode='w+', dir=None)
    # with open(os.path.join(save_path, f'{name}.txt'), 'w+') as fw:
    #     for f in file_names:
    #         fw.write(f)
    #         fw.write('\n')

def get_ASUS_snoring_index(save_path, split, balancing=True):
    # TODO: Multiple class (miner)
    # Load and check dataset
    subject_list = []
    data_pair = {}
    train_pairs, valid_pairs = [], []
    df = pd.read_csv(save_path)

    for index, row in df.iterrows():
        pair = (row['path'], row['label'])
        if row['subject'] in data_pair:
            data_pair[row['subject']].append(pair)
        else:
            subject_list.append(row['subject'])
            data_pair[row['subject']] = [pair]
        # TODO: balancing

    # indexing
    if balancing:
        sample_count = {}
        for subject in subject_list:
            for sample in data_pair[subject]:
                if sample[1] in sample_count[subject]:
                    sample_count[subject][sample[1]] += 1
                else:
                    sample_count[subject][sample[1]] = 1

    else:
        sample_count = []
        for subject in subject_list:
            sample_count.append(len(data_pair[subject]))
        sorted_idx = np.argsort(np.array(sample_count))
        subject_list = np.take(subject_list, sorted_idx)
        # TODO: subject_list[::3]
        valid_subjects = subject_list[::3]
        train_subjects = list(set(subject_list)-set(valid_subjects))

        for subject in train_subjects:
            train_pairs.extend(data_pair[subject])

        for subject in valid_subjects:
            valid_pairs.extend(data_pair[subject])

    return train_pairs, valid_pairs

def load_input_data(config):
    assert isinstance(config.dataset.data_path, dict)
    total_train, total_valid = [], []
    for key in config.dataset.data_path:
        if key == 'ASUS_snoring':
            save_path = save_ASUS_snoring_index(config.dataset.data_path[key])
            train, valid = get_ASUS_snoring_index(save_path)
        elif key == 'esc-50':
            pass
        elif key == 'Kaggle_snoring':
            pass
        total_train.extend(train)
        total_valid.extend(valid)

    return total_train, total_valid

    return datasets_indexed

def get_pydub_sound(filename, audio_format, sr=None, channels=None):
    """
    Get Pydub sound object and set the basic params
    """
    # sound = AudioSegment.from_file(filename, 'mp4')
    sound = AudioSegment.from_file(filename, audio_format)
    if sr: sound = sound.set_frame_rate(sr)
    if channels: sound = sound.set_channels(channels)
    return sound

def load_audio_waveform(filename, audio_format, sr=None, channels=None):
    """
    Pydub based waveform loading function
    """
    sound = get_pydub_sound(filename, audio_format, sr, channels)
    waveform = np.array(sound.get_array_of_samples(), np.float32)
    sr = sound.frame_rate
    return waveform, sr

def get_files(path, keys=[], return_fullpath=True, sort=True, sorting_key=None):
    """Get all the file name under the given path with assigned keys
    Args:
        path: (str)
        keys: (list, str)
        return_fullpath: (bool)
        sort: (bool)
        sorting_key: (func)
    Return:
        file_list: (list)
    """
    file_list = []
    assert isinstance(keys, (list, str))
    if isinstance(keys, str): keys = [keys]
    # Rmove repeated keys
    keys = list(set(keys))

    def push_into_filelist(root, f, file_list, is_fullpath):
        if is_fullpath:
            file_list.append(os.path.join(root, f))
        else:
            file_list.append(f)

    for i, (root, dirs, files) in enumerate(os.walk(path)):
        for j, f in enumerate(files):
            if keys:
                for key in keys:
                    if key in f:
                        push_into_filelist(root, f, file_list, return_fullpath)
            else:
                push_into_filelist(root, f, file_list, return_fullpath)

    if file_list:
        if sort: file_list.sort(key=sorting_key)
    else:
        if keys:
            logging.warning(f'No file exist with key {keys}.')
        else:
            logging.warning(f'No file exist.')
    return file_list

def generate_filenames(path, keys=None, include_or_exclude=None, is_fullpath=True, loading_formats=None):
    """Get all the file name under the given path with assigned keys and including condition"""
    # TODO: index error when keys=['a','a','a'] include_or_exclude=['include', 'exclude', 'exclude']
    if len(keys) == 0: keys = None
    if len(include_or_exclude) == 0: include_or_exclude = None
    if keys and include_or_exclude:
        assert len(keys) == len(include_or_exclude)
        full_keys = [f'{condition}_{k}' for k, condition in zip(keys, include_or_exclude)]
        # TODO: logging instead of print for repeat key (think about raise error or not)
        if len(full_keys) != len(list(set(full_keys))):
            print('Warning: Repeat keys')
            full_keys = list(set(full_keys))
    if keys:
        filenames = {k: [] for k in full_keys}
    else:
        filenames = []

    for root, dirs, files in os.walk(path):
        for f in files:
            if loading_formats:
                process = False
                for format in loading_formats:
                    if format in f:
                        process = True
                        break
            else:
                process = True

            if process:
                if is_fullpath:
                    final_path = os.path.join(root, f)
                else:
                    final_path = f
                if keys:
                    for idx, k in enumerate(keys):
                        if include_or_exclude[idx] == 'include':
                            if k in final_path:
                                filenames[full_keys[idx]].append(final_path)
                        elif include_or_exclude[idx] == 'exclude':
                            if k not in final_path:
                                filenames[full_keys[idx]].append(final_path)
                        else:
                            raise ValueError('Unknown key condition')
                else:
                    filenames.append(final_path)
    return filenames

def get_class(class_name, modules):
    for module in modules:
        m = importlib.import_module(module)
        clazz = getattr(m, class_name, None)
        if clazz:
            return clazz
    raise RuntimeError(f'Unsupported dataset class: {class_name}')

def load_content_from_txt(path, access_mode='r') -> list:
    if isinstance(path, PurePath):
        path = str(path)
    with open(path, access_mode) as fw:
        content = fw.read().splitlines()
    return content

def inspect_data_split(data_split):
    # TODO: if split in 0.725 0.275
    train_split = data_split.get('train', 0)
    valid_split = data_split.get('valid', 0)
    test_split = data_split.get('test', 0)
    if train_split+valid_split+test_split != 1:
        raise ValueError('Incorrect splitting of dataset.')
    split_code = f'{10*train_split:.0f}{10*valid_split:.0f}{10*test_split:.0f}'
    return split_code

def get_data_path(data_path, index_root, data_split, keywords=[]):
    # TODO: save in csv
    # TODO: save with label
    split_code = inspect_data_split(data_split)
    dataset_name = os.path.split(data_path)[1]

    # Check if an index already exists, create one if not.
    index_path = os.path.join(index_root, dataset_name)
    if not os.path.isdir(index_path):
        os.mkdir(index_path)
        file_path_list = get_files(data_path, keys=keywords, return_fullpath=True, sort=True)
        train_split = int(data_split.get('train', 0)*len(file_path_list))
        valid_split = int(data_split.get('valid', 0)*len(file_path_list))
        test_split = int(data_split.get('test', 0)*len(file_path_list))

        train_path_list = file_path_list[:train_split]
        valid_path_list = file_path_list[train_split:train_split+valid_split]
        test_path_list = file_path_list[train_split+valid_split:train_split+valid_split+test_split]

        train_path = os.path.join(index_path, f'{dataset_name}_train_{split_code}.txt')
        valid_path = os.path.join(index_path, f'{dataset_name}_valid_{split_code}.txt')
        test_path = os.path.join(index_path, f'{dataset_name}_test_{split_code}.txt')

        save_content_in_txt(train_path_list, train_path)
        save_content_in_txt(valid_path_list, valid_path)
        save_content_in_txt(test_path_list, test_path)

        data_path_dict = {
            'train': train_path,
            'valid': valid_path,
            'test': test_path}
    else:
        file_path_list = get_files(index_root, return_fullpath=True, sort=True)
        data_path_dict
    return data_path_dict

def get_data_indices(data_name, data_path, save_path, data_split, mode, generate_index_func):
    """"Get dataset indices and create if not exist"""
    # # TODO: gt not exist condition
    # create index folder and sub-folder if not exist
    os.chdir(save_path)
    index_dir_name = f'{data_name}_data_index'
    sub_index_dir_name = f'{data_name}_{data_split[0]}_{data_split[1]}'
    input_data_path = os.path.join(save_path, index_dir_name, sub_index_dir_name)
    if not os.path.isdir(input_data_path): os.makedirs(input_data_path)

    # generate index list and save in txt file
    generate_index_func(data_path, data_split, input_data_path)

    # load index list from saved txt
    os.chdir(input_data_path)
    if os.path.isfile(f'{mode}.txt'):
        input_data_indices = load_content_from_txt(f'{mode}.txt')
        input_data_indices.sort()
    else:
        input_data_indices = None

    if os.path.isfile(f'{mode}_gt.txt'):
        ground_truth_indices = load_content_from_txt(f'{mode}_gt.txt')
        ground_truth_indices.sort()
    else:
        ground_truth_indices = None
    return input_data_indices, ground_truth_indices

def generate_kaggle_breast_ultrasound_index(data_path, save_path, data_split):
    data_keys = {'input': 'exclude_mask', 'ground_truth': 'include_mask'}
    save_input_and_label_index(data_path, save_path, data_split, data_keys, loading_format=['png', 'jpg'])

def generate_kaggle_snoring_index(data_path, save_path, data_split):
    # TODO: no ground truth case
    # TODO: keys=None, include_or_exclude=None case
    data_keys = {'input': None}
    save_input_and_label_index(data_path, save_path, data_split, data_keys, loading_format=['wav', 'm4a'])

def save_input_and_label_index(data_path, save_path, data_split, data_keys=None, loading_format=None):
    # TODO: test input output
    # assert 'input' in data_keys, 'Undefined input data key'
    # assert 'ground_truth' in data_keys, 'Undefined ground truth data key'
    class_name = os.listdir(data_path)
    os.chdir(save_path)
    include_or_exclude, keys = [], []
    if data_keys:
        for v in data_keys.values():
            if v:
                include_or_exclude.append(v.split('_')[0])
                keys.append(v.split('_')[1])
    data_dict = generate_filenames(
        data_path, keys=keys, include_or_exclude=include_or_exclude, is_fullpath=True, loading_formats=loading_format)

    def save_content_ops(data, train_name, valid_name):
        # TODO: Is this a solid solution?
        data.sort(key=len)
        split = int(len(data)*data_split[0])
        train_input_data, val_input_data = data[:split], data[split:]
        save_content_in_txt(train_input_data,  train_name, filter_bank=class_name, access_mode="w+", dir=save_path)
        save_content_in_txt(val_input_data, valid_name, filter_bank=class_name, access_mode="w+", dir=save_path)

    # if data_keys['input']:
    #     input_data = data_dict[data_keys['input']]
    # else:
    #     input_data = data_dict

    if data_keys:
        if 'input' in data_keys:
            if data_keys['input']:
                input_data = data_dict[data_keys['input']]
            else:
                input_data = data_dict
            save_content_ops(input_data, 'train.txt', 'valid.txt')
        if 'ground_truth' in data_keys:
            if data_keys['ground_truth']:
                ground_truth = data_dict[data_keys['ground_truth']]
            else:
                ground_truth = data_dict
            save_content_ops(ground_truth, 'train_gt.txt', 'valid_gt.txt')
    else:
        input_data = data_dict
        save_content_ops(input_data, 'train.txt', 'valid.txt')

# TODO: mkdir?
def save_content_in_txt(content, path, filter_bank=None, access_mode='a+', dir=None):
    # assert isinstance(content, (str, list, tuple, dict))
    # TODO: overwrite warning
    with open(path, access_mode) as fw:
        # def string_ops(s, dir, filter):
        #     pair = string_filtering(s, filter)
        #     return os.path.join(dir, list(pair.keys())[0], list(pair.values())[0])

        if isinstance(content, str):
            # if dir:
            #     content = string_ops(content, dir, filter=filter_bank)
            #     # content = os.path.join(dir, content)
            fw.write(content)
        else:
            for c in content:
                # if dir:
                #     c = string_ops(c, dir, filter=filter_bank)
                #     # c = os.path.join(dir, c)
                fw.write(f'{c}\n')

def get_path_generator_in_case_order(data_path, return_fullpath, load_format=[]):
    dir_list = get_dir_list(data_path)
    for d in dir_list:
        file_list = get_files(d, keys=load_format, return_fullpath=return_fullpath)
        for file_idx, f in enumerate(file_list):
            yield (file_idx, f)

def save_data_label_pair_in_csv(data_path, save_path=None, save_name=None, load_format='wav', return_fullpath=True):
    path_loader = get_path_generator_in_case_order(data_path, return_fullpath, load_format=load_format)
    nums, ids, labels = [], [], []
    for idx, file_and_idx in enumerate(path_loader, 1):
        file_idx, f = file_and_idx
        file_path, file_name = os.path.split(f)
        label = int(file_path[-1])
        file_name = file_name.split('.')[0]
        print(idx, file_name, label)
        nums.append(file_idx)
        ids.append(file_name)
        labels.append(label)

    pair_dict = {'case_index': nums,
                 'id': ids,
                 'label': labels}
    pair_df = pd.DataFrame(pair_dict)
    if not save_name:
       save_name = 'train.csv'
    if save_path is not None:
        pair_df.to_csv(os.path.join(save_path, save_name))
    else:
        pair_df.to_csv(save_name)

def save_fileanmes_in_txt(glob_path, save_path=None, recursive=True):
    files = glob.glob(glob_path, recursive=recursive)
    if not save_path:
        save_path = 'filenames.txt'

    with open(save_path, 'w') as fw:
        for file in files:
            fw.write(f'{file}\n')
    return files

def generate_gt_csv_for_data(id_to_label, save_path):
    pass

if __name__ == "__main__":
    from dataset.melspec import melspec
    melspec()

    # data_path = rf'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\raw_final_test\freq6_no_limit\2_21\raw_f_h_2_mono_16k'
    # save_data_label_pair_in_csv(data_path, save_name='train1.csv')
    # data_path = rf'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\raw_final_test\freq6_no_limit_shift\2_21\raw_f_h_2_mono_16k'
    # save_data_label_pair_in_csv(data_path, save_name='train2.csv')
    # data_path = rf'C:\Users\test\Desktop\Leon\Datasets\ESC-50\ESC-50_process\esc50_16k\esc50_16k_2'
    # save_data_label_pair_in_csv(data_path, save_name='train3.csv')

    # # ASUS snoring
    # train_wav_list_path = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\index\Freq2\2_21_2s_my2\train.txt'
    # test_wav_list_path = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\index\Freq2\2_21_2s_my2\test.txt'
    # out_dir = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\preprocess\2_21_2s_my2'
    # # out_dir = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_cpp\2_21_2s_my2'
    # get_melspec_from_cpp(train_wav_list_path, out_dir)
    # get_melspec_from_cpp(test_wav_list_path, out_dir)

    # # ESC-50
    # # wav_list_path = r'C:\Users\test\Desktop\Leon\Datasets\ESC-50\ESC-50_process\esc50\esc50_2\test.txt'
    # wav_list_path = r'C:\Users\test\Desktop\Leon\Datasets\ESC-50\ESC-50_process\esc50\esc50_2\file_names.txt'
    # out_dir = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\preprocess\esc50\44100'
    # # out_dir = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_cpp\esc50\44100'
    # get_melspec_from_cpp(wav_list_path, out_dir, sampling_rate=44100)

    # Kaggle snoring
    # wav_list_path = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\index\Kaggle_snoring_full\valid.txt'
    # out_dir = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_cpp\kaggle'
    # get_melspec_from_cpp(wav_list_path, out_dir, sampling_rate=48000)

    # # ASUS new
    # redmi = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_0727\wave_split\1658889529250_RedmiNote8\0'
    # pixel = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_0727\wave_split\1658889531056_Pixel4XL\0'
    # iphone = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_0727\wave_split\1658889531172_iPhone11\0'
    # sr = 16000

    # # for data_path in [redmi, pixel, iphone]:
    # for data_path in [redmi]:
    #     split = os.path.split(os.path.split(data_path)[0])[1]
    #     glob_path = os.path.join(data_path, '*.wav')
    #     save_path = os.path.join(data_path, 'filenames.txt')
    #     save_fileanmes_in_txt(glob_path, recursive=True, save_path=save_path)

    #     wav_list_path = save_path
    #     out_dir = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_cpp'
    #     out_dir = os.path.join(out_dir, split, str(sr))
    #     get_melspec_from_cpp(wav_list_path, out_dir, sampling_rate=sr)


# -*- coding: utf-8 -*-
'''
    :codeauthor: :email:`Rahul Handay <rahulha@saltstack.com>`
'''

# Import Python Libs
from __future__ import absolute_import

# Import Salt Testing Libs
from salttesting import TestCase, skipIf
from salttesting.helpers import ensure_in_syspath
from salttesting.mock import (
    MagicMock,
    patch,
    NO_MOCK,
    NO_MOCK_REASON
)

ensure_in_syspath('../../')

# Import Salt Libs
from salt.states import mdadm

# Globals
mdadm.__salt__ = {}
mdadm.__opts__ = {}

@skipIf(NO_MOCK, NO_MOCK_REASON)
class MdadmTestCase(TestCase):
    '''
        Validate the mdadm state
    '''
    def test_present(self):
        '''
            Test to verify that the raid is present
        '''
        ret = [{'changes': {}, 'comment': 'Raid salt already present',
                'name': 'salt', 'result': True},
               {'changes': {},
                'comment': "Devices are a mix of RAID constituents"
                " (['dev0']) and non-RAID-constituents(['dev1']).",
                'name': 'salt', 'result': False},
               {'changes': {},
                'comment': 'Raid will be created with: True', 'name': 'salt',
                'result': None},
               {'changes': {}, 'comment': 'Raid salt failed to be created.',
                'name': 'salt', 'result': False}]

        mock = MagicMock(side_effect=[{'salt': True}, {'salt': False},
                                      {'salt': False}, {'salt': False},
                                      {'salt': False}])
        with patch.dict(mdadm.__salt__, {'raid.list': mock}):
            self.assertEqual(mdadm.present("salt", 5, "dev0"), ret[0])

            mock = MagicMock(side_effect=[0, 1])
            with patch.dict(mdadm.__salt__, {'cmd.retcode': mock}):
                self.assertDictEqual(mdadm.present("salt", 5,
                                                   ["dev0", "dev1"]),
                                     ret[1])

            mock = MagicMock(return_value=True)
            with patch.dict(mdadm.__salt__, {'cmd.retcode': mock}):
                with patch.dict(mdadm.__opts__, {'test': True}):
                    with patch.dict(mdadm.__salt__, {'raid.create': mock}):
                        self.assertDictEqual(mdadm.present("salt", 5, "dev0"),
                                             ret[2])

                with patch.dict(mdadm.__opts__, {'test': False}):
                    with patch.dict(mdadm.__salt__, {'raid.create': mock}):
                        self.assertDictEqual(mdadm.present("salt", 5, "dev0"),
                                             ret[3])

    def test_absent(self):
        '''
            Test to verify that the raid is absent
        '''
        ret = [{'changes': {}, 'comment': 'Raid salt already absent',
                'name': 'salt', 'result': True},
               {'changes': {},
                'comment': 'Raid saltstack is set to be destroyed',
                'name': 'saltstack', 'result': None},
               {'changes': {}, 'comment': 'Raid saltstack has been destroyed',
                'name': 'saltstack', 'result': True}]

        mock = MagicMock(return_value=["saltstack"])
        with patch.dict(mdadm.__salt__, {'raid.list': mock}):
            self.assertDictEqual(mdadm.absent("salt"), ret[0])

            with patch.dict(mdadm.__opts__, {'test': True}):
                self.assertDictEqual(mdadm.absent("saltstack"), ret[1])

            with patch.dict(mdadm.__opts__, {'test': False}):
                mock = MagicMock(return_value=True)
                with patch.dict(mdadm.__salt__, {'raid.destroy': mock}):
                    self.assertDictEqual(mdadm.absent("saltstack"), ret[2])

if __name__ == '__main__':
    from integration import run_tests
    run_tests(MdadmTestCase, needs_daemon=False)


#
#
# UtmTrav : calculation of an open and cloded traverse
#           projected on planeor UTM projected grid.
#
#
from pygeodesy.dms import toDMS, parseDMS
from pygeodesy import Utm,parseUTM5,Ellipsoids
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import yaml,sys
from pathlib import Path
###############################################################
def parse_dms( dms ):
    return parseDMS( dms, sep=':')

class UTMTraverse:
    def __init__(self, YAMLFILE):
        with open(YAMLFILE,'r') as f:
        	YAML = yaml.safe_load( f )
        self.df_Sta = pd.DataFrame.from_dict(YAML['FIXPNT'] ,
            orient='index', columns=['Easting','Northing'] )
        def UtmStr(row):
            UTM_PRE = f"{YAML['ZONE']} {YAML['HEMI']} "
            return '{} {} {}'.format( UTM_PRE, row.Easting, row.Northing )
        self.df_Sta['UTMSTR'] =  self.df_Sta.apply( UtmStr, axis='columns')
        self.df_Sta['Utm'] = self.df_Sta['UTMSTR'].apply(parseUTM5)
        self.df_Sta.drop( columns=['Easting','Northing'], axis='index', inplace=True )
        self.df_Sta['Iter'] = -1
        self.df_Sta.reset_index(inplace=True)
        self.df_Sta.rename( columns={'index':'Name'}, inplace=True)
        self.df_Sta['Name'] = self.df_Sta['Name'].astype( str )
        ##############################
        self.df_Dis = pd.DataFrame.from_dict( YAML['DIST'],orient='index',
                            columns=['dist_m'] )
        self.df_Dis['Iter'] = -1
        self.df_Dis['ScalFac'] = 0.0
        self.df_Dis.reset_index(inplace=True)
        self.df_Dis.rename( columns={'index':'Name'}, inplace=True)
        ##############################
        self.df_Ang = pd.DataFrame.from_dict( YAML['ANGL'],orient='index',
                            columns=['ang_dms'] )
        self.df_Ang['ang_deg'] = self.df_Ang['ang_dms'].apply( parse_dms )
        self.df_Ang['Iter'] = -1
        self.df_Ang[['ac_BS','ac_FS']] = [0.,0.]
        self.df_Ang['type'] = 'open'
        self.df_Ang.reset_index(inplace=True)
        self.df_Ang.rename( columns={'index':'Name'}, inplace=True)
        ##############################
        self.YAML = YAML
        #import pdb; pdb.set_trace()
        return

    def CalcTraverse(self,ITER):
        df_Sta = self.df_Sta[( self.df_Sta['Iter']==self.df_Sta['Iter'].max() )
                          |  (self.df_Sta['Iter']==-1) ]
        df_Dis = self.df_Dis[self.df_Dis['Iter']==-1]
        df_Ang = self.df_Ang[self.df_Ang['Iter']==-1]
        for i in range(len(df_Dis)):
            DIS_AT,DIS_TO = self.df_Dis.iloc[i]['Name'].split(',')
            DIST          = self.df_Dis.iloc[i]['dist_m']
            ANG_BS,ANG_AT,ANG_FS = self.df_Ang.iloc[i]['Name'].split(',')
            ANGL                 = self.df_Ang.loc[i]['ang_deg']
            TYPE                 = self.df_Ang.loc[i]['type']
            UtmBS = self.df_Sta[self.df_Sta['Name']==ANG_BS].iloc[-1]['Utm']
            UtmAT = self.df_Sta[self.df_Sta['Name']==ANG_AT].iloc[-1]['Utm']
            ######################################
            if ITER==0:
                k = 1.0 ; ac1 = 0.0 ; ac2 = 0.0
            else:
                UtmFS = self.df_Sta[self.df_Sta['Name']==ANG_FS].iloc[-1]['Utm']
                k   = self.LineSF( UtmAT, UtmFS)
                ac1 = self.LineAC( UtmAT, UtmBS)
                ac2 = self.LineAC( UtmAT, UtmFS)
                #ac1 = 27.17/3600; #ac2 = 20.67/3600
            BS_Azi = self.GridAz( UtmAT, UtmBS  )
            corrAng =  ANGL + (ac1-ac2 )
            FS_Azi = BS_Azi + corrAng
            _,FS_Azi = divmod( FS_Azi, +360 )
            ######################################
            GD = k*DIST
            ETo = UtmAT.easting  + GD*np.sin( np.radians(FS_Azi) )
            NTo = UtmAT.northing + GD*np.cos( np.radians(FS_Azi) )
            ######################################
            df_StaX = pd.DataFrame( { 'Name': ANG_FS,
                'UTMSTR':None, 'Utm': Utm( self.YAML['ZONE'], self.YAML['HEMI'],
                ETo,NTo),'Iter': ITER }, index=[0]  )
            self.df_Sta = pd.concat( [self.df_Sta, df_StaX], axis=0, ignore_index=True )
            df_DisX = pd.DataFrame( { 'Name':f'{DIS_AT},{DIS_TO}',
                'dist_m': GD, 'AdjAz':FS_Azi, 'Iter': ITER, 'ScalFac': k }, index=[0] )
            self.df_Dis = pd.concat( [self.df_Dis,df_DisX], axis=0, ignore_index=True )
            df_AngX= pd.DataFrame( { 'Name': f'{ANG_BS},{ANG_AT},{ANG_FS}',
                'ang_dms': toDMS(corrAng), 'ang_deg': corrAng, 'Iter': ITER,
                'ac_BS': ac1*3600, 'ac_FS': ac2*3600, 'type':TYPE }, index=[0] )
            self.df_Ang = pd.concat( [ self.df_Ang, df_AngX ], axis=0,ignore_index=True )
        #import pdb; pdb.set_trace()
        return self.df_Sta, self.df_Dis, self.df_Ang

    def GridAz( self, UtmFr, UtmTo ):
      dE = UtmTo.easting-UtmFr.easting
      dN = UtmTo.northing-UtmFr.northing
      az = np.arctan2( dE, dN )
      _,az = divmod( az, np.pi*2 )  # 2PI
      return np.degrees( az )

    def LineSF( self,StaFr,StaTo ):
        utm_mid = Utm( self.YAML['ZONE'], self.YAML['HEMI'],
                (StaFr.easting +StaTo.easting)/2,
                (StaFr.northing+StaTo.northing)/2  )
        sf_to  = StaTo.toLatLon().scale
        sf_mid = utm_mid.toLatLon().scale
        sf_fr  = StaFr.toLatLon().scale
        sf_line = (1/6)*(sf_fr+4*sf_mid+sf_to)  # quadratic mean
        #sf_line = (sf_fr+sf_to)/2  # average
        return sf_line

    def LineAC( self,StaFr,StaTo ):
        if 'ARC2CHORD' in self.YAML.keys() and self.YAML['ARC2CHORD'] is True:
            utm_mid = Utm( self.YAML['ZONE'], self.YAML['HEMI'],
                     (StaFr.easting+StaTo.easting)/2,
                        (StaFr.northing+StaTo.northing)/2  )
            rm = Ellipsoids.WGS84.rocGauss(utm_mid.toLatLon().lat)
            N2N1 = StaTo.northing-StaFr.northing
            E2E1 = (StaTo.easting-500_000) + 2*( StaFr.easting-500_000)
            del12 = np.degrees( N2N1*E2E1/(6*rm*rm) )
            return del12
        else:
            return 0.0

    def CloseTrav( self ):
        print('####### Adjust Interior Angles of the Close Traverse ########')
        self.JuncPnt = self.YAML['JUNCTION']
        df_JuncAng = self.df_Ang[self.df_Ang['Name'].str.contains(
                                             f',{self.JuncPnt},' )]
        assert( len(df_JuncAng) == 2),\
           f"***ERROR*** {self.JuncPnt} must have 2 records in ANGL..."
        ##### partition open & close travesre #####
        df_AngCloTrv = self.df_Ang[ self.df_Ang.Name==df_JuncAng.iloc[1].Name ]
        nameAng = df_AngCloTrv.Name.str.split(',').to_list()
        nameAng = np.array( nameAng )
        idx = df_AngCloTrv.index.values.astype(int)[0]
        df_AngCloTrv = self.df_Ang.iloc[ idx: ]
        ##### angle correction due to angel excess #####
        sumAng = df_AngCloTrv.ang_deg.sum()
        sumAng_= 180*(len(df_AngCloTrv)-2)
        nAng = len( df_AngCloTrv )
        difAng = sumAng-sumAng_
        print(f'Close traverse number of angles : {nAng}')
        print(f'Close traverse angle excess (n-2)*180 : {difAng*3600:+.0f} sec.')
        self.df_Ang.loc[ idx:idx+nAng, 'ang_deg' ] = \
            self.df_Ang.loc[ idx:idx+nAng, 'ang_deg' ]-difAng/nAng  # ***ANG_ADJ***
        self.df_Ang.loc[ idx:idx+nAng, 'type' ] = 'closed'
        self.df_Ang['ang_dms'] = self.df_Ang['ang_deg'].apply( toDMS  )
        assert(np.isclose(self.df_Ang.iloc[idx:].ang_deg.sum(),sumAng_ )),"Not closed!"
        print('Close traverse sum of angels          : {:.7f} deg'.format(
                                df_AngCloTrv.ang_deg.sum() ) )
        ##### merge junction angles #####
        jncAngSum = df_JuncAng.ang_deg.sum()
        jncAngNam = self.df_Ang.iloc[idx-1].Name+','+self.df_Ang.iloc[idx].Name
        jncAngNam = jncAngNam.split(',')
        assert( jncAngNam[1]==jncAngNam[4] or jncAngNam[2]==jncAngNam[3]),\
                      f'***ERROR*** cannot merge {jncAngNam}'
        jncAng = jncAngNam[0:2]+ [jncAngNam[-1],]
        jncAng = ",".join( jncAng )
        self.df_Ang.loc[ idx, ['Name', 'ang_deg', 'type' ] ] =\
                                       [jncAng,jncAngSum,'junction']
        self.df_Ang.drop( idx-1, axis='index', inplace=True )
        return

    def Clousure( self ):
        JPNT=trav.YAML['JUNCTION']
        jpnt  = df_Sta[df_Sta.Name==JPNT].iloc[0].Utm
        jpnt_ = df_Sta[df_Sta.Name==JPNT+'_'].iloc[0].Utm
        self.dE = jpnt.easting -jpnt_.easting
        self.dN = jpnt.northing-jpnt_.northing
        self.dJPNT = np.sqrt( self.dE*self.dE + self.dN*self.dN )

        print(f'Closure at junction {JPNT:6} : {self.dJPNT:+.3f} m.')
        self.idxJPNT =trav.df_Dis[trav.df_Dis.Name.str.startswith(f'{JPNT},')].iloc[-1].name
        self.sumTrv = trav.df_Dis.iloc[self.idxJPNT:].dist_m.sum()
        print(f'Sum of closed traverse      : {self.sumTrv:,.3f} m.')
        print(f'Linear closure              :  1: {self.sumTrv/self.dJPNT:,.0f} ')

    def AdjustCompassRule( self):
        dE= self.dE ;    dN=self.dN    ; dS=self.dJPNT
        df_DL = df_Dis.iloc[self.idxJPNT:].copy()
        df_DL.drop( columns=['ScalFac'], axis='index', inplace=True )
        #import pdb; pdb.set_trace()
        df_DL['Dep'] = df_DL['dist_m']*np.sin(np.radians(df_DL['AdjAz']))
        df_DL['Lat'] = df_DL['dist_m']*np.cos(np.radians(df_DL['AdjAz']))
        sumDep = df_DL['Dep'].sum()
        sumLat = df_DL['Lat'].sum()
        sumDis = df_DL.dist_m.sum()
        df_DL['AdjDep'] = df_DL['Dep'] - sumDep*df_DL['dist_m']/sumDis
        df_DL['AdjLat'] = df_DL['Lat'] - sumLat*df_DL['dist_m']/sumDis
        df_DL_ = df_DL.copy()
        for col in ( 'Dep','Lat','AdjDep','AdjLat' ):
            df_DL_[col] = df_DL_[col].map('{:10.3f}'.format )
        print( df_DL_ )
        #import pdb; pdb.set_trace()
        sumAdjDep = df_DL['AdjDep'].sum()
        sumAdjLat = df_DL['AdjLat'].sum()
        print( f'sumDist={sumDis:.3f}m | sumDep={sumDep:+.3f}m sumLat={sumLat:+.3f}m | '\
               f'sumAdjDep={sumAdjDep:.3f}m sumAdjLat={sumAdjLat:.3f}m' )
        #####################################
        print( '=== Adjusted Coordiate by Compass Rule ===')
        print( '=Station=====Easting========Northing=====')
        FRutm = None
        for i,row in df_DL.iterrows():
            FR,TO = row.Name.split(',')
            if FRutm is None:
                FRutm = trav.df_Sta[ trav.df_Sta.Name==FR ].iloc[-1].Utm
                E,N = FRutm.easting, FRutm.northing
                print( f'{FR:6s} {E:15,.3f}  {N:15,.3f}' )
            E = E + row.AdjDep
            N = N + row.AdjLat
            print( f'{TO:6s} {E:15,.3f}  {N:15,.3f}' )

    def Plot(self, TITLE):
        df_Sta = self.df_Sta[self.df_Sta.Iter<=0]
        df_Dis = self.df_Dis[self.df_Dis.Iter==0]
        #import pdb; pdb.set_trace()
        for i,row in df_Sta.iterrows():
	        #print( i,row )
                if row.Iter==-1: ms = 20; fc='green'
                else:            ms = 10; fc='red'
                plt.plot( [row.Utm.easting], [row.Utm.northing],
                        marker="^", markersize=ms, mfc=fc, mec=fc )
                plt.text( row.Utm.easting,row.Utm.northing, row.Name )
        for i,row in df_Dis.iterrows():
	        #print( i, row )
	        FR,TO = row.Name.split(',')
	        fr = df_Sta[df_Sta.Name==FR].iloc[-1].Utm
	        to = df_Sta[df_Sta.Name==TO].iloc[-1].Utm
	        plt.plot( [fr.easting,to.easting] , [fr.northing,to.northing], '-r' )
        plt.ticklabel_format(useOffset = False, style = 'plain')
        plt.xticks(rotation=90)
        plt.gca().set_aspect("equal")
        plt.grid()
        plt.tight_layout()
        plt.title( TITLE )
        #plt.show()
        plt.savefig( TITLE )

###########################################################
###########################################################
###########################################################
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)

if len(sys.argv)==2:
    YAMLFILE = Path(sys.argv[1])
else:
    YAMLFILE = Path( './Dec17_Travese_Compute/P4_Trav.yaml' )
trav = UTMTraverse( YAMLFILE )
### Case closed traverse , adjust interior angles #########
if 'JUNCTION' in trav.YAML.keys():
    trav.CloseTrav()

###########################################################
for i in range(trav.YAML['ITER']):
    print(f'========================= Iter {i} ============================')
    df_Sta,df_Dis,df_Ang = trav.CalcTraverse(ITER=i)
trav.Clousure()
###########################################################
def _toUtmStr(row):
    if row.UTMSTR is None:
        return row.Utm.toStr(prec=-3)
    else:
        return row.UTMSTR
df_Sta['UTMSTR'] = df_Sta.apply( _toUtmStr, axis='columns' )
df_Dis['ScalFac'] = df_Dis['ScalFac'].map( '{:.9f}'.format )
if 1:
    print( df_Sta )
    print( df_Dis )
    print( df_Ang )
if trav.YAML['ITER']>=2:
    dE=list(); dN=list()
    for grp,row in df_Sta.groupby('Name'):
        MAX = row.Iter.max()
        if MAX==trav.YAML['ITER']-1:
            dE.append( row.iloc[MAX].Utm.easting- row.iloc[MAX-1].Utm.easting )
            dN.append( row.iloc[MAX].Utm.northing- row.iloc[MAX-1].Utm.northing )
    print( 'Converting@{} dE : {}'.format(trav.YAML['ITER'], np.array( dE ).round(3) ) )
    print( 'Converting@{} dN : {}'.format(trav.YAML['ITER'], np.array( dN ).round(3) ) )
for fmt in ('png','pdf','svg'):
    PLOT = f'./CACHE/{YAMLFILE.stem}.{fmt}'
    print( f'Plotting {PLOT}...')
    trav.Plot( PLOT )
#trav.Plot( (YAMLFILE.stem + '.pdf' ) )
#############################################################
trav.AdjustCompassRule()
print( '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ End of UtmTrav.py @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@')
#import pdb; pdb.set_trace()


#!/usr/bin/env python3.8

"""
Author : Kashif Khan
This script helps to search Endpoint in following criteria.
1: All results
2: Filter by Node ID
3: Filter by EPG
4: Filter by VLAN ID
5: Filter by Interface Name
6: Filter by Tenant Name
7: Filter by MAC Address

"""
# Imports block
from connectivity import get_aci_token
from credentials import credentials
import json
import requests
from pprint import pprint
from prettytable import PrettyTable
import csv

def get_ep_details(aci_cookie, apic_ip):
    """
    Fetches EP details using API call

    Parameters:

    aci_cookie (dict): Session dictionary
    apic_ip (string): APIC controller ip address
    mac_address (string): (Optional) default is None. It searches specific mac-address

    Returns:
    dict: API response in JSON

    """

    url = f'{apic_ip}/api/node/class/fvCEp.json?rsp-subtree=full&rsp-subtree-class=fvCEp,fvRsCEpToPathEp,fvIp,fvRsHyper,fvRsToNic,fvRsToVm'

    headers = {
        'cache-control': "no-cache"
    }

    get_response = requests.get(
        url, headers=headers, cookies=aci_cookie, verify=False).json()

    return get_response

def get_processed_data(get_ep_details_result):

    data = []
    fields = ['mac', 'ip','encap','dn','path']

    for each_ep in get_ep_details_result['imdata']:
        line_dict = {}
        for key,value in each_ep['fvCEp'].items():

            if isinstance (value,list):
                for each_tdn in value:
                    if each_tdn.get('fvRsCEpToPathEp'):
                        line_dict.update({'path':each_tdn['fvRsCEpToPathEp']['attributes']['tDn']})
            else:
                line_dict.update({'mac':value['mac']})
                line_dict.update({'ip':value['ip']})
                line_dict.update({'encap':value['encap']})
                line_dict.update({'dn':value['dn']})

        data.append(line_dict)

    processed_data = list()

    for row in data:

        dn_splitted_list = row['dn'].split("/")
        tdn_splitted_list = row['path'].split("/")[2:]

        switch_splitted = tdn_splitted_list[0].split("-")[1:]
        tdn_splitted_list = tdn_splitted_list[1:]
        tdn_splitted_list = "/".join(tdn_splitted_list)
        interface_splitted = tdn_splitted_list.split("-")[1:]

        mac = row['mac']
        vlan = row['encap'].lstrip("vlan-")
        tenant = dn_splitted_list[1].lstrip("tn-")
        ap = dn_splitted_list[2].lstrip("ap-")
        epg = dn_splitted_list[3].lstrip("epg-")
        switch = "-".join(switch_splitted)
        interface = "-".join(interface_splitted).strip("[]")
        temp_dict = {'mac_address':mac,'vlan':vlan,'tenant':tenant,'application_profile':ap,'epg':epg,'switch':switch,'interface':interface}

        processed_data.append(temp_dict)
    return processed_data

def print_details_onscreen(processed_data):

    table = PrettyTable()
    table.field_names = ['MAC Address','VLAN','Tenant','AP','EPG','Switch','Interface']

    table.align['Interface'] = 'l'
    table.align['MAC Address'] = 'l'
    table.align['VLAN'] = 'l'
    table.align['Tenant'] = 'l'
    table.align['AP'] = 'l'
    table.align['EPG'] = 'l'
    table.align['Switch'] = 'l'

    count = 0
    for each_row in processed_data:

        count += 1
        only_values = [values for values in each_row.values()]
        table.add_row(only_values)  # adds each mac address detail in table.

    print(table)
    print(f"Total number of interfaces where Endpoint mac-address learnd are {count}")

def get_filtered_data_func(filter_value,filter_type,get_data):

    get_filtered_data = [filtered_data
                        for filtered_data in get_data
                        if filter_value in filtered_data[filter_type]]
    print_details_onscreen(get_filtered_data)

def save_to_csv(list_of_all_data):

    keys = list_of_all_data[0].keys()

    with open('ep_data.csv', 'w', newline='')  as output_file:

        dict_writer = csv.DictWriter(output_file, keys)
        dict_writer.writeheader()
        dict_writer.writerows(list_of_all_data)

    print('\n' + '-'*30)
    print("File has been saved!!!")
    print('-'*30 + '\n')

def main():

    aci_cookie = get_aci_token(
        credentials["username"], credentials["password"], credentials["apic_ip"])
    get_ep_details_result = get_ep_details(aci_cookie, credentials["apic_ip"])

    get_data = get_processed_data(get_ep_details_result)

    main_operations_list = ['Exit',
                            'Print Endpoint details on screen',
                            'Save data to CSV']

    while True:

        for index,main_items in enumerate(main_operations_list,0):
            print(f"{index}: {main_items}")

        main_operation = input('\nChoose number to select type of operation : ')
        if main_operation == '0':
            break

        elif main_operation == '1':
            sub_operations1_list = ['Exit',
                                    'All results',
                                    'Filter by Node ID',
                                    'Filter by EPG',
                                    'Filter by VLAN ID',
                                    'Filter by Interface Name',
                                    'Filter by Tenant Name',
                                    'Filter by MAC Address',
                                    ]

            while True:
                for index,sub_menu_items in enumerate(sub_operations1_list,0):
                    print(f"{index}: {sub_menu_items}")

                subops1 = input('\nChoose number to select type of operation : ')
                if subops1 == '0':
                    break

                elif subops1 == '1':
                    print_details_onscreen(get_data)

                elif subops1 =='2':
                    filter_value = input("Enter Node ID: ")
                    filter_type='switch'
                    if len(filter_value) != 3:
                        print('Wrong Node ID! try again')
                        continue
                    get_filtered_data_func(filter_value,filter_type,get_data)

                elif subops1 =='3':
                    filter_value = input("Enter EPG: ")
                    filter_type='epg'
                    get_filtered_data_func(filter_value,filter_type,get_data)

                elif subops1 =='4':
                    filter_value = input("Enter VLAN ID: ")
                    filter_type='vlan'
                    get_filtered_data_func(filter_value,filter_type,get_data)

                elif subops1 =='5':
                    filter_value = input("Enter Interface ID: ")
                    filter_type='interface'
                    get_filtered_data_func(filter_value,filter_type,get_data)

                elif subops1 =='6':
                    filter_value = input("Enter Tenant Name: ")
                    filter_type='tenant'
                    get_filtered_data_func(filter_value,filter_type,get_data)

                elif subops1 =='7':
                    filter_value = input("Enter MAC Address: ").upper()
                    filter_type='mac_address'
                    get_filtered_data_func(filter_value,filter_type,get_data)

        elif main_operation == '2':
            save_to_csv(get_data)

if __name__ == '__main__':
    main()


import pyrealsense2 as rs
import numpy as np
from pybot.cmn_structs import *
from pybot.sensor_structs import *

class RealsenseInterface():
    def __init__(self):
        self.pipeline = rs.pipeline()
        config = rs.config()
        config.enable_stream(rs.stream.depth,1280, 720, rs.format.z16, 30)
        config.enable_stream(rs.stream.color, 1920, 1080, rs.format.bgr8, 30)
        config.enable_stream(rs.stream.accel, rs.format.motion_xyz32f, 250)
        config.enable_stream(rs.stream.gyro, rs.format.motion_xyz32f, 200)
        # Start streaming
        profile = self.pipeline.start(config)
        depth_sensor = profile.get_device().first_depth_sensor()
        align_to = rs.stream.color
        self.align = rs.align(align_to)

    def __next__(self):
        frames = self.pipeline.wait_for_frames()
        # Align the depth frame to color frame
        aligned_frames = self.align.process(frames)

        # Get aligned frames
        aligned_depth_frame = aligned_frames.get_depth_frame() # aligned_depth_frame is a 640x480 depth image
        color_frame = aligned_frames.get_color_frame()
        for frame in frames:
            if frame.is_motion_frame():
                pose_frame = frame.as_motion_frame().get_motion_data()
        # Validate that both frames are valid
        if not aligned_depth_frame or not color_frame:
            print("Either color or depth frame is invalid, skipping")
            return

        depth_image = np.array(aligned_depth_frame.get_data()).astype(np.int64)
        color_image = np.array(color_frame.get_data()).astype(np.int64)

        i = color_frame.profile.as_video_stream_profile().get_intrinsics()
        intrin = np.array([[i.fx,0,i.ppx,0],[0,i.fy,i.ppy,0],[0,0,1,0],[0,0,0,1]]).astype(np.int64)

        #convert to our structs
        header = make_header()
        height,width=color_image.shape[:2]
        color_image = Image(header,height,width,"BGR8",color_image)
        depth_image = Image(header,height,width,"Z16",depth_image)
        intrin = CameraInfo(height,width,None,None,intrin,None,None)
        return {'color':color_image,'depth':depth_image,'intrin':intrin}


import os
import pickle
import numpy as np
import pandas as pd
import sdv
from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler
from scipy.stats import pearsonr
from sklearn import preprocessing
from sklearn.feature_selection import VarianceThreshold
from sklearn.linear_model import Lasso
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.utils import shuffle
from hyperparameters import classifiers, param_grids

from settings import file_extension, undersample, oversample, noise, synthetic, lasso

def create_folders():
    folders = ['results', 'models'] if "validate" not in file_extension else ['results']
    for name in folders:
        directory = file_extension + '_' + name + '/'
        if not os.path.exists(directory): os.makedirs(directory)

def read_files():
    df_radiomics = pd.read_excel('data/' + file_extension + "_radiomics.xlsx", engine='openpyxl')
    df_radiomics = df_radiomics.loc[:, ~df_radiomics.columns.duplicated()]
    start_idx = df_radiomics.columns.get_loc("original_shape_Elongation")
    df_radiomics = df_radiomics.iloc[:, start_idx:]

    df_events = pd.read_excel('data/' + "db_basis_" + file_extension + ".xlsx", engine='openpyxl')
    df_events = df_events.rename(columns={"Randomisatie nummer": "PP"})
    df_events = df_events.loc[:, ~df_events.columns.str.contains('^Unnamed')]

    df = pd.merge(df_radiomics, df_events, on='PP', how='outer').drop_duplicates()
    df = df[df['Event'].notna()]

    return df

def train_test_split(df, test_size):
    df = shuffle(df)

    df_test = df.iloc[:test_size, :]
    df_test = df_test.reset_index()
    df_test = df_test.drop(columns='index')

    df_train = df.iloc[test_size:, :]
    df_train = df_train.reset_index()
    df_train = df_train.drop(columns='index')

    if synthetic:
        model = sdv.tabular.GaussianCopula(primary_key='PP')
        model.fit(df_train)
        df_train += model.sample(200)

    return df_train, df_test

def normalize_data(df, min_max_scaler=None):
    events = df.Event.values
    vals = df.drop(columns=['Event', 'PP']).values
    cols = df.drop(columns=['Event', 'PP']).columns
    if not min_max_scaler: min_max_scaler = preprocessing.MinMaxScaler()
    vals_scaled = min_max_scaler.fit_transform(vals)
    df = pd.DataFrame(vals_scaled, columns=cols)
    df['Event'] = events
    return df, min_max_scaler

def variance_threshold_selector(data, threshold=0.5):
    selector = VarianceThreshold(threshold)
    selector.fit(data)
    return data[data.columns[selector.get_support(indices=True)]]

def remove_variance(df):
    min_variance = 1e-3
    low_variance = variance_threshold_selector(df, min_variance)
    return low_variance

def pearson_correlation(df):
    df_pearson = []
    for index in range(0, len(df.columns) - 1):
        name = df.columns[index]
        df_pearson.append({'Name': name,
                           'Correlation': pearsonr(df.Event, df.iloc[:, index])[0],
                           'P-value': pearsonr(df.Event, df.iloc[:, index])[1]})
    df_pearson = pd.DataFrame(df_pearson)
    df_pearson = df_pearson[df_pearson['P-value'] < 0.05]
    features = df_pearson.Name.to_list() + ['Event']
    df_train = df.loc[:, features]
    return df_train, features

def lasso_reduction(df_train, df_test, features):
    x_train = df_train.to_numpy()[:, :-1]
    y_train = df_train.Event.to_numpy()

    pipeline = Pipeline([('scaler', StandardScaler()), ('model', Lasso())])
    search = GridSearchCV(pipeline, {'model__alpha': np.arange(0.1, 10, 0.1)}, cv=5, scoring="neg_mean_squared_error",
                          verbose=0)
    search.fit(x_train, y_train)
    coefficients = search.best_estimator_.named_steps['model'].coef_
    importance = np.abs(coefficients)
    features = np.array(features[0:len(features) - 1])
    features_lasso = list(features[importance > 0]) + ['Event']

    df_train = df_train.loc[:, features_lasso]
    df_test = df_test.loc[:, features_lasso]

    return df_train, df_test, features_lasso

def preprocess_train_data(df_train):
    df_train, min_max_scaler = normalize_data(df_train)
    df_train = remove_variance(df_train)
    if not lasso: df_train, features = pearson_correlation(df_train)
    if lasso: features = df_train.columns

    return df_train, min_max_scaler, features

def preprocess_test_data(df, min_max_scaler, features):
    df, _ = normalize_data(df, min_max_scaler=min_max_scaler)

    return df.loc[:, features]

def preprocess_data(df_train, df_test):
    df_train, min_max_scaler, features = preprocess_train_data(df_train)
    df_test = preprocess_test_data(df_test, min_max_scaler, features)
    if lasso: df_train, df_test, features = lasso_reduction(df_train, df_test, features)

    x_train, y_train = df_train.to_numpy()[:, :-1], df_train.Event.to_numpy()
    x_test, y_test = df_test.to_numpy()[:, :-1], df_test.Event.to_numpy()

    return x_train, y_train, x_test, y_test, min_max_scaler, features

def get_best_grid(x_train, y_train, model):
    if 0.9 * len(y_train) - sum(y_train) > sum(y_train):
        oversampler = RandomOverSampler(sampling_strategy=0.90)
        if oversample: x_train, y_train = oversampler.fit_resample(x_train, y_train)
    undersampler = RandomUnderSampler(sampling_strategy='majority')
    if undersample: x_train, y_train = undersampler.fit_resample(x_train, y_train)
    if noise: x_train = x_train + np.random.normal(0.0, 0.2, size=x_train.shape)

    param_grid = param_grids[model]
    grid = GridSearchCV(classifiers[model], param_grid=param_grid, scoring="roc_auc", refit=True, cv=5, n_jobs=-1,
                        verbose=0)
    grid.fit(x_train, y_train)
    return grid

def save_model(best_grid, x_test, y_test, features, scaler, model):
    pickle.dump(best_grid, open(file_extension + '_models/' + model + '_model.pkl', 'wb'))
    pickle.dump(x_test, open(file_extension + '_models/' + model + '_x_test.pkl', 'wb'))
    pickle.dump(y_test, open(file_extension + '_models/' + model + '_y_test.pkl', 'wb'))
    pickle.dump(features, open(file_extension + '_models/' + model + '_features.pkl', 'wb'))
    pickle.dump(scaler, open(file_extension + '_models/' + model + '_scaler.pkl', 'wb'))

def jitter(x, scale=0.1):
    return x + np.random.normal(0, scale, x.shape)

def jitter_test(classifier, x_test, y_test, scales=np.linspace(0, 0.5, 30), n=5):
    out = []
    for s in scales:
        avg = 0.0
        for r in range(n):
            avg += roc_auc_score(y_test, classifier.predict_proba(jitter(x_test, s))[:, 1])
        out.append(avg / n)
    return out, scales


import discord
import os
import json
import gspread
from oauth2client.service_account import ServiceAccountCredentials
import asyncio
import random
from keep_alive import keep_alive

scope = ["https://spreadsheets.google.com/feeds",'https://www.googleapis.com/auth/spreadsheets',"https://www.googleapis.com/auth/drive.file","https://www.googleapis.com/auth/drive"]
creds = ServiceAccountCredentials.from_json_keyfile_dict(json.loads(os.getenv("SERVICE CREDS")), scope)
client = gspread.authorize(creds)
sheet = client.open("Amber's Secret Santa 2022").worksheet("Gift Reveal")

participants = sheet.row_count - 1
AMBER_ID = 607544531042304010
GIFTER_ID_COL = 4
GIFTEE_ID_COL = 7
PARTNER_COL = 8
ARTWORK_COL = 10
GALLERY_ID = 1031026351391645767

intents = discord.Intents.default()
intents.members = True
client = discord.Client(intents=intents)

@client.event
async def on_ready():
  print('We have logged in as {0.user}'.format(client))

@client.event
async def on_message(message):
  if message.author == client.user:
    return

  #bot recieves dm
  if isinstance(message.channel, discord.DMChannel):

    #amber sends testids command
    if message.content.startswith(">testids") and message.author.id == AMBER_ID:
      msg = ""
      for i in range(participants):
        rowNum = i+2

        gifterID = int(sheet.cell(rowNum, GIFTER_ID_COL).value)
        gifter = client.get_user(gifterID)

        if gifter == None:
          amber = client.get_user(AMBER_ID)
          msg = msg + str(rowNum) + " " + str(gifterID) + "\n"
        else:
          amber = client.get_user(AMBER_ID)
          msg = msg + str(rowNum) + " <@" + str(gifterID) + ">\n"

        #avoid quota limit
        if (i + 1) % 30 == 0:
          amber = client.get_user(AMBER_ID)
          await amber.send(msg)
          msg = ""
          await asyncio.sleep(60)
        else:
          sentAll = False

      if not sentAll:
        amber = client.get_user(AMBER_ID)
        await amber.send(msg)

    #amber sends sendpartners command
    if message.content.startswith(">sendpartners") and message.author.id == AMBER_ID:
      for i in range(participants):
        rowNum = i+2

        #avoid quota limit
        if (i + 1) % 30 == 0:
          await asyncio.sleep(60)

        gifterID = int(sheet.cell(rowNum, GIFTER_ID_COL).value)
        gifteeID = int(sheet.cell(rowNum, GIFTEE_ID_COL).value)

        gifter = client.get_user(gifterID)
        giftee = client.get_user(gifteeID)

        try:
          if gifter == None:
            amber = client.get_user(AMBER_ID)
            await amber.send(str(rowNum) + " Gifter " + str(gifterID) + " not in server")
          elif giftee == None:
            amber = client.get_user(AMBER_ID)
            await amber.send(str(rowNum) + " Giftee " + str(gifteeID) + " not in server")
          else:
            await gifter.send("Hey <@" + str(gifterID) + ">! \n\nYour giftee for Amber's Secret Santa 2022 is " + giftee.name + "#" + giftee.discriminator + "! Don't tell anyone who your giftee is, it's a surprise! To find your giftee's wishlist, search 'in: wishlists from: " + giftee.name + "#" + giftee.discriminator + "' in the server! \n\nPlease dm a sketch of your artwork to your group leader by **December 14th**! Let your group leader know if you need an extension! \n\nWhenever you're ready to submit your finished artwork, dm '>submit' to this bot and attach your file(s) to the message! Please let your group leader know when you've submitted so that we can double check your submission. To change your submission, send the '>submit' command again and attach your updated file(s), this will replace all previous files. The final artwork deadline is **December 23rd**! \n\nHave fun! :D")
            sheet.update_cell(rowNum, PARTNER_COL, "sent")
        except:
          amber = client.get_user(AMBER_ID)
          await amber.send(str(rowNum) + " Unknown error sending giftee " + str(gifteeID) + " to gifter " + str(gifterID))

    #anyone sends submit command
    if message.content.startswith(">submit"):
      correctFileTypes = True
      senderID = message.author.id

      try:
        if message.attachments:
          allFiles = ""
          confirmMsg = ""
          fileCount = 0

          senderCell = sheet.find(str(senderID), in_row = None, in_column = GIFTER_ID_COL)

          if senderCell:
            senderRow = senderCell.row

            for i in range(len(message.attachments)):
              attachment = message.attachments[i]
              if attachment.content_type in ('image/jpeg', 'image/gif', 'image/png', 'image/heic', 'video/mp4'):
                file = attachment.url
                allFiles = allFiles + file + " "
                fileCount += 1
                confirmMsg = confirmMsg + ":white_check_mark: File " + str(i+1) + " received: " + file + "\n\n"
              else:
                correctFileTypes = False
                confirmMsg = confirmMsg + ":x: Error: File " + str(i+1) + " is not a jpeg, png, gif, heic or mp4 file!\n\n"

            await message.channel.send(confirmMsg)

            if correctFileTypes:
              try:
                sheet.update_cell(senderRow, ARTWORK_COL, allFiles)
                await message.channel.send(str(fileCount) + " file(s) submitted successfully!")
              except:
                await message.channel.send("Failed to add artwork to database. Please dm amber ASAP!")
            else:
              await message.channel.send("Submission failed. Please double check your file formats and try again!")

          else:
            await message.channel.send("No user found. Please dm amber ASAP!")
        else:
          await message.channel.send("No file detected. Please attach your file(s) to your '>submit' message!")
      except:
        await message.channel.send("Unknown error. Please dm amber ASAP!")

    #amber sends testsubmit command
    '''if message.content.startswith(">testsubmit") and message.author.id == AMBER_ID:
      confirmMsg = "gottem"
      fileCount = 1

      for i in range(participants):
        senderID = i

        try:
          file = "https://cdn.discordapp.com/attachments/1041724795357773957/1045794420781490326/anya_-_angry.png"
          senderCell = sheet.find(str(senderID), in_row = None, in_column = GIFTER_ID_COL)

          if senderCell:
            senderRow = senderCell.row

            await message.channel.send(confirmMsg)

            try:
              sheet.update_cell(senderRow, ARTWORK_COL, file)
              await message.channel.send(str(fileCount) + " file(s) submitted!")
            except:
              await message.channel.send("Failed to submit artwork. Please dm amber!")
          else:
            await message.channel.send("No user found. Please dm amber!")
        except:
          await message.channel.send("Unknown error. Please dm amber ASAP!")'''

    #amber sends serversendart command
    if message.content.startswith(">serversendart") and message.author.id == AMBER_ID:
      allPuns = ["Hold on for deer life, this artwork for @ will blow you away!", "This artwork for @ will make you fall in love at frost sight!", "This artwork for @ is snow amazing!", "This artwork for @ is looking tree-mendous!", "This gift for @ sleighs!", "This artwork for @ is the best thing since sliced (ginger)bread!", "This gorgeous gift for @ graces us with its presents!", "It’s rien-ning gorgeous artwork! This one’s for @!", "Tree-t your eyes to this amazing artwork for @!", "This gift for @ is be-yule-tiful!", "You have no i-deer how dazzling this artwork for @ is!", "This amazing gift for @ will leave you feeling santa-mental!", "This pine art for @ will spruce things up!", "This gift for @ is snow joke!", "This jolly artwork for @ will leave a fa-la-la-la-lasting impression!"]

      for i in range(participants):
        rowNum = i+2

        #avoid quota limit
        if (i + 1) % 30 == 0:
          await asyncio.sleep(60)

        gifteeID = int(sheet.cell(rowNum, GIFTEE_ID_COL).value)
        giftee = client.get_user(gifteeID)
        links = sheet.cell(rowNum, ARTWORK_COL).value

        try:
          if links == None:
            amber = client.get_user(AMBER_ID)
            await amber.send(str(rowNum) + " Giftee <@" + str(gifteeID) + "> no links")
          else:
            pun = random.choice(allPuns)
            allPuns.remove(pun)
            pun = pun.replace("@", "<@" + str(gifteeID) + ">")

            galleryChannel = client.get_channel(GALLERY_ID)
            await galleryChannel.send(pun + " " + links)

            if len(allPuns) == 0:
              allPuns = ["Hold on for deer life, this artwork for @ will blow you away!", "This artwork for @ will make you fall in love at frost sight!", "This artwork for @ is snow amazing!", "This artwork for @ is looking tree-mendous!", "This gift for @ sleighs!", "This artwork for @ is the best thing since sliced (ginger)bread!", "This gorgeous gift for @ graces us with its presents!", "It’s rien-ning gorgeous artwork! This one’s for @!", "Tree-t your eyes to this amazing artwork for @!", "This gift for @ is be-yule-tiful!", "You have no i-deer how dazzling this artwork for @ is!", "This amazing gift for @ will leave you feeling santa-mental!", "This pine art for @ will spruce things up!", "This gift for @ is snow joke!", "This jolly artwork for @ will leave a fa-la-la-la-lasting impression!"]
        except:
          amber = client.get_user(AMBER_ID)
          await amber.send(str(rowNum) + "Unknown error for giftee " + str(gifteeID))

    #amber sends dmsendart command
    '''if message.content.startswith(">dmsendart") and message.author.id == AMBER_ID:
      for i in range(participants):
        rowNum = i+2

        #avoid quota limit
        if (i + 1) % 30 == 0:
          await asyncio.sleep(60)

        gifteeID = int(sheet.cell(rowNum, GIFTEE_ID_COL).value)
        giftee = client.get_user(gifteeID)
        links = sheet.cell(rowNum, ARTWORK_COL).value

        try:
          if giftee == None:
            amber = client.get_user(AMBER_ID)
            await amber.send(str(rowNum) + " Giftee " + str(gifteeID) + " not in server")
          elif links == None:
            amber = client.get_user(AMBER_ID)
            await amber.send(str(rowNum) + " Giftee <@" + str(gifteeID) + "> no links")
          else:
            await giftee.send("Hey <@" + str(gifteeID) + ">! Here's the gift you recieved for Amber's Secret Santa 2022! " + links + "\n\nHead over to the #gift-gallery channel in the server to take a look at all the gifts from this event (including what you gifted)! You can also try to guess who your gifter was in the #gifter-guessing channel, the first person to guess correctly will be awarded their own emote in my main discord server, amber's jam jar: <https://discord.gg/hcgwvwApXR>, and everyone who guesses correctly will receive a special role! You have 5 guesses!")
        except:
          amber = client.get_user(AMBER_ID)
          await amber.send(str(rowNum) + " Unknown error for giftee " + str(gifteeID))'''

    if message.content.startswith(">editmsg") and message.author.id == AMBER_ID:
      channel = client.get_channel(GALLERY_ID)
      message = await channel.fetch_message(1056436525002403894)
      await message.edit(content="These artworks for <@1044953902065406003> are the best thing since sliced (ginger)bread! https://cdn.discordapp.com/attachments/1056215594644553779/1056602058272292974/ADBDC920-1386-4DC2-B16A-36855DFBE5D6.jpg https://cdn.discordapp.com/attachments/914323249884708965/1056436177206525952/Untitled_Artwork.png (there were a few complications with your gift, so you got 2!)")

keep_alive()
try:
  client.run(os.getenv("TOKEN"))
except:
  os.system("kill 1")


from collections import defaultdict

class Solution:
    def calcEquation(self, equations: list, values: list, queries: list) -> list:
        graph = defaultdict(list)
        for i in range(len(equations)):
            d1, d2 = equations[i]
            val = values[i]
            graph[d1].append((d2, val))
            graph[d2].append((d1, 1 / val))

        def dfs(d1, d2, visted, curVal):
            if d1 == d2:
                return curVal

            if d1 in visted:
                return -1

            visted.add(d1)

            tmp = -1
            for child, val in graph[d1]:
                tmp = max(tmp,dfs(child, d2, visted, curVal * val))
            return tmp

        ans = []
        for d1, d2 in queries:
            if d1 in graph and d2 in graph:
                ans.append(dfs(d1, d2, set(), 1))
            else:
                ans.append(-1)

        return ans