import nibabel as nib
import numpy as np
import os
import json
import nibabel.processing as nip
import nibabel.orientations as nio
from scipy.interpolate import interp1d, RegularGridInterpolator
from scipy.signal import savgol_filter
import matplotlib.pyplot as plt
import PIL
from io import BytesIO
from matplotlib.colors import ListedColormap, Normalize
from matplotlib.patches import Circle
import matplotlib as mpl
import warnings
from pathlib import Path
import pandas as pd
import math
import time
from copy import deepcopy
from skimage.transform import resize

mpl.rcParams['savefig.pad_inches'] = 0
v_dict = {1: 'C1', 2: 'C2', 3: 'C3', 4: 'C4', 5: 'C5', 6: 'C6', 7: 'C7',
    8: 'T1', 9: 'T2', 10: 'T3', 11: 'T4', 12: 'T5', 13: 'T6', 14: 'T7',
    15: 'T8', 16: 'T9', 17: 'T10', 18: 'T11', 19: 'T12', 20: 'L1',
    21: 'L2', 22: 'L3', 23: 'L4', 24: 'L5', 25: 'L6', 26: 'Sacrum',
    27: 'Cocc', 28: 'T13'
}

colors_itk = (1/255)*np.array([
    [255,  0,  0], [  0,255,  0], [  0,  0,255], [255,255,  0], [  0,255,255],
    [255,  0,255], [255,239,213],  # Label 1-7 (C1-7)
    [  0,  0,205], [205,133, 63], [210,180,140], [102,205,170], [  0,  0,128],
    [  0,139,139], [ 46,139, 87], [255,228,225], [106, 90,205], [221,160,221],
    [233,150,122], [165, 42, 42],  # Label 8-19 (T1-12)
    [255,250,250], [147,112,219], [218,112,214], [ 75,  0,130], [255,182,193],
    [ 60,179,113], [255,235,205],  # Label 20-26 (L1-6, sacrum)
    [255,235,205], [255,228,196],  # Label 27 cocc, 28 T13,
    [218,165, 32], [  0,128,128], [188,143,143], [255,105,180],
    [255,  0,  0], [  0,255,  0], [  0,  0,255], [255,255,  0], [  0,255,255],
    [255,  0,255], [255,239,213],  # 29-39 unused
    [  0,  0,205], [205,133, 63], [210,180,140], [102,205,170], [  0,  0,128],
    [  0,139,139], [ 46,139, 87], [255,228,225], [106, 90,205], [221,160,221],
    [233,150,122],   # Label 40-50 (subregions)
    [255,250,250], [147,112,219], [218,112,214], [ 75,  0,130], [255,182,193],
    [ 60,179,113], [255,235,205], [255,105,180], [165, 42, 42], [188,143,143],
    [255,235,205], [255,228,196], [218,165, 32], [  0,128,128] # rest unused
    ])
cm_itk = ListedColormap(colors_itk)
cm_itk.set_bad(color='w', alpha=0)

wdw_sbone = Normalize(vmin=-500, vmax=1300, clip=True)
wdw_hbone = Normalize(vmin=-200, vmax=1000, clip=True)

cm_sr = (1/255)*np.array([
        [104,159,56], [ 216,67,28]
        ])

cm_sr = ListedColormap(cm_sr)
cm_sr.set_bad(color='w', alpha=0)

cm_bmd_co = (1/255)*np.array([
        [0,0,0],[104,159,56], [255,179,0], [ 216,67,28], [183,28,28], # green, yellow, red, dark red for >120, >80, >50, <50
        ])

cm_bmd = ListedColormap(cm_bmd_co)
cm_bmd.set_bad(color='w', alpha=0)

colors_labels = (1/255)*np.array([
    [  0,  0,  0],  #
    [230,255,  0],  #   "Label 1"
    [255,247,  0],  #   "Label 3"
    [255,162,  0],  #   "Label 5"
    [255,  0, 12],  #   "Label 4"
    [166,  0,255],  #   "Label 5"
    [  0,  0,255],  #   "Label 6"
    [ 20,108,  0],  #   "Label 7"
    [104,159, 56], #   "Label 8"
    [255,179,  0], #   "Label 9"
    [216, 67, 28], #   "Label 10"
    [183, 28, 28]  #   "Label 11"
    ])

cm_labl = ListedColormap(colors_labels)
cm_labl.set_bad(color='w', alpha=0)  # set NaN to full opacity for overlay

def plot_bmd_label(axs, ctd, zms, bmd_val, size=3, text=True):
    # requires v_dict = dictionary of mask labels and a pd dataframe with labels "bmd_val"
    try:
        bmd_val = float(bmd_val)

        if bmd_val > 120:
            bmd = 1
        elif bmd_val > 80:
            bmd = 2
        elif bmd_val > 50:
            bmd = 3
        else:
            bmd = 4
#         print('bmd ', bmd)
        for v in ctd[1:]:
            if v[0] ==22:
                axs.add_patch(Circle((v[2]*zms[1], v[1]*zms[0]), size, color=cm_bmd_co[bmd]))
                if text:
                    axs.text(v[2]*zms[1]- 40, v[1]*zms[0]+2, str(bmd_val), fontdict={'color': cm_bmd(bmd), 'weight': 'bold'}, backgroundcolor='black')
    except Exception as e:
        pass

def initialize_database(rater):
    csv_path = 'CTFU_Fx_{}.csv'.format(rater)
    if not os.path.isfile(csv_path):
        columns = ['ID', 'type', 'ce',
                   'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7',
                   'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'T8', 'T9', 'T10', 'T11', 'T12',
                   'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'T13']
        df = pd.DataFrame(columns= columns)
        df.to_csv(csv_path, index=False)
    return csv_path

def populate_database(database_path, fx_dict, img_path, typ):
    print(database_path)
    df = pd.read_csv(database_path)
    fx_dict['ID'] = str(os.path.basename(img_path).replace('_ct.nii.gz', ''))
    if fx_dict['ID'] in df.ID.values:
        idx = df.index[df['ID']==fx_dict['ID']].tolist()
        df = df.drop(index=idx, axis=0)# = fx
    fx_dict['ce'] = get_ce(img_path)
    fx_dict['type'] = typ
    df = df.append(fx_dict, ignore_index=True)
    df.to_csv(database_path, index=False)
    return True

def get_ce(pth):
    try:
        ce = str(pth).split('ce-')[1].split('_')[0]
    except Exception:
        ce = ''
    return ce

def reorient_to(img, axcodes_to=('P', 'I', 'R'), verb=False, msk=True):

    # Note: nibabel axes codes describe the direction not origin of axes
    # direction PIR+ = origin ASL
    aff = img.affine
    if msk:
        arr = np.asanyarray(img.dataobj, dtype=img.dataobj.dtype)
    else:
        arr = img.get_fdata()
    ornt_fr = nio.io_orientation(aff)
    ornt_to = nio.axcodes2ornt(axcodes_to)
    ornt_trans = nio.ornt_transform(ornt_fr, ornt_to)
    arr = nio.apply_orientation(arr, ornt_trans)
    aff_trans = nio.inv_ornt_aff(ornt_trans, arr.shape)
    newaff = np.matmul(aff, aff_trans)
    newimg = nib.Nifti1Image(arr, newaff)
    if verb:
        print("[*] Image reoriented from", nio.ornt2axcodes(ornt_fr), "to", axcodes_to)
    return newimg

def get_paddings(msk1, msk2, ctd1, ctd2):

    rat_pad_1_1, rat_pad_1_2 = 0, 0
    rat_pad_2_1, rat_pad_2_2 = 0, 0

    ctd_dict1 = {x[0] : [x[1], x[2], x[3]] for x in ctd1[1:]}
    ctd_dict2 = {x[0] : [x[1], x[2], x[3]] for x in ctd2[1:]}

    if msk2.shape[1] > msk1.shape[1]:
        rat_pad_1_1 = len([x for x in ctd_dict2.keys() if x<min(list(ctd_dict1.keys()))])
        rat_pad_1_2 = len([x for x in ctd_dict2.keys() if x>max(list(ctd_dict1.keys()))])

        to_pad_tot = msk2.shape[1] - msk1.shape[1]
        rat_pad_1_1 = (rat_pad_1_1/(rat_pad_1_1 + rat_pad_1_2+1e-7)) * to_pad_tot
        rat_pad_1_2 = (rat_pad_1_2/(rat_pad_1_1 + rat_pad_1_2+1e-7)) * to_pad_tot

    if msk1.shape[1] > msk2.shape[1] > 0:
        to_pad_tot = msk1.shape[1] - msk2.shape[1]
        rat_pad_2_1 = len([x for x in ctd_dict1.keys() if x<min(list(ctd_dict2.keys()))])
        rat_pad_2_2 = len([x for x in ctd_dict1.keys() if x>max(list(ctd_dict2.keys()))])

        rat_pad_2_1 = (rat_pad_2_1/(rat_pad_2_1 + rat_pad_2_2+1e-7)) * to_pad_tot
        rat_pad_2_2 = (rat_pad_2_2/(rat_pad_2_1 + rat_pad_2_2+1e-7)) * to_pad_tot

    if msk2.shape[0] > msk1.shape[0]:
        to_pad_rl_1 = msk2.shape[0] - msk1.shape[0]
        to_pad_rl_2 = 0
    elif msk1.shape[0]>  msk2.shape[0]:
        to_pad_rl_2 = msk1.shape[0] - msk2.shape[0]
        to_pad_rl_1 = 0
    else:
        to_pad_rl_1,  to_pad_rl_2= 0, 0

    if msk2.shape[2] > msk1.shape[2]:
        to_pad_io_1 = msk2.shape[2] - msk1.shape[2]
        to_pad_io_2 = 0
    elif msk1.shape[2]>  msk2.shape[2]:
        to_pad_io_2 = msk1.shape[2] - msk2.shape[2]
        to_pad_io_1 = 0
    else:
        to_pad_io_1,  to_pad_io_2= 0, 0

    return int(rat_pad_1_1), int(rat_pad_1_2), int(rat_pad_2_1), int(rat_pad_2_2), int(to_pad_rl_1), int(to_pad_rl_2), int(to_pad_io_1),  int(to_pad_io_2)

def pad_arr(arr1, arr2, pads, msk=False):
    if not msk:
        cnst = (-1024)
    else:
        cnst = (0)

    new_arr1 = np.pad(
        arr1,
        [(0, int(pads[4])), (int(pads[0]), int(pads[1])), (0, int(pads[6]))],
        mode='constant',
        constant_values = cnst
    )
    new_arr2 = np.pad(
        arr2,
        [(0, pads[5]), (pads[2], pads[3]), (0, pads[7])],
        mode='constant',
        constant_values = cnst
    )
    return new_arr1, new_arr2

def pad_ctds(ctd1, ctd2, pads):
    new_ctd1 = [[x[0], x[1], x[2]+pads[0], x[3]] if isinstance(x,list) else x for x in  ctd1]

    new_ctd2 = [[x[0], x[1], x[2]+pads[2], x[3]] if isinstance(x,list) else x for x in  ctd2]
    return new_ctd1, new_ctd2

def pad_niftis(bimg, bmsk,sr, fimg, fmsk, res, bctd, fctd):
    bimg_data = bimg.get_fdata()
    bmsk_data = np.asanyarray(bmsk.dataobj, dtype=bmsk.dataobj.dtype)
    fimg_data = fimg.get_fdata()
    fmsk_data = np.asanyarray(fmsk.dataobj, dtype=fmsk.dataobj.dtype)

    res_data = np.asanyarray(res.dataobj, dtype=res.dataobj.dtype)
    sr_arr = np.asanyarray(sr.dataobj, dtype=sr.dataobj.dtype)

    pads = get_paddings(bmsk_data,fmsk_data,bctd,fctd)

    bimg_data, fimg_data =  pad_arr(bimg_data, fimg_data, pads, msk=False)

    bmsk_data, fmsk_data =  pad_arr(bmsk_data, fmsk_data, pads, msk=True)
    sr_arr, _ = pad_arr(sr_arr, sr_arr, pads, msk=True)
    res_data, _ = pad_arr(res_data, res_data, pads, msk=True)

    bctd1, fctd1 = pad_ctds(bctd, fctd, pads)

    bimg1 = nib.Nifti1Image(bimg_data,bimg.affine)
    bmsk1 = nib.Nifti1Image(bmsk_data,bmsk.affine)
    res1 = nib.Nifti1Image(res_data,res.affine)
    sr1 = nib.Nifti1Image(sr_arr,sr.affine)
    fimg1 = nib.Nifti1Image(fimg_data,fimg.affine)
    fmsk1 = nib.Nifti1Image(fmsk_data,fmsk.affine)
    return bimg1, bmsk1,sr1, fimg1, fmsk1, res1, bctd1, fctd1

def get_labels_from_csv(dataframe, case_id, exclude_cervical=False):
    verts = ['C2', 'C3', 'C4', 'C5', 'C6', 'C7',
                   'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'T8', 'T9', 'T10', 'T11', 'T12',
                   'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'T13']
    try:
        all_entries = dataframe[dataframe['ID']==case_id].to_dict(orient='records')[0]
        v_bmd = all_entries.get('vBMD_L3')
        if np.isnan(v_bmd):
            v_bmd = ''

    except:
        return pd.DataFrame,0
    return dataframe[dataframe['ID']==case_id], v_bmd

def get_labels_from_imp(dataframe, img_path):
    verts = ['C2', 'C3', 'C4', 'C5', 'C6', 'C7',
                   'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'T8', 'T9', 'T10', 'T11', 'T12',
                   'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'T13']
    try:
        case_id =  get_csv_entry(img_path)
        all_entries = dataframe[dataframe['ID']==case_id].to_dict(orient='records')[0]
        labels_dict = {x:y for x,y in all_entries.items() if 'def_' in x and not np.isnan(y)}

    except:
        return {}

    return labels_dict

def process_data_snp_sub(rater_dict, labels, dpi=120):
    img_pth = rater_dict['b_img_pth']
    fimg_pth = rater_dict['f_img_pth']
    sub_path = rater_dict['sub_pth']
    # img_pth, msk_pth, sr_pth, ctd_pth,fimg_pth, fmsk_pth, fctd_pth, sub_path, labels, dpi=120
    msk_pth, sr_pth, ctd_pth = get_derivative_path_from_raw_ct(img_pth)
    fmsk_pth, fsr_pth, fctd_pth = get_derivative_path_from_raw_ct(fimg_pth)

    b_labels, bbmd = get_labels_from_csv(labels, get_csv_entry(img_pth))
    f_labels, fbmd = get_labels_from_csv(labels, get_csv_entry(fimg_pth))

    bimg = nib.load(img_pth)
    bmsk = nib.load(msk_pth)
    bctd = load_centroids(ctd_pth)
    sub = nib.load(sub_path)
    sr = nib.load(sr_pth)
    b_ex_cents = [x[0] for x in bctd[1:] if isinstance(x[0], int)]

    fimg = nib.load(fimg_pth)
    fmsk = nib.load(fmsk_pth)
    fctd = load_centroids(fctd_pth)
    f_ex_cents = [x[0] for x in fctd[1:] if isinstance(x[0], int)]

    to_remove_bs = [x for x in b_ex_cents if x not in f_ex_cents]

    bimg, bmsk,sr, fimg, fmsk, sub, bctd, fctd = pad_niftis(bimg, bmsk,sr, fimg, fmsk, sub, bctd, fctd)

    to_ax = ('I', 'A', 'L')
    bimg = reorient_to(bimg, axcodes_to=to_ax, msk=False)
    bmsk = reorient_to(bmsk, axcodes_to=to_ax)
    sr = reorient_to(sr, axcodes_to=to_ax)
    sub = reorient_to(sub, axcodes_to=to_ax, msk=False)
    bctd = reorient_centroids_to(bctd, bimg)

    fimg = reorient_to(fimg, axcodes_to=to_ax, msk=False)
    fmsk = reorient_to(fmsk, axcodes_to=to_ax)
    fctd = reorient_centroids_to(fctd, fimg)

    zms = (1,1,1)
    bzms = (1,1,1)
    fzms = (1,1,1)

    bimg_data = np.asanyarray(bimg.dataobj, dtype=np.int16)
    bmsk_data = np.asanyarray(bmsk.dataobj, dtype=bmsk.dataobj.dtype)

    fimg_data = fimg.get_fdata()
    fmsk_data = np.asanyarray(fmsk.dataobj, dtype=fmsk.dataobj.dtype)

    res_data = np.asanyarray(sub.dataobj, dtype=sub.dataobj.dtype)

    try:
        bsag_img, bcor_img, _, _, _ = sag_cor_curveprojection(bctd, bimg_data)
        bsag_msk, bcor_msk, y_cord, z_cord, x_ctd = sag_cor_curveprojection(bctd, bmsk_data)
        res_im, res_cor, _, _, _ = sag_cor_curveprojection(bctd, res_data)

        bsag_img, res_im, new_bctd = create_interpolated_im(bsag_img,res_im,x_ctd,z_cord,30,bctd)
        _, bsag_msk, _ = create_interpolated_im(bsag_img,bsag_msk,x_ctd,z_cord,30,bctd)
        bcor_img, res_cor, new_bcent_cor = create_interpolated_im(bcor_img,res_cor,x_ctd,y_cord,30,bctd)
        _, bcor_msk, _ = create_interpolated_im(bcor_img,bcor_msk,x_ctd,y_cord,30,bctd)

    except Exception as e:
        if len(bctd)==2:
            l = 1
        else:
            l = int(len(bctd)/2)
        bsag_img = bimg_data[:,:,int(bctd[l][3])]
        bsag_msk = bmsk_data[:,:,int(bctd[l][3])]
        bcor_img = bimg_data[:,int(bctd[l][2]),:]
        bcor_msk = bmsk_data[:,int(bctd[l][2]),:]
        res_cor = res_data[:,int(bctd[l][2]),:]
        new_bctd = bctd
        new_bcent_cor = bctd
        res_im = res_data[:,int(bctd[l][2]),:]

    try:
        fsag_img, fcor_img,y_cord, z_cord, x_ctd = sag_cor_curveprojection(fctd, fimg_data)
        fsag_img, _, new_fctd = create_interpolated_im(fsag_img,fsag_img,x_ctd,z_cord,30,fctd)
        fcor_img, _, new_fcent_cor = create_interpolated_im(fcor_img,fcor_img,x_ctd,y_cord,30,fctd)
    except Exception as e:
        if len(fctd)==2:
            l = 1
        else:
            l = int(len(fctd)/2)
        fsag_img = fimg_data[:,:,int(fctd[l][3])]
        fcor_img = fimg_data[:,int(fctd[l][2]),:]
        new_fctd = fctd
        new_fcent_cor = fctd

    bdrr_iso = deepcopy(bimg_data.astype(float))
    bdrr_iso[bmsk_data==0] = np.nan
    drr_b = np.nansum(bdrr_iso,2)

    fdrr_iso = deepcopy(fimg_data.astype(float))
    fdrr_iso[fmsk_data==0] = np.nan
    drr_f = np.nansum(fdrr_iso,2)

    for x in to_remove_bs:
        bsag_msk[bsag_msk==x] = 0
        bcor_msk[bcor_msk==x] = 0

    fig, axs, size = create_figure_1(dpi,drr_b,None)
    fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
    axs.imshow(drr_b, cmap=plt.cm.gray, interpolation='lanczos')

    plot_sag_centroids(axs, bctd, zms)
    plot_bmd_label(axs,bctd,zms, bbmd)
    buffer_ = BytesIO()
    plt.savefig(buffer_, format = "png")
    buffer_.seek(0)
    image = PIL.Image.open(buffer_)
    b_sag_drr = np.asarray(image)
    buffer_.close()
    plt.close()

    fig, axs,_ = create_figure_1(dpi,drr_f,size)
    fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
    axs.imshow(drr_f, cmap=plt.cm.gray, interpolation='lanczos')
    plot_sag_centroids(axs, fctd, zms)
    plot_bmd_label(axs,fctd,zms, fbmd)
    buffer_ = BytesIO()
    plt.savefig(buffer_, format = "png")
    buffer_.seek(0)
    image = PIL.Image.open(buffer_)
    f_sag_drr = np.asarray(image)
    buffer_.close()
    plt.close()

    bsag_msk[bsag_msk>0] = 2
    res_im[res_im>0] = 1

    bsag_msk
    bsag_msk[res_im>0]  = np.nan

    bsag_msk[bsag_msk==0] = np.nan
    res_im[res_im==0] = np.nan
    bsag_msk[0,0] = 1
    res_im[0,0] = 2

    fig, axs, size = create_figure_1(dpi,bsag_img,size)
    fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
    axs.imshow(bsag_img, cmap=plt.cm.gray, norm=wdw_sbone)
    axs.imshow(bsag_msk, cmap=cm_sr, alpha=0.7)
    axs.imshow(res_im, cmap=cm_sr, alpha=0.2)

    plot_sag_centroids(axs, new_bctd, zms)
    if not b_labels.empty:
        plot_sag_labels(axs, new_bctd, zms, b_labels, size=2, text=True)

    buffer_ = BytesIO()
    plt.savefig(buffer_, format = "png")
    buffer_.seek(0)
    image = PIL.Image.open(buffer_)
    bsag_img_ = np.asarray(image)
    buffer_.close()
    plt.close()

    fig, axs,fsize = create_figure_1(dpi,fsag_img,size)
    fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
    axs.imshow(fsag_img, cmap=plt.cm.gray, norm=wdw_sbone)
    plot_sag_centroids(axs, new_fctd, fzms)
    if not f_labels.empty:
        plot_sag_labels(axs, new_fctd, zms, f_labels, size=2, text=True)
    buffer_ = BytesIO()
    plt.savefig(buffer_, format = "png")
    buffer_.seek(0)
    image = PIL.Image.open(buffer_)
    fsag_img_ = np.asarray(image)

    buffer_.close()
    plt.close()
    ###################################

    bcor_msk[bcor_msk>0] = 2
    res_cor[res_cor>0] = 1

    bcor_msk[res_cor>0]  = np.nan

    bcor_msk[bcor_msk==0] = np.nan
    res_cor[res_cor==0] = np.nan
    bcor_msk[0,0] = 1
    res_cor[0,0] = 2

    fig, axs,fsize = create_figure_1(dpi,bcor_img,size)
    fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
    axs.imshow(bcor_img, cmap=plt.cm.gray, norm=wdw_sbone)
    axs.imshow(bcor_msk, cmap=cm_sr, alpha=0.7)
    axs.imshow(res_cor, cmap=cm_sr, alpha=0.2)
    plot_cor_centroids(axs, new_bcent_cor, fzms)

    buffer_ = BytesIO()
    plt.savefig(buffer_, format = "png")
    buffer_.seek(0)
    image = PIL.Image.open(buffer_)
    bcor_img_ = np.asarray(image)

    buffer_.close()
    plt.close()

    fig, axs,fsize = create_figure_1(dpi,fcor_img,size)
    fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
    axs.imshow(fcor_img, cmap=plt.cm.gray, norm=wdw_sbone)
    plot_cor_centroids(axs, new_fcent_cor, fzms)

    buffer_ = BytesIO()
    plt.savefig(buffer_, format = "png")
    buffer_.seek(0)
    image = PIL.Image.open(buffer_)
    fcor_img_ = np.asarray(image)

    buffer_.close()
    plt.close()

    return bimg, b_sag_drr, f_sag_drr, bctd, fimg, bsag_img_,fsag_img_, fctd, bcor_img_, fcor_img_,bzms,fzms, size, fsize

def plot_sag_labels(axs, ctd, zms, ctd_labels, size=2, text=True):
    # requires v_dict = dictionary of mask labels and a pd dataframe with labels "ctd_labels"
    for v in ctd[1:]:
        if v[0] < 8 or v[0] >28:
            continue
        if not ('def_'+v_dict[v[0]] in ctd_labels):
            continue
        if ctd_labels['def_'+v_dict[v[0]]].values > 0:            # handle non-existing labels
            vert_label = int(ctd_labels['def_'+v_dict[v[0]]])
        else:
            vert_label = 0
        if vert_label > 0:
            axs.add_patch(Circle((v[2]*zms[1], v[1]*zms[0]), size, color=colors_labels[vert_label]))
            if text:
                axs.text(v[2]*zms[1]- 11, v[1]*zms[0]+2, vert_label, fontdict={'color': cm_labl(vert_label), 'weight': 'bold'})

def sag_cor_curveprojection(ctd_list, arr):
    # Sagittal and coronal projections of a curved plane defined by centroids
    # Note: Pass centoids (ctd_list) and image (img) in IAL direction!
    # if x-direction (=S/I) is not fully incremental, a straight, not an interpolated plane will be returned
    new_ctd=[]              # handle T13 = label 28
    [new_ctd.append([19.5,x[1],x[2],x[3]]) if x[0]==28 else new_ctd.append(x) for x in ctd_list[1:]]
    new_ctd.sort()
    out =[ctd_list[0]]
    [out.append(x) for x in new_ctd[1:]]
    ctd_arr = np.transpose(np.asarray(out[1:]))[1:]
    shp = arr.shape
    x_ctd = np.rint(ctd_arr[0]).astype(int)
    y_ctd = np.rint(ctd_arr[1]).astype(int)
    z_ctd = np.rint(ctd_arr[2]).astype(int)
    cor_plane = np.zeros((shp[0], shp[2]))
    sag_plane = np.zeros((shp[0], shp[1]))
    x_ctd_unique = np.unique(x_ctd)
    if (len(ctd_list) <= 3) or ((not np.array_equal(x_ctd, sorted(x_ctd_unique))) and (not  np.array_equal(x_ctd,sorted(x_ctd_unique, reverse=True)))):
        y_cord = np.rint(np.mean(y_ctd)).astype(int)
        z_cord = np.rint(np.mean(z_ctd)).astype(int)
        cor_plane = arr[:, y_cord, :]
        sag_plane = arr[:, :, z_cord]
        y_cord = np.array([y_cord]*shp[1])
        z_cord = np.array([z_cord]*shp[2])
    else:
        f_sag = interp1d(x_ctd, y_ctd, kind='quadratic')
        f_cor = interp1d(x_ctd, z_ctd, kind='quadratic')
        window_size, poly_order = int((max(x_ctd)-min(x_ctd))/2), 3
        if  window_size % 2 == 0:
            window_size+=1
        y_cord = np.array([np.rint(f_sag(x)).astype(int) for x in range(min(x_ctd), max(x_ctd))])
        y_cord = np.rint(savgol_filter(y_cord, window_size, poly_order)).astype(int)
        z_cord = np.array([np.rint(f_cor(x)).astype(int) for x in range(min(x_ctd), max(x_ctd))])
        z_cord = np.rint(savgol_filter(z_cord, window_size, poly_order)).astype(int)
        y_cord[y_cord <  0]= 0                      #handle out-of-volume interpolations
        y_cord[y_cord >= shp[1]] = shp[1] - 1
        z_cord[z_cord < 0] = 0
        z_cord[z_cord >= shp[2]] = shp[2] - 1
        for x in range(0, shp[0] - 1):
            if x < min(x_ctd):
                cor_plane[x, :] = arr[x, y_cord[0], :]
                sag_plane[x, :] = arr[x, :, z_cord[0]]
            elif x >= max(x_ctd):
                cor_plane[x, :] = arr[x, y_cord[-1], :]
                sag_plane[x, :] = arr[x, :, z_cord[-1]]
            else:
                cor_plane[x, :] = arr[x, y_cord[x - min(x_ctd)], :]
                sag_plane[x, :] = arr[x, :, z_cord[x - min(x_ctd)]]
    return sag_plane, cor_plane, y_cord, z_cord, x_ctd

def reorient_centroids_to(ctd_list, img, decimals=1, verb=False):
    # reorient centroids to image orientation
    # todo: reorient to given axcodes (careful if img ornt != ctd ornt)
    ctd_arr = np.transpose(np.asarray(ctd_list[1:]))
    if len(ctd_arr) == 0:
        print("[#] No centroids present")
        return ctd_list
    v_list = ctd_arr[0].astype(int).tolist()  # vertebral labels
    ctd_arr = ctd_arr[1:]
    ornt_fr = nio.axcodes2ornt(ctd_list[0])  # original centroid orientation
    axcodes_to = nio.aff2axcodes(img.affine)
    ornt_to = nio.axcodes2ornt(axcodes_to)
    trans = nio.ornt_transform(ornt_fr, ornt_to).astype(int)
    perm = trans[:, 0].tolist()
    shp = np.asarray(img.dataobj.shape)
    ctd_arr[perm] = ctd_arr.copy()
    for ax in trans:
        if ax[1] == -1:
            size = shp[ax[0]]
            ctd_arr[ax[0]] = np.around(size - ctd_arr[ax[0]], decimals)
    out_list = [axcodes_to]
    ctd_list = np.transpose(ctd_arr).tolist()
    for v, ctd in zip(v_list, ctd_list):
        out_list.append([v] + ctd)
    if verb:
        print("[*] Centroids reoriented from", nio.ornt2axcodes(ornt_fr), "to", axcodes_to)
    return out_list

def load_centroids(ctd_path):
    with open(ctd_path) as json_data:
        dict_list = json.load(json_data)
        json_data.close()
    ctd_list = []
    for d in dict_list:
        if 'direction' in d:
            ctd_list.append(tuple(d['direction']))
        elif 'nan' in str(d):            #skipping NaN centroids
            continue
        else:
            ctd_list.append([d['label'], d['X'], d['Y'], d['Z']])
    return ctd_list

def create_interpolated_im(img,msk,x_ctd,y_cord,inter,ctd):
    #interpolates a given 2D image 'img' along with its mask 'msk' to an interpolation interval 'inter' using the distance dictionart l_dict

    #create a dictionary of euclidean distances between each slice
    xcords = range(x_ctd[0],x_ctd[-1],1)
    distances = {}
    co = 0
    cx = 0
    cy = 0
    for x,y in zip(xcords,y_cord):

        if co>0:
            d =math.sqrt((x -cx)**2 + (y -cy)**2)
            distances[x] = d
        else:
            distances[x] = 1
        co+=1
        cx = x
        cy = y
    interp_ctd = []

    ks = list(sorted(distances.keys()))
    #split the spine into chunks depending on the value of inter
    chunks = [ks[x:x+inter] for x in range(0, len(ks), inter)]

    n_d = {}
    n_d1 = {}
    for iii,ch in enumerate(chunks):

        n_d[ch[0]+inter] = sum([distances[y] for y in ch])
    #calculate new centroids

    new_cent = deepcopy(ctd)

    for cent in new_cent:
        if type(cent) is list:
            su =sum([distances[i]-1 for i in distances if i<cent[1]])
            cent[1]+= su
    l_dict = n_d

    x = l_dict.values()
    h,w = img.shape

    first_o=0
    ixx_img =None
    ixx_msk = None
    for k in sorted(l_dict.keys()):
        new_slices = 0
        val = l_dict[k]

        if val >= inter:
            new_slices = int(round(val-inter))
        if new_slices > 0:
            hh = k-first_o+new_slices

            nimg = resize(img[first_o:k,:],(hh,w),order = 3,preserve_range=True, mode='constant',anti_aliasing=False)
            nmsk = resize(msk[first_o:k,:],(hh,w),order = 0,preserve_range=True, mode='constant',anti_aliasing=False)

            if ixx_img is None:
                ixx_img = nimg
                ixx_msk= nmsk
            else:
                ixx_img = np.concatenate((ixx_img,nimg),axis = 0)
                ixx_msk = np.concatenate((ixx_msk,nmsk),axis = 0)
            first_o = k
        else:
            if ixx_img is None:
                ixx_img = img[first_o:k,:]
                ixx_msk= msk[first_o:k,:]
            else:
                ixx_img = np.concatenate((ixx_img,img[first_o:k,:]),axis = 0)
                ixx_msk = np.concatenate((ixx_msk,msk[first_o:k,:]),axis = 0)
            first_o = k
    ixx_img = np.concatenate((ixx_img,img[k:,:]),axis=0)
    ixx_msk=np.concatenate((ixx_msk,msk[k:,:]),axis=0)
    return ixx_img,ixx_msk,new_cent

def create_figure_1(dpi, planes, size=None):
    fig_h = round(2 * planes.shape[0] / dpi, 2)
    fig_w = round(2 * planes.shape[1] / dpi, 2)
    if size:
        s = size
    else:
        s=(fig_w, fig_h)
    fig, axs = plt.subplots(1, 1,figsize=s )
    axs.axis('off')
    return fig, axs, (fig_w,fig_h)

def plot_sag_centroids(axs, ctd, zms):
    # requires v_dict = dictionary of mask labels
    for v in ctd[1:]:
        axs.add_patch(Circle((v[2]*zms[1], v[1]*zms[0]), 1, color=colors_itk[v[0]-1]))
        axs.text(4, v[1]*zms[0], v_dict[v[0]], fontdict={'color': cm_itk(v[0]-1), 'weight': 'bold'})

def plot_cor_centroids(axs, ctd, zms):
    # requires v_dict = dictionary of mask labels
    for v in ctd[1:]:
        axs.add_patch(Circle((v[3]*zms[2], v[1]*zms[0]), 1, color=colors_itk[v[0]-1]))
        axs.text(4, v[1]*zms[0], v_dict[v[0]], fontdict={'color': cm_itk(v[0]-1), 'weight': 'bold'})

def save_json(out_dict, out_path):
    def convert(o):
        if isinstance(o, np.int64):
            return int(o)
        raise TypeError
    with open(out_path, 'w') as f:
        json.dump(out_dict, f, indent=4, default=convert)
    print("[*] Json saved:", out_path)

def sag_i(x,sag):

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
    fig = plt.figure(figsize=(3, 12))
    ax_neu = fig.add_subplot(1,1,1)
    plt.axis('off')

    ax_neu.imshow(sag)
    fig.canvas.draw()

def sag_drr(x,drr):

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
    fig = plt.figure(figsize=(3, 12))
    ax_neu = fig.add_subplot(1,1,1)
    plt.axis('off')

    ax_neu.imshow(drr,  cmap=plt.cm.gray, interpolation='lanczos')
    fig.canvas.draw()

def cor_i(x,cor):

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
    fig = plt.figure(figsize=(3, 12))
    fig.patch.set_facecolor('black')
    ax_neu = fig.add_subplot(1,1,1)
    plt.axis('off')

    ax_neu.imshow(cor)
    fig.canvas.draw()

def cor_f(slc,img,zms,size):

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        img_median = img.get_fdata()[:,slc,:]

    fig, axs,_ = create_figure_1(120,img_median,size)
    fig.subplots_adjust(bottom=0, top=1, left=0, right=1)
    axs.imshow(img_median, cmap=plt.cm.gray, norm=wdw_sbone)
    buffer_ = BytesIO()
    plt.savefig(buffer_, format = "png")
    buffer_.seek(0)
    image = PIL.Image.open(buffer_)
    img_median = np.asarray(image)
    buffer_.close()
    plt.close()

    fig = plt.figure(figsize=(3, 12))
    ax_neu = fig.add_subplot(1,1,1)
    plt.axis('off')

    ax_neu.imshow(img_median)
    fig.canvas.draw()

def sag_f(slc,img,zms):

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        img_median = img.get_fdata()[:,:,slc]
    fig = plt.figure(figsize=(3, 12))
    ax_neu = fig.add_subplot(1,1,1)
    plt.axis('off')

    ax_neu.imshow(img_median, cmap="gray", norm=wdw_sbone)
    fig.canvas.draw()
#     display(fig)

v_dict = {
    1: 'C1', 2: 'C2', 3: 'C3', 4: 'C4', 5: 'C5', 6: 'C6', 7: 'C7',
    8: 'T1', 9: 'T2', 10: 'T3', 11: 'T4', 12: 'T5', 13: 'T6', 14: 'T7',
    15: 'T8', 16: 'T9', 17: 'T10', 18: 'T11', 19: 'T12', 20: 'L1',
    21: 'L2', 22: 'L3', 23: 'L4', 24: 'L5', 25: 'L6', 28: 'T13'
}
def get_raw_ct_paths(directory):
    im_paths = []
    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith("_ct.nii.gz"):
                im_paths.append(os.path.join(root, file))
    return im_paths

def get_derivative_path_from_raw_ct(img_path):
    drv = str(img_path).replace("rawdata", "derivatives")

    msk_path = drv.replace('_ct.nii.gz', '_seg-vert_msk.nii.gz')
    sr_path = drv.replace('_ct.nii.gz', '_seg-subreg_msk.nii.gz')
    ctd_path = sr_path.replace('_msk.nii.gz', '_ctd.json')
    return msk_path, sr_path, ctd_path

def get_baseline_fu_ses(unique_sessions):
    ses1 = int(unique_sessions[0][4:])
    ses2 = int(unique_sessions[1][4:])

    if ses1 > ses2:
        baseline_ses = unique_sessions[1]
        followup_ses = unique_sessions[0]
    else:
        baseline_ses = unique_sessions[0]
        followup_ses = unique_sessions[1]
    return baseline_ses, followup_ses

def get_study_dict(fold, baseline_ses, followup_ses):

    baseline_dir = os.path.join(fold, baseline_ses)
    followup_dir = os.path.join(fold, followup_ses)

    baseline_ims = [os.path.join(baseline_dir, x) for x in os.listdir(baseline_dir) if x.endswith('_ct.nii.gz')]
    followup_ims = [os.path.join(followup_dir, x)  for x in os.listdir(followup_dir) if x.endswith('_ct.nii.gz')]

    study_dict = {'baselines': baseline_ims, 'followups': followup_ims}
    return study_dict

def get_sub_nifti_path(baseline_path, followup_path):
    sub_dir = os.path.join(os.path.dirname(os.path.dirname(baseline_path)).replace('rawdata','derivatives'), 'subtraction')
    baseline_ses = os.path.basename(os.path.dirname(baseline_path))
    followup_ses = os.path.basename(os.path.dirname(followup_path))
    spl_b, spl_f = '', ''
    if 'split' in baseline_path:
        spl_b = '_split-{}'.format(baseline_path.split('split-')[1].split('_')[0])
    if 'split' in followup_path:
        spl_f = '_split-{}'.format(followup_path.split('split-')[1].split('_')[0])

    sub_name = os.path.basename(baseline_path).split('_')[0] + '_sesb-{}{}'.format(baseline_ses.split('-')[1],spl_b) + '_sesf-{}{}'.format(followup_ses.split('-')[1],spl_f) + '_msk.nii.gz'
    sub_path = os.path.join(sub_dir, sub_name)

    return sub_path

def create_evaluation_path(results_folder, sub_path, rater):
    return os.path.join(results_folder,os.path.basename(sub_path).replace('_ref.nii.gz', '_rater-{}_eval.json'.format(rater)))

def get_paths_followup(dataset_folder, ex, rater,chunks=None, results_folder='./results'):
    main_fold = os.path.join(dataset_folder, 'rawdata')
    folders = [os.path.join(main_fold,x) for x in os.listdir(main_fold) if 'sub-ctfu' in x]
    folders.sort()
    if chunks is not None:
        pointer1 = chunks * 20
        pointer2 = pointer1 + 20
    else:
        pointer1 = 0
        pointer2 = len(folders)
    paths = []
    for fold in folders[pointer1:pointer2]:
        unique_sessions = [x for x in os.listdir(fold) if 'ses' in x]
        if len(unique_sessions) != 2:
            continue
        baseline_ses, followup_ses = get_baseline_fu_ses(unique_sessions)
        print('dataset_folder ', fold)
        print('baseline: ', baseline_ses)
        print('fu: ', followup_ses)

        study_dict = get_study_dict(fold, baseline_ses, followup_ses)
        print(study_dict)

        for baseline_path in study_dict['baselines']:
            for followup_path in study_dict['followups']:
                sub_path = get_sub_nifti_path(baseline_path, followup_path)
                print('subpath ', sub_path)
                eval_path = create_evaluation_path(results_folder, sub_path, rater)
                if ex:
                    if os.path.isfile(eval_path):
                        print('continued')
                        continue

                b_msk_path, b_sr_path, b_ctd_path = get_derivative_path_from_raw_ct(baseline_path)
                f_msk_path, f_sr_path, f_ctd_path = get_derivative_path_from_raw_ct(followup_path)

                if os.path.isfile(sub_path):
                    print(baseline_path)
                    ps = [baseline_path, b_msk_path,b_sr_path,b_ctd_path,followup_path,f_msk_path,f_sr_path,f_ctd_path,sub_path]
                    paths.append(ps)
                else:
                    print('stoopid')
            print('#############')
    return paths

def load_json(json_path):
    name = Path(json_path).name
    name = str(name).split('_')[0]
    with open(json_path) as json_data:
        js = json.load(json_data)
        js['name'] = name
        json_data.close()
    return js

def get_results_fu(path,vert):
    try:
        entry = get_csv_entry(path)
        labels = pd.read_csv('./labels.csv')

        label  = labels[vert][labels['ID']==entry]

        if label.values > 0:            # handle non-existing labels
            vert_label = str(int(label))
        else:
            vert_label = ""
    except Exception as e:
        print('error ', str(e))
        vert_label = ""
    return vert_label

def get_csv_entry(img_path):
    csv_name = str(os.path.basename(img_path).replace('_ct.nii.gz', ''))
    return csv_name

def get_paths_from_case(case_path):
    cases = []
    unique_sessions = [x for x in os.listdir(case_path) if 'ses' in x]
    baseline_ses, followup_ses = get_baseline_fu_ses(unique_sessions)
    study_dict = get_study_dict(case_path, baseline_ses, followup_ses)
    for baseline_path in study_dict['baselines']:
        for followup_path in study_dict['followups']:
            sub_path = get_sub_nifti_path(baseline_path, followup_path)

            if os.path.isfile(sub_path):
                p_dict = {
                    'b_img_pth': baseline_path,
                    'f_img_pth': followup_path,
                    'sub_pth'  : sub_path,
                }
                cases.append(p_dict)
    return cases

def get_rater_cases_from_csv(data_frame, dataset_path,rater=None, load_all=False, exclude_rated=False):
    labelled_IDs = pd.read_csv('./CTFU_Fx_{}.csv'.format(str(rater)))['ID'].tolist()

    rater_cases = []
    for idx, row in data_frame.iterrows():

        if load_all:
            rater_cases.append(row['case_name'])

        else:
            if row['rater'] == rater or row['multirater'] ==1:
                rater_cases.append(row['case_name'])
    rater_cases= list(set(rater_cases))
    rater_cases.sort()
    cases_paths = []
    for case in rater_cases:
        cs_pth = os.path.join(os.path.join(dataset_path,'rawdata'), case)
        if not os.path.isdir(cs_pth):
            continue
        case_p = get_paths_from_case(os.path.join(os.path.join(dataset_path,'rawdata'), case))
        if not case_p:
            continue
        if exclude_rated:
            for idx,c in enumerate(case_p):
                ID_b = os.path.basename(c['b_img_pth']).replace('_ct.nii.gz', '')
                ID_f = os.path.basename(c['f_img_pth']).replace('_ct.nii.gz', '')
                if ID_b in labelled_IDs and ID_f in labelled_IDs:
                    case_p.pop(idx)
        cases_paths = cases_paths + case_p
    return cases_paths


# solve a + b + c = p AND a^2 + b^2 = c^2
# AND maybe a + b > c
from collections import defaultdict, Counter
from functools import reduce

max_p = 1000

# reverse lookup of perfect squares
square_reverse_lookup = dict((i ** 2, i) for i in range(0, max_p))

# find all triples
def triples(upper_bound):
    for a in range(1, upper_bound):
        for b in range(a, upper_bound):
            c2 = a ** 2 + b ** 2
            if c2 in square_reverse_lookup:
                yield(a, b, square_reverse_lookup[c2])

solutions = Counter(sum(t) for t in triples(max_p // 2) if sum(t) <= max_p)

print(solutions.most_common(1))


# Imports libraries and moodules
import pygame
from pygame.locals import *
import constants
import time
import random

class Guanabana():
    def __init__(self, parent_window):
        self.image = pygame.image.load("images\guanabana.png")
        self.parent_window = parent_window
        self.x = 120
        self.y = 120

    def draw(self):
        self.parent_window.blit(self.image, (self.x, self.y))
        pygame.display.flip()

    # Move method
    def move(self):
        self.x = random.randint(32, 768)
        self.y = random.randint(32, 568)

class Snake():
    def __init__(self, parent_window, length):
        self.parent_window = parent_window
        self.snake_body = pygame.image.load("images\circulo.png")
        self.direction = "down"

        self.length = length
        self.x = [32]*length
        self.y = [32]*length

    def increse_length(self):
        self.length += 1
        self.x.append(-1)
        self.y.append(-1)

    def draw(self):
        self.parent_window.fill(constants.BG_COLOR)

        for i in range(self.length):

           self.parent_window.blit(self.snake_body, (self.x[i], self.y[i]))
        pygame.display.flip()

    def move_left(self):
        self.direction = "left"

    def move_right(self):
        self.direction = "right"

    def move_up(self):
        self.direction = "up"

    def move_down(self):
        self.direction = "down"

    def walk(self):
        # Update body
        for i in range (self.length-1, 0, -1):
            self.x[i] = self.x[i-1]
            self.y[i] = self.y[i-1]

        # Update head
        if self.direction == "left":
            self.x[0] -= constants.SIZE_SNAKE

        if self.direction == "right":
            self.x[0] += constants.SIZE_SNAKE

        if self.direction == "up":
            self.y[0] -= constants.SIZE_SNAKE

        if self.direction == "down":
            self.y[0] += constants.SIZE_SNAKE

        self.draw()

class Game():
    def __init__(self):
        pygame.init()
        self.window = pygame.display.set_mode(constants.SIZE)
        self.title = pygame.display.set_caption(constants.TITLE)
        self.window.fill(constants.BG_COLOR)
        icon = pygame.image.load("images\serpiente.png")
        pygame.display.set_icon(icon)

        self.snake = Snake(self.window, 2)
        self.snake.draw()

        self.guanabana = Guanabana(self.window)
        self.guanabana.draw()

        pygame.display.update()

    # Collition logic
    def is_collition(self, x1, y1, x2, y2):
        if x1 >= x2 and x1 < x2 + constants.APPLE_SIZE:
            if y1 >= y2 and y1 < y2 + constants.APPLE_SIZE:
                return True
        return False

    # Play mathod
    def play(self):
        self.snake.walk()
        self.guanabana.draw()

        if self.is_collition(self.snake.x[0], self.snake.y[0], self.guanabana.x, self.guanabana.y):
            #print("JH")
            self.snake.increse_length()
            self.guanabana.move()

    def run(self):
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == KEYDOWN:
                    if event.key == K_ESCAPE:
                        running = False

                    if event.key == K_LEFT:
                        self.snake.move_left()

                    if event.key == K_RIGHT:
                        self.snake.move_right()

                    if event.key == K_UP:
                        self.snake.move_up()

                    if event.key == K_DOWN:
                        self.snake.move_down()

                elif event.type == QUIT:
                    running = False

            self.play()
            time.sleep(0.2)

game = Game()
game.run()


# Jukebox_Ver2.py

# Lesson 176: Simple Database Browser

# This program makes a simple GUI window that
# allows you to select artists, their albums,
# and the songs.  It is a fake interface to
# a jukebox.

# This version creates a class to handle the
# population of the tables.

# Note: There is a change to the way python 3.7 (for mac) handles the bind/select:
# code: elf.bind('<<ListboxSelect>>', self.on_select)
# When you select a value in another scroll box, it causes any other scroll
# box's selected value to become unselected, and to
# generate an event that calls your function associated
# with that other scroll box.  The event passed has
# a value of nothing selected.  As a result, "curselection()",
# w/i the function, returns empty.
# You need to code around that.
# It was not like that in previous versions of python.

# - Jim
# (contact info)

import sqlite3
import tkinter    # python 3 naming of library.

###########
# Classes #
###########

class ScrollBox(tkinter.Listbox):
    # Inherits from tkinter.Listbox

    # This class will combine a listbox, & a scroll bar.
    # They will share the same location on the grid
    # with the the scroll bar being on the right, and
    # the list box being on the left.

    def __init__(self, window, **kwargs):

        # Call the parent's init function 1st:
        super().__init__(window, **kwargs)

        # Add in the scroll bar below:

        # This class will have a list box, and a scroll bar.  So, the "self.yview"
        # will refer to the list box's yview.
        # (This says that when you move the scroll bar, also run the file_list's "yview" function.)

        self.scrollbar = tkinter.Scrollbar(window, orient=tkinter.VERTICAL, command=self.yview)

    # Overriding from Listbox:
    def grid(self, row, column, sticky='nsw', rowspan=1, columnspan=1, **kwargs):
        # Regarding the "grid" function:
        # From: https://www.tutorialspoint.com/python/tk_grid.htm
        # This geometry manager organizes widgets in a table-like structure in the parent widget.

        # 1st call the parent class' function to place the list box in the window's grid.
        # Place the list box in whatever row, and column the user specifies.
        super().grid(row=row, column=column, sticky=sticky, rowspan=rowspan, columnspan=columnspan, **kwargs)

        # Now add our scrollbar (which was created in the init function) to the same
        # location on the window's grid.
        # Put the scroll bar in the same grid as the list box.  However, stick to the east side.
        self.scrollbar.grid(row=row, column=column, sticky='nse', rowspan=rowspan)
        # Link the list back to the scroll bar:  When the list box contents adjust
        # (due to the person using the arrow keys to move w/i it, or the contents
        # updating), update the scroll bar.
        # Note: Adjusting the list box due to the scroll bar moving is handled
        # in the init function with: command=self.yview)
        self['yscrollcommand'] = self.scrollbar.set

class DataListBox(ScrollBox):
    # Subclassing our ScrollBox to add in
    # auto populate of scroll boxes.

    def __init__(self, window, connection, table, field, sort_order=(), **kwargs):
        # window = The TKInter window that our app is running in.
        # connection = the connection to the DB.
        # table = which table to work with in the DB.
        # field = which column in the table to look in.
        # sort_order = a list of how do we want the output displayed in the scroll box?

        # init method creates the list box, and the sqlite query.

        super().__init__(window, **kwargs)

        self.linked_box = None
        self.link_field = None
        self.link_value = None    # Holds the vale that was selected.

        # Remember a 'cursor' object allows you to execute statements in the DB
        # which then return an iterator (a pointer to a stream of data).
        # You can iterate forward through the data, and that's it.
        self.cursor = connection.cursor()

        self.table = table
        self.field = field

        # Now that the scroll box is created, make it so
        # that, when you select an entry in it, we will call a function
        # that  will cause the "linked_box" to update with the data
        # from column "link_field" for the value selected in our list box.
        # For example, if you select "Aerosmith" from the "Artists" list box,
        # then the "Albums" list box should be populated with the available
        # from Aerosmith.
        # Note: Apparently, the 'bind' function will pass, as a parameter,
        # to the function to call the event/action that was triggered.
        self.bind('<<ListboxSelect>>', self.on_select)

        # Query to execute:
        self.sql_select = "SELECT " + self.field + ", _id" + " FROM " + self.table

        # If there is an order by, then add it to the select statement.
        # Otherwise, sort by the field you are requesting data for.

        # ','.join(sort_order) --> Creates a comma separate list of "sort_order".

        if sort_order:
            self.sql_sort = " ORDER BY " + ','.join(sort_order)

        else:
            self.sql_sort = " ORDER BY " + self.field

    def clear(self):

        # Clears a scroll box

        # http://effbot.org/tkinterbook/listbox.htm
        # delete's format is:
        # delete(first, last=None)
        # - Deletes one or more items.
        # - Use delete(0, END) to delete all items in the list.
        self.delete(0, tkinter.END)

    def link(self, widget, link_field):
        # This function ties to 'widgets' (data list boxes)
        # together.
        # A 'widget' will be another 'DataListBox'.

        self.linked_box = widget  # Example "artiists" is tied to "albums".

        # Every "DataListBox" has a "link_field".
        # The passed widget of type "DataListBox".
        # The "link_field" will be what we column gets looked through to use
        # populate the widget's list box.
        widget.link_field = link_field

    def requery(self, link_value=None):

        # Function to run the query.

        # Store the ID so that we know the "master" record
        # we populated from.
        self.link_value = link_value

        # If a value was passed in, make the query specific to that
        # value.
        if link_value and self.link_field:

            sql = self.sql_select + " WHERE " + self.link_field + "=?" + self.sql_sort

            # print(sql)    # TODO: For testing.  Remove once coding is completed.

            self.cursor.execute(sql, (link_value,))

        else:
            # For debugging during writing the code.
            # print(self.sql_select + self.sql_sort)  # TODO: Delete after testing.

            # Get the new data.
            self.cursor.execute(self.sql_select + self.sql_sort)

        # Remove the old data from the list box.
        self.clear()

        # Put the new data in the list box.
        # Note the stream contains tuples!
        for value in self.cursor:
            # Format: objName.insert(location, itemToAdd)
            # So, keep appending to the end.
            # 'value' is a tuple.
            self.insert(tkinter.END, value[0])

        # For the case where we click on an artist,
        # the 'albums' list box will populate.  We want
        # the 'songs' list to clear b/c we have not yet
        # selected an album.
        # The code below will do those b/c the "linked_box"
        # (for the "albums" list) is the "songs" list.
        if self.linked_box:
            self.linked_box.clear()

    def on_select(self, event):
        # This function looks at what was selected in its widget box, and
        # populates another list box (like "albums", or "songs") with data
        # related to the choice.  For example, if "Aerosmith" is selected from
        # the "artists" list, then the available Aerosmith albums would be populated
        # into the "albums" list box.  If an album is selected from the "albums"
        # list box, then all the songs from the album would be put into the "songs"
        # list box.

        # If the user did not provide a list box to populate,
        # then there is no point in running the code:
        if not self.linked_box:
            # No linked box.
            return

        # "is" returns True is a & b are the same object.
        # print("Is 'self', and 'event.widget' the same? ", self is event.widget)    # TODO: remove this test line.

        # Note: There is a change to the way python 3.7 (for mac)
        # handles the bind/select.  When you select
        # a value in another scroll box, it causes this scroll
        # box's selected value to become unselected, and to
        # generate an event that calls this function, and pass
        # no value selected.  As a result, "curselection()"
        # returns empty.

        # So, we need to test for the unselected condition,
        # and not run the function if so.

        if len(self.curselection()) == 0:
            # This list box was just unselected,
            # and not has nothing selected.  As
            # result, we should not be running this
            # function.  Only run it if we selected
            # an item from the list.
            return

        # If you are here, then the function was NOT
        # called due to an "unselect" event happening.

        # From the list box, get the presently selected item.
        # Notes:
        # 1) self.curselection() --> will return a list of POSITIONS of all the selected items.
        # 2) As it's possible to select more than 1 item from list box by
        # holding down the shift, or other keys, we use [0] to grab the 1st selected
        # item from the list.
        # 3) self.get(index) --> Once we have the index, get the value at that location.
        # 4) you need the "," after 'self.get(index)' to return a tuple.
        # The SELECT query needs a tuple.
        # 5) 'value' will hold the text of what was selected.

        field_index = self.curselection()[0]
        value = self.get(field_index),

        # If a value was passed in (that holds the value of
        # the item selected), create a text string that we
        # can add to the sql query.
        # So, for example, if "aerosmith" was the selected artist,
        # then add it the where clause when looking up the album, and
        # all its songs.  This will protect us from times when multiple
        # artists have the same album name like "Greatest Hits".
        if self.link_value:
            sql_where = " WHERE " + self.field + "=? AND " + self.link_field + "=?"
            # Also update the "value" tuple variable that is used to plug values into
            # the sqlite statement.
            value = value[0], self.link_value
        else:
            # No artist was select.  So, nothing to add to the 'where' clause.
            sql_where = " WHERE " + self.field + "=?"

        # Take the text value, and go to the right DB table to find the "id" value
        # associated with the text name held in "value".
        #
        # fetchone(): If the name returns multiple values, grab just the 1st.
        # fetchone(): returns a tuple.

        # "self.sql_select" is a generic select statement, defined in __init__, that will
        # search the table associated with the list box for the value defined.
        # (From init: self.sql_select = "SELECT " + self.field + ", _id" + " FROM " + self.table)
        # It returns the 'field' + the '_id'.  Hence, we want [1] in fetchone()[1].

        number_id = self.cursor.execute(self.sql_select + sql_where, value).fetchone()[1]

        self.linked_box.requery(number_id)

#########################
# End Class Definitions #
#########################

# Only run the code below if someone specifically
# run this program.  The below 'if' line keeps the
# code from running if the class is imported as a library.
# This is how the teacher did it.  I think I would have moved
# the class to its own file.
if __name__ == '__main__':

    db_conn = sqlite3.connect("music.sqlite")

    # Create the GUI window.
    main_window = tkinter.Tk()
    main_window.title('Music DB Browser')
    main_window.geometry('1024x768')

    # Set up the columns:

    # - A note about 'weight' with tkinter/grid use from:
    # (http://effbot.org/tkinterbook/grid.htm)
    #
    # weight=
    # A relative weight used to distribute additional space between columns. A
    # column with the weight 2 will grow twice as fast as a column with weight
    # 1. The default is 0, which means that the column will not grow at all.

    main_window.columnconfigure(0, weight=2)
    main_window.columnconfigure(1, weight=2)
    main_window.columnconfigure(2, weight=2)
    main_window.columnconfigure(3, weight=1)    # Spacer column on right.

    main_window.rowconfigure(0, weight=1)
    main_window.rowconfigure(1, weight=5)
    main_window.rowconfigure(2, weight=5)
    main_window.rowconfigure(3, weight=1)

    ##########
    # Labels #
    ##########

    artists_label = tkinter.Label(main_window, text="Artists")
    artists_label.grid(row=0, column=0)

    albums_label = tkinter.Label(main_window, text="Albums")
    albums_label.grid(row=0, column=1)

    songs_label = tkinter.Label(main_window, text="Songs")
    songs_label.grid(row=0, column=2)

    ###################
    # Artist List Box #
    ###################

    # The init method creates the list box, and sqlite query.
    artists_list = DataListBox(main_window, db_conn, "artists", "name")

    # padx --> 30 pixels on the left, 0, on the right)
    artists_list.grid(row=1, column=0, sticky='nsew', rowspan=2, padx=(30, 0))
    artists_list.config(border=2, relief='sunken')

    # # Populate the list box with the all the artist:
    # for artist in db_conn.execute("SELECT artists.name FROM artists ORDER BY artists.name"):
    #     # For each artist (in alphabetical order), add the artist to
    #     # the end of the list.  This will keep it in alphabetical order.
    #     # It looks like the "insert" function takes in a location in the
    #     # list box (in this case the end of the box), a list to add.
    #     # Apparently, the select statement returns a tuple of 1 element.
    #     # (Note: the code works fine as "artist" not "artist[0]".  But,
    #     # this the way the teacher wants it done.)
    #     # artists_list.insert(location to insert, what to insert)
    #     artists_list.insert(tkinter.END, artist[0])

    # Populate the list box w/ the data from the artists' table:
    artists_list.requery()

    ###################
    # Albums List Box #
    ###################

    album_lv = tkinter.Variable(main_window)
    album_lv.set(("Choose an artist",))
    # The init method creates the list box, and sqlite query.
    # Note: Below, "sort_order" takes a list.
    albums_list = DataListBox(main_window, db_conn, "albums", "name", sort_order=("name",))

    albums_list.grid(row=1, column=1, sticky='nsew', padx=(30, 0))
    albums_list.config(border=2, relief='sunken')

    # Make it so that when we select an artist in the 'artists' list box,
    # the 'albums' list box is updated with albums from that artist:
    artists_list.link(albums_list, "artist")

    #################
    # Song List Box #
    #################

    song_lv = tkinter.Variable(main_window)
    song_lv.set(("Choose a song",))
    # Create the box.
    songs_list = DataListBox(main_window, db_conn, "songs", "title", sort_order=("track", "title"))

    songs_list.grid(row=1, column=2, sticky='nsew', padx=(30, 0))
    songs_list.config(border=2, relief='sunken')

    # Make it so that when we select an album in the 'album' list box,
    # the 'songs' list box is updated with songs from that album:
    albums_list.link(songs_list, "album")

    #############
    # Main Loop #
    #############

    main_window.mainloop()

    print("Closing database connection.")
    db_conn.close()

L176 Simple Database Browser/Jukebox_Ver2.py


import os
import sys
import asyncio
import logging
import colorlog

import yaml

from ..common import crypto
from .router import mgr as routermgr
from .router import api as apigroup

from ..models.database import db
from ..models.channel import mgr as chanmgr
from ..models.key import mgr as keymgr
from ..models.router import group as routergroup
from ..models.watchdog import wd as wdmgr

from .adapter import revChatGPT
from .adapter import claude
from .adapter import bard
from .adapter import gpt4free
from .adapter import hugchat
from .adapter import qianwen

from . import log
from . import cfg as cfgutil

class Application:
    """Application instance."""

    dbmgr: db.DatabaseInterface
    """Database manager."""""

    router: routermgr.RouterManager
    """Router manager."""

    channel: chanmgr.AbsChannelManager
    """Channel manager."""

    key: keymgr.AbsAPIKeyManager
    """API Key manager."""

    watchdog: wdmgr.AbsWatchDog

    logging_level: int = logging.INFO

    def __init__(
        self,
        dbmgr: db.DatabaseInterface,
        router: routermgr.RouterManager,
        channel: chanmgr.AbsChannelManager,
        key: keymgr.AbsAPIKeyManager,
        watchdog: wdmgr.AbsWatchDog,
        logging_level: int = logging.INFO,
    ):
        self.dbmgr = dbmgr
        self.router = router
        self.channel = channel
        self.key = key
        self.watchdog = watchdog
        self.logging_level = logging_level

    async def run(self):
        """Run application."""
        loop = asyncio.get_running_loop()

        loop.create_task(self.watchdog.run())

        await self.router.serve(loop)

log_colors_config = {
    'DEBUG': 'green',  # cyan white
    'INFO': 'white',
    'WARNING': 'yellow',
    'ERROR': 'red',
    'CRITICAL': 'cyan',
}

default_config = {
    "1-documentation": "see at https://github.com/RockChinQ/free-one-api",
    "database": {
        "type": "sqlite",
        "path": "./data/free_one_api.db",
    },
    "watchdog": {
        "heartbeat": {
            "interval": 1800,
            "timeout": 300,
            "fail_limit": 3,
        },
    },
    "router": {
        "port": 3000,
        "token": "12345678",
    },
    "web": {
        "frontend_path": "./web/dist/",
    },
    "logging": {
        "debug": False,
    },
    "random_ad": {
        "enabled": False,
        "rate": 0.05,
        "ad_list": [
            " (This response is sponsored by Free One API. Consider star the project on GitHub: https://github.com/RockChinQ/free-one-api )",
        ]
    },
    "misc": {
        "chatgpt_api_base": "https://chatproxy.rockchin.top/api/",
    }
}

async def make_application(config_path: str) -> Application:
    """Make application."""
    if not os.path.exists(config_path):
        with open(config_path, "w") as f:
            yaml.dump(default_config, f)
            print("Config file created at", config_path)
            # print("Please edit it and run again.")
            # sys.exit(0)
    config = {}
    with open(config_path, "r") as f:
        config = yaml.load(f, Loader=yaml.FullLoader)

    # complete config
    config = cfgutil.complete_config(config, default_config)

    # dump config
    with open(config_path, "w") as f:
        yaml.dump(config, f)

    # set default values
    from .adapter import revChatGPT

    revChatGPT.RevChatGPTAdapter.CHATGPT_API_BASE = config['misc']['chatgpt_api_base']

    # logging
    logging_level = logging.INFO

    if 'logging' in config and 'debug' in config['logging'] and config['logging']['debug']:
        logging_level = logging.DEBUG

    if 'DEBUG' in os.environ and os.environ['DEBUG'] == 'true':
        logging_level = logging.DEBUG

    print("Logging level:", logging_level)
    logging.debug("Debug mode enabled.")

    terminal_out = logging.StreamHandler()

    terminal_out.setLevel(logging_level)
    terminal_out.setFormatter(colorlog.ColoredFormatter(
        "[%(asctime)s.%(msecs)03d] %(log_color)s%(pathname)s (%(lineno)d) - [%(levelname)s] :\n"
            "%(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
        log_colors=log_colors_config,
    ))

    for handler in logging.getLogger().handlers:
        logging.getLogger().removeHandler(handler)

    logging.getLogger().addHandler(terminal_out)

    # save ad to runtime
    if 'random_ad' in config and config['random_ad']['enabled']:
        from ..common import randomad

        randomad.enabled = config['random_ad']['enabled']
        randomad.rate = config['random_ad']['rate']
        randomad.ads = config['random_ad']['ad_list']

    from ..common import randomad

    # make database manager
    from .database import sqlite as sqlitedb

    dbmgr_cls_mapping = {
        "sqlite": sqlitedb.SQLiteDB,
    }

    dbmgr = dbmgr_cls_mapping[config['database']['type']](config['database'])
    await dbmgr.initialize()

    # database handler
    dblogger = log.SQLiteHandler(dbmgr)

    # failed to set debug level for db handler
    dblogger.setLevel(logging.INFO if logging_level <= logging.INFO else logging_level)
    dblogger.setFormatter(logging.Formatter("[%(asctime)s.%(msecs)03d] %(pathname)s (%(lineno)d) - [%(levelname)s] :\n%(message)s"))

    logging.getLogger().addHandler(dblogger)

    # make channel manager
    from .channel import mgr as chanmgr

    channelmgr = chanmgr.ChannelManager(dbmgr)
    await channelmgr.load_channels()

    # make key manager
    from .key import mgr as keymgr

    apikeymgr = keymgr.APIKeyManager(dbmgr)
    await apikeymgr.list_keys()

    # make forward manager
    from .forward import mgr as forwardmgr

    fwdmgr = forwardmgr.ForwardManager(channelmgr, apikeymgr)

    # make router manager
    from .router import mgr as routermgr

    #   import all api groups
    from .router import forward as forwardgroup
    from .router import api as apigroup
    from .router import web as webgroup

    # ========= API Groups =========
    group_forward = forwardgroup.ForwardAPIGroup(dbmgr, channelmgr, apikeymgr, fwdmgr)
    group_api = apigroup.WebAPIGroup(dbmgr, channelmgr, apikeymgr)
    group_api.tokens = [crypto.md5_digest(config['router']['token'])]
    group_web = webgroup.WebPageGroup(config['web'], config['router'])

    paths = []

    paths += group_forward.get_routers()
    paths += group_web.get_routers()
    paths += group_api.get_routers()

    # ========= API Groups =========

    routermgr = routermgr.RouterManager(
        routes=paths,
        config=config['router'],
    )

    # watchdog and tasks
    from .watchdog import wd as watchdog

    wdmgr = watchdog.WatchDog()

    # tasks
    from .watchdog.tasks import heartbeat

    hbtask = heartbeat.HeartBeatTask(
        channelmgr,
        config['watchdog']['heartbeat'],
    )

    wdmgr.add_task(hbtask)

    app = Application(
        dbmgr=dbmgr,
        router=routermgr,
        channel=channelmgr,
        key=apikeymgr,
        watchdog=wdmgr,
        logging_level=logging_level,
    )

    logging.info("Application initialized.")

    return app


#!/usr/bin/env python3
import os
import sys

from setuptools import setup

from dmscreen import __version__ as VERSION

if sys.version_info < (3, 6):
    sys.exit('Python 3.6 is required to run DMScreen')

for directory, _, filenames in os.walk(u'share'):
    dest = directory[6:]
    if filenames:
        files = []
        for filename in filenames:
            filename = os.path.join(directory, filename)
            files.append(filename)

setup(
    name='DMScreen',
    version=VERSION,
    author='Tom Todd',
    author_email='thomas.m.a.todd@gmail.com',
    packages=[
        'dmscreen',
        'dmscreen.gui',
        'dmscreen.gui.pages',
        'dmscreen.util',
        'dmscreen.models',
        'dmscreen.data',
    ],
    scripts=['bin/dmscreen.py'],
    description='D&D 5e DM Screen Application',
    long_description="""An application for keeping track of and generating
     information for dungeon masters in D&D fifth edition.""",
    classifiers=[
        'Intended Audience :: End Users/Desktop',
        'Programming Language :: Python',
        'Operating System :: Linux',
        'Topic :: Games/Entertainment'
    ],
)


from unittest import result
from selenium import webdriver
import time
import pandas as pd
from selenium.webdriver.common.by import By
import requests
from bs4 import BeautifulSoup as bs
import pprint
import re #文字列のパターンで検索できる
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
import difflib
import matplotlib.pyplot as plt
import datetime
import numpy as np
import schedule
from matplotlib import animation
from time import sleep

def rate_graph():
    url = "https://jp.investing.com/rates-bonds/world-government-bonds"

    dt_now = datetime.datetime.now()
    r = requests.get(url)
    time.sleep(1)

    soup = bs(r.text, "html.parser")

    Country_10years_dict_result_now = {
        "Canada": 0,
        "America": 0,
        "Australia": 0,
        "New Zealand": 0,
        "Germany": 0,
        "France": 0,
        "England": 0,
        "Turkey": 0,
        "Japan": 0,
    }
    Country_10years_dict_result_1hourago = {
        "Canada": 0,
        "America": 0,
        "Australia": 0,
        "New Zealand": 0,
        "Germany": 0,
        "France": 0,
        "England": 0,
        "Turkey": 0,
        "Japan": 0,
    }
    Country_10years_dict_difference ={
        "Canada": 0,
        "America": 0,
        "Australia": 0,
        "New Zealand": 0,
        "Germany": 0,
        "France": 0,
        "England": 0,
        "Turkey": 0,
        "Japan": 0,
    }

    Country_10years_dict = {   #redBgが更新時？
        "Canada" :"pid-25275-last",
        "America": "pid-23705-last",
        "Australia": "pid-23878-last",
        "New Zealand": "pid-42410-last",
        "Germany": "pid-23693-last",
        "France": "pid-23778-last",
        "England": "pid-23673-last",
        "Turkey":  "pid-24037-last",
        "Japan":  "pid-23901-last",
    }

    Country_10years_dict_rate = {}

    print("rate_graph")
    # 10年のタグの中身を辞書化
    for key_a, value_a in Country_10years_dict.items():
        f = soup.find("td", class_=value_a)
        d = f.contents[0] #タグの中身のみ表示
        Country_10years_dict_result_now[key_a]=d
        #一時間前の値と現在の値の差分を計算して辞書化
        for difference in (Country_10years_dict_result_1hourago.keys() | Country_10years_dict_result_now.keys()):

            num_1 = float(Country_10years_dict_result_1hourago.get(difference))
            num_2 = float(Country_10years_dict_result_now.get(difference))

            total = num_2 - num_1
            Country_10years_dict_difference[difference]=total
    #それぞれの国と日本の差分を計算して辞書化
    for key_a, value_a in Country_10years_dict_difference.items():

        num_1 = float(Country_10years_dict_difference.get(key_a))
        num_2 = float(Country_10years_dict_difference.get("Japan"))
        result_a = num_1 - num_2
        if result_a == 0:
            pass
        else:
            Country_10years_dict_rate[key_a]=(round(result_a, 7))
    print(Country_10years_dict_rate)
    #Country_10years_dict_rateをグラフに

    fig, ax = plt.subplots()
    xs = []
    ys = []

    for i, d in Country_10years_dict_rate.items():

        xs.append(i)
        ys.append(d)

        ax.set_xlim(-1, 10)
        ax.set_ylim(-10, 30)
        ax.plot(xs, ys)

        plt.text(i, d, d)
        plt.title('RATE')
        dt_now = datetime.datetime.now()
        dirname = r"D:\python project\portforio\interest rate comparison\graf_image"
        filename = dirname + dt_now.strftime('%Y%m%d_%H%M%S') + '.png'
        plt.savefig(filename)

schedule.every(10).seconds.do(rate_graph)

while True:
    schedule.run_pending()
    time.sleep(1)

interest rate comparison/interest rate comparison.py


from itchat.content import *
from ProcessInterface import ProcessInterface
import itchat
import re
import logging

class PaiDuiHook(ProcessInterface):
    groupContentCacheMaxCapacity = 5
    maxSelfPaiDuiTTL = 15

    def __init__(self, blacklist=[]):
        self.blacklist = blacklist
        self.groupLastMsgsDict = {}
        # A dictionary controlling not pai dui for more than one time
        # Key: (groupName, content), Value: TTL (0 or non-exist means OK to paidui)
        self.selfPaiDuiTTL = {}
        logging.info('PaiduiHook initialized.')

    def WhatToPaiDui(self, groupName):
        msgCount = {}
        msgs = self.groupLastMsgsDict[groupName]
        for msg in msgs:
            if msg['Content'] not in msgCount:
                msgCount[msg['Content']] = 0
            msgCount[msg['Content']] += 1
        contentToPaiDui = [ x for x in msgCount if msgCount[x] > 1 ]
        if len(contentToPaiDui) == 0:
            # No dui to pai
            return
        # it's possible that two duis are formed at the same time, but only one can pass the TTL check
        for content in contentToPaiDui:
            if (groupName, content) not in self.selfPaiDuiTTL or self.selfPaiDuiTTL == 0:
                self.selfPaiDuiTTL[(groupName, content)] = self.maxSelfPaiDuiTTL
                yield content  # We use yield here because we still need to conitnue managing the TTL
            else:
                self.selfPaiDuiTTL[(groupName, content)] -= 1

    def isFromSelf(self, msg):
        if re.search('^@@', msg['ToUserName']):
            return True
        else:
            return False

    def updateGroupContentCache(self, msg, groupName):
        if groupName not in self.groupLastMsgsDict:
            self.groupLastMsgsDict[groupName] = []
        if len(self.groupLastMsgsDict[groupName]) >= self.groupContentCacheMaxCapacity:
            self.groupLastMsgsDict[groupName].pop(0)
        self.groupLastMsgsDict[groupName].append({ 'Content': msg['Content'] })

    def process(self, msg, type):
        if type != TEXT:
            return
        groupName = msg['User']['NickName']
        if any([ re.search(x, groupName) is not None for x in self.blacklist ]):
            return
        if re.search('^/', msg['Content']):
            return
        if self.isFromSelf(msg):
            # Stop processing if the message is from myself
            return

        self.updateGroupContentCache(msg, groupName)
        contentToPaiDui = list(self.WhatToPaiDui(groupName))
        if len(contentToPaiDui) > 1:
            logging.error('Multiple duis detected.')
        if len(contentToPaiDui) != 0:
            # Pai dui!
            itchat.send(msg['Content'], msg['FromUserName'])
            logging.info('Pai Dui! {0}.'.format(msg['Content']))
            # Update data structure to avoid Pai dui for multiple times.
            self.updateGroupContentCache({ 'Content': msg['Content'], 'FromSelf': True }, groupName)


#! /usr/bin/env python

from pyecharts import Pie

attr = ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "娃子"]
v1 = [11,12,13,14,10,10]
v2 = [19,21,32,20,20,33]
pie = Pie("饼图-玫瑰图示例", title_pos = "center", width = 900)
pie.add("商品A", attr, v1, center = [25,50], is_random = True, radius = [30,75], rosetype = "radius")
pie.add("商品B", attr, v1, center = [75,50], is_random = True, radius = [30,75],
        rosetype = "area", is_legend_show = False, is_labet_show = True)

pie


#!/usr/bin/env python3
# -*- coding: UTF-8 -*-

import os
import re
import sys
import json
import time
import requests
import urllib.parse

class HttpClient():
    def __init__(self, proxy = None):
        self.__session__ = requests.Session()
        self.__user_agent__ = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36"
        self.__proxies = {}
        if proxy is not None and proxy != "":
            self.__proxies["http"] = proxy
            self.__proxies["https"] = proxy

    def get(self, url, headers = {}, **params):
        headers["User-Agent"] = self.__user_agent__
        url = url.rstrip("/")
        if len(params) != 0:
            if url[-1] != "?":
                url = url + "?"
            for k in params.keys():
                url = url + ("%s=%s&" % (k, urllib.parse.quote_plus(str(params[k]))))
            url = url[0:-1]
        return self.__session__.get(url, headers = headers, timeout = 30, proxies = self.__proxies)

    def get_json(self, url, headers = {}, params = {}, encode = "utf-8"):
        response = self.__get(url, headers, params)
        return json.loads(response.content.decode("utf-8"))

class IpCache():
    def __init__(self, cache_file):
        self.__cache_file = cache_file
        self.__cache = self.__load()

    def __load(self):
        if not os.path.exists(self.__cache_file):
            return {}
        try:
            with open(self.__cache_file, "r", encoding = "UTF-8") as f:
                return json.load(f)
        except:
            return {}

    def save(self):
        with open(self.__cache_file, "w", encoding = "UTF-8") as f:
            json.dump(self.__cache, f, indent = 4, ensure_ascii = False)

    def get(self, _key):
        if _key in self.__cache:
            return self.__cache[_key]
        return None

    def set(self, _key, _value):
        self.__cache[_key] = _value

def handle(cache_file, source_url, target_file):
    http_client = HttpClient()
    ip_cache = IpCache(cache_file)
    asn_re = re.compile("var ip_result = (\{[^;]+\});")
    headers = {
        "Host": "ip138.com",
        "Referer": "https://ip138.com/"
        }
    ip_list = []
    china_list = ["上海市", "中国 ", "云南省", "内蒙古自治区", "北京市", "吉林省", "四川省", "天津市", "宁夏回族自治区", "安徽省", "山东省", "山西省", "广东省", "广西壮族自治区", "新疆维吾尔自治区", "江苏省", "江西省", "河北省", "河南省", "浙江省", "海南省", "湖北省", "湖南省", "甘肃省", "福建省", "西藏自治区", "贵州省", "辽宁省", "重庆市", "陕西省", "青海省", "黑龙江省"]
    for cidr in http_client.get(source_url).content.decode("utf-8").split("\n"):
        cidr = cidr.strip()
        if len(cidr) == 0:
            continue
        if cidr[0] == "#":
            continue
        cidr_split = cidr.split("/")
        if len(cidr_split) != 2:
            continue
        check_ip = cidr_split[0]
        check_ip = check_ip[0:len(check_ip) - 1] + "1"
        asn_data = ip_cache.get(check_ip)
        if asn_data is None:
            print("miss cache")
            try:
                html_content = http_client.get("https://ip138.com/iplookup.asp", headers = headers, ip = check_ip, action = 2).content.decode("GB18030")
            except:
                ip_cache.save()
                return
            asn_match = asn_re.findall(html_content)
            if len(asn_match) == 1:
                asn_data = json.loads(asn_match[0])
                ip_cache.set(check_ip, asn_data)
            else:
                print("error")
                print(html_content)
        is_pass = False
        for key in china_list:
            if asn_data["ASN归属地"].count(key) > 0:
                is_pass = True
                break
        if is_pass:
            ip_list.append(cidr)
            print(cidr)
    ip_cache.save()
    with open(target_file, 'w', encoding = "UTF-8") as f:
        for ip in ip_list:
            f.write(f"{ip}\n")

if __name__ == "__main__":
    if len(sys.argv) != 4:
        print("parma error")
        exit(1)
    cache_file = sys.argv[1]
    source_url = sys.argv[2]
    target_file = sys.argv[3]
    handle(cache_file, source_url, target_file)


import os
import sys
from PIL import Image
import numpy as np
import pickle as pkl

if __name__ == "__main__":

    # out path
    out_path = './data/val2017_preprocessed.pkl'

    if os.path.exists(out_path):
        print("Out file {} already exists, exiting.".format(out_path))
        sys.exit()

    # get image files:
    image_dir = "./data/val2017"
    image_file_names = [ os.path.join(image_dir, fn) for fn in os.listdir(image_dir) if os.path.splitext(fn)[1] == ".jpg" ]

    # process each file
    from algorithms.ObjectDetection import ObjectDetector

    result_dict = {}

    od = ObjectDetector()
    for fn in image_file_names:
        print("Processing #{}".format(len(result_dict)))
        image = np.asarray(Image.open(fn))
        boxes_out, labels_out, scores_out = od.Predict(image)
        #od.Plot(image, boxes_out, labels_out, scores_out)
        result_dict[fn] = {}
        result_dict[fn]["boxes"] = boxes_out
        result_dict[fn]["labels"] = labels_out
        result_dict[fn]["scores"] = scores_out

    with open(out_path, 'wb') as f:
        pkl.dump(result_dict, f)


""" RTMA PROFILER/BENCHMARK

    This module is a commandline script, which can run the library tasks in a sandboxed setting.
"""
import argparse
import time
import statistics
import json
from rtmaii import hierarchy, configuration
from numpy import arange, int16, frombuffer, sin, pi
from pydispatch import dispatcher

class Tracker(object):
    """ Tracks signal responses from library, storing response times over numerous runs.

        Args:
            - signals: Signals to be tracked.

        Attributes:
            reset (dict): contains initial state of tracker, each key value is set to None.
            tracker (dict): When threads respond with results, values set to time taken to respond.
            time_taken (dict): Stores response times over numerous runs for statisitical analysis.
    """
    def __init__(self, signals: list):
        dispatcher.connect(self.set_switch, sender=0) # Catch any signal from channel 0.
        # Could be extended to monitor delay of each channel when multi-channel analysis is enabled.
        self.reset = {key: None for key in signals}
        self.tracker = self.reset.copy()
        self.time_taken = {key: [] for key in signals}

    def store_times(self, start_time):
        """ Store response times from current run. """
        for signal, end_time in self.tracker.items():
            self.time_taken[signal].append(end_time-start_time)

    def reset_tracker(self):
        """ Reset tracker to default unfilled state """
        self.tracker = self.reset.copy()

    def set_switch(self, **kwargs):
        """ Set switch on tracker to signify that signal has been recieved. """
        if kwargs['signal'] in self.tracker:
            self.tracker[kwargs['signal']] = time.time()

    def print_times(self):
        """ Loop through tracker times printing average response time of threads. """
        for signal, times in self.time_taken.items():
            print('{} average retrieval benchmark: '.format(signal))
            print('\tMEDIAN ', statistics.median(times))
            print('\tMEAN ', statistics.mean(times))
            print('\tSTDEV ', statistics.stdev(times))

    def add_signal(self, signal):
        """ Adds signal for tracker to monitor. """
        self.reset[signal] = None
        self.time_taken[signal] = []

    def wait_for_signals(self):
        """ Wait for all values in the tracker to be filled with a return time. """
        while None in self.tracker.values():
            pass

##--- DEFAULT SETTINGS ---##
BANDS = {
    "sub-bass":[20, 60],
    "bass":[60, 250],
    "low-mid":[250, 500],
    "mid":[500, 2000],
    "upper-mid":[2000, 4000],
    "presence":[4000, 6000],
    "brilliance":[6000, 20000]
}
TASKS = {
    "pitch": True,
    "genre": True,
    "beat": True,
    "export_spectrograms" : True,
    "bands": True
}
PARSER = argparse.ArgumentParser(
    description="Benchmark each task based on configuration options used in a sandbox simulation."
    )

##--- PARSER ARGUMENTS ---##
PARSER.add_argument("-b", "--bands",
                    help="Frequency bands to analyse as a dictionary.",
                    type=json.loads, default=BANDS)
PARSER.add_argument("-s", "--samplingrate",
                    help="Sampling rate in Hertz, i.e. 44100",
                    type=int, default=44100)
PARSER.add_argument("-f", "--framespersample",
                    help="Frames per buffer sample, default is 1024",
                    type=int, default=1024)
PARSER.add_argument("-r", "--blocksize",
                    help="Size of signal before performing frequency based analysis.",
                    type=int, default=16384)
PARSER.add_argument("-t", "--tasks", help="Analysis Tasks to run.",
                    type=json.loads, default=TASKS)
PARSER.add_argument("-p", "--pitchmethod",
                    help="Pitch method to use for analysis.",
                    type=str, default='hps')
PARSER.add_argument("-n", "--noruns",
                    help="Number of runs to perform.",
                    type=int, default=10)
PARSER.add_argument("-c", "--channelcount",
                    help="Number of channels to mimic being analysed.",
                    type=int, default=1)
PARSER.add_argument('-mc', "--mergechannels", dest='mergechannels',
                    help="Merge channel data into one thread.",
                    type=bool, default=True)
PARSER.add_argument("-m", "--multichannelanalysis", dest='mergechannels',
                    help="Toggle multi channel analysis", action='store_false')
ARGS = PARSER.parse_args()

def generate_sine(sampling_rate, time_step):
    """ Generates a basic sine wave to send as a stub to hierarchy. """
    return sin(2 * pi * 440 * time_step / sampling_rate)

def main():
    """ BENCHMARKING PROCESS

        1. Creates dummy signal to send through hierarchy.
        2. Creates independent Hierarchy and Config, so audio is not needed. (Using passed args)
        3. Put dummy signal into Hierarchy X times to hit analysis threshold of some threads.
        4. Put dummy signal into Hierarchy N times (Specified by args)
        5. Print out average response times for threads.
    """
    print('Config options used in this benchmark are:')
    for key, value in ARGS.__dict__.items():
        print('\t{}: {}'.format(key, value))
    print('On average {} buffer samples will be retrieved from the audio source a second.'
          .format(ARGS.samplingrate // ARGS.framespersample))
    print('[Warning] This is only an approximation, Python can take a while to warm up.')

    time_step = arange(ARGS.framespersample * ARGS.channelcount, dtype=int16)
    stub_wave = (generate_sine(ARGS.samplingrate, time_step) * 1000).tobytes()
    stub_count = 127 # Amount needed to start genre predictions.
    ARGS.tasks['genre'] = False # Currently disabled, needs rework in order for benchmark to work

    config = configuration.Config(
        **{'bands': ARGS.bands,
           'frames_per_sample': ARGS.framespersample,
           'pitch_algorithm': ARGS.pitchmethod,
           'merge_channels': ARGS.mergechannels,
           'tasks': ARGS.tasks,
           'block_size': ARGS.blocksize
          }
        )
    config.set_source(
        {'channels': ARGS.channelcount,
         'rate': ARGS.samplingrate
        })
    root = hierarchy.Hierarchy(config, [])

    signals = []
    signals.append('signal')
    tasks = config.get_config('tasks')
    if tasks['pitch']:
        signals.append('pitch')
        signals.append('note')
    if tasks['bands']:
        signals.append('spectrum') # Used by both tasks.
        signals.append('bands')
    if tasks['beat']:
        # signals.append('bpm') Had to disable because of last minute changes from members.
        signals.append('beats')
    tracker = Tracker(signals)

    # Prepare workers, hitting thresholds for frequency coordinator and spectrogram.
    print("Preparing workers...")
    for _ in range(stub_count):
        # As some tasks have a threshold before running, we need to feed them a couple of stubs.
        root.put(frombuffer(stub_wave))
    tracker.wait_for_signals()

    if tasks['genre']: # Genre only runs once every 128 frames.
        print('[Warning] Genre is not currently supported for benchmarks.')
        # This would increase the timings of benchmarks substantially, so have currently disabled.
        #tracker.add_signal('genre')

    print("Running tests...")
    # Run the analysis N times.
    for _ in range(ARGS.noruns):
        tracker.reset_tracker()
        start_time = time.time()
        # The root conversion time is taken into account.
        root.put(frombuffer(stub_wave))
        tracker.wait_for_signals()
        tracker.store_times(start_time)
    tracker.print_times()

if __name__ == '__main__':
    main()


from django import forms
from .models import Goods

class GoodsForm(forms.ModelForm):
  # def clean_qq(self):
  #   cleaned_data = self.cleaned_data('qq')
  #   if not cleaned_data.isdigit():
  #     raise forms.ValidationError('必须是数字')
  #   return int(cleaned_data)

  class Meta:
    model = Goods
    fields = (
      'g_name', 'g_type', 'unit', 'sp_id'
    )
    dic = {}
    for field in fields:
      dic[field] = forms.TextInput(attrs={'class': 'form-control'})
    widgets = dic