import scipy.stats as st
from collections import Counter
import sklearn
from matplotlib import cm
import scipy
import  itertools
from datetime import datetime
from seaborn import clustermap
from scipy.cluster.hierarchy import linkage
import matplotlib as mpl
from sklearn.linear_model import LogisticRegression
import os
import time
import pickle as pkl
import pandas as pd
from dataLoader import *
from basic_data_methods_helper import *
from statsmodels.stats.multitest import multipletests
from matplotlib.collections import LineCollection
from Bio import Phylo
import matplotlib.colors as colors
import re
from statistics import mode
import seaborn as sns
from sksurv.linear_model import CoxnetSurvivalAnalysis, CoxPHSurvivalAnalysis
from sksurv.metrics import concordance_index_censored
import matplotlib.lines as mlines
import seaborn as sns
import argparse

# Set font for figures
from matplotlib import rc
rc('font',**{'family':'sans-serif','sans-serif':['Arial']})

import pandas as pd
from helper import *
from basic_data_methods_helper import *
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, LinearSegmentedColormap

def get_data(key, dl, dtype='filtered_data', week=None, features=None):
    data = dl.keys[key][dtype]
    ix_keep = [ix for ix in data.index.values if ix.split('-')[1].isnumeric()]
    if week == None:
        ix_keep = [ix for ix in ix_keep if float(ix.split('-')[1]) <= 2]
    else:
        ix_keep = [ix for ix in ix_keep if float(ix.split('-')[1]) == week]

    data = data.loc[ix_keep]
    if features is not None:
        data = data[features]
    outcomes = dl.keys[key]['targets'].loc[ix_keep]
    return data, outcomes

def make_side_heatmap(key, rownames,
                      path_to_univariate_analysis= '/Users/jendawk/Dropbox (MIT)/C Diff Recurrence Paper/Analyses/univariate_analysis/',
                      plot_padj=True):
    df = {}
    for week in [0, 1, 2]:
        if key == '16s' and isinstance(week, list):
            continue
        if key == '16s':
            test = 'deseq2'
            pname = 'padj'
        else:
            test = 'OLS'
            pname = 'FDR, Outcome'
        univ_anal = pd.read_csv(path_to_univariate_analysis + '/' + key + '/' + test + '_' + key + str(week) +
                                '.csv', index_col=0)
        #         df[week] = univ_anal.loc[univ_anal[pname]<0.1]
        if 't-stat' in univ_anal.columns.values:
            test_stat_lab = 't-stat'
        elif 'test-statistic' in univ_anal.columns.values:
            test_stat_lab = 'test-statistic'
        elif 'test statistic' in univ_anal.columns.values:
            test_stat_lab = 'test statistic'
        elif 'log2fold' in univ_anal.columns.values:
            test_stat_lab = 'log2fold'
        else:
            test_stat_lab = 'coef, outcome'

        if 'BH corrected' in univ_anal.columns.values:
            p_lab = 'BH corrected'
        elif 'BH Corrected' in univ_anal.columns.values:
            p_lab = 'BH Corrected'
        elif 'padj' in univ_anal.columns.values:
            p_lab = 'padj'
        else:
            p_lab = 'FDR, Outcome'

        if plot_padj:
            data_to_plot = univ_anal[[p_lab, test_stat_lab]]
        else:
            data_to_plot = univ_anal[test_stat_lab]

        not_present = list(set(rownames) - set(data_to_plot.index.values))
        for metab in not_present:
            if plot_padj:
                data_to_plot.loc[len(data_to_plot.index)] = [1, 0]
            else:
                data_to_plot.loc[len(data_to_plot.index)] = [0]
            data_to_plot.index.values[-1] = metab

        df[week] = data_to_plot.loc[rownames]
        if len(rownames) == 0:
            df[week] = data_to_plot

    return df

def get_metab_clustermap(data, key):
    x = data.T
    if key == '16s':
        dist_mat = scipy.spatial.distance.pdist(x, metric='braycurtis')
        dist_mat = scipy.spatial.distance.squareform(dist_mat)

    else:
        corr, p = st.spearmanr(x.T)
        if len(corr.shape) == 0:
            corr = np.array([[1, corr], [corr, 1]])
        dist_mat = (1 - corr) / 2
        dist_mat = (dist_mat + dist_mat.T) / 2
        np.fill_diagonal(dist_mat, 0)

    linkage = scipy.cluster.hierarchy.linkage(scipy.spatial.distance.squareform(dist_mat), method='average',
                                              optimal_ordering=True)

    g = sns.clustermap(data.T, row_linkage=linkage, col_cluster=False, row_cluster=True,
                       vmin=-4, vmax=4, cmap=plt.get_cmap('bwr'))

    g.cax.set_visible(False)
    g.fig.set_visible(False)
    return g.dendrogram_row.reordered_ind

def get_pt_clustermap(data, outcomes, key):
    rgb_outcome = {'Recurrer': matplotlib.colors.to_rgba('r')[:-1], 'Non-recurrer': matplotlib.colors.to_rgba('g')[:-1]}
    # pt_outcome = dict(zip(np.unique(pts), generate_colormap(len(np.unique(pts)))))

    o_map = outcomes.map(rgb_outcome)
    df_colors = o_map
    x = data

    if key == '16s':
        dist_mat = scipy.spatial.distance.pdist(x, metric='braycurtis')
        dist_mat = scipy.spatial.distance.squareform(dist_mat)
    else:
        corr, p = st.spearmanr(x.T)
        dist_mat = (1 - corr) / 2
        dist_mat = (dist_mat + dist_mat.T) / 2
        np.fill_diagonal(dist_mat, 0)

    linkage = scipy.cluster.hierarchy.linkage(scipy.spatial.distance.squareform(dist_mat), method='average',
                                              optimal_ordering=True)

    g = sns.clustermap(data.T, col_linkage=linkage, col_cluster=True, row_cluster=False, col_colors=df_colors,
                       vmin=-4, vmax=4, cmap=plt.get_cmap('bwr'))

    g.cax.set_visible(False)
    g.fig.set_visible(False)
    return g, rgb_outcome

def make_metab_dendrogram(df_dendro, data, fig, ax, custom_order, skip_offset = .10, buffer = 0):

    ypts_S = [0]
    sub_path_S = []
    coords_S = []
    colors_S = []
    col_dict = {}

    ypts = [0]
    sub_path = []
    hcoords = []
    vcoords = []
    hcolors = []
    vcolors = []
    annotation = []
    order_dict = {}
    order_past = 0
    pathways = []
    y_add = 0
    for j,pathway in enumerate(custom_order):
        if isinstance(pathway, list):
            path = pathway[0]
        else:
            path = pathway
        if path in df_dendro['SUB_PATHWAY'].values:
            sup_or_sub = 'SUB_PATHWAY'
        elif path in df_dendro['SUPER_PATHWAY'].values:
            sup_or_sub = 'SUPER_PATHWAY'
        else:
            print(pathway)
            print('ERROR - NO PATHWAY')
            return None
        if isinstance(pathway, list):
            metabs = np.concatenate([df_dendro.index.values[df_dendro[sup_or_sub]==path] for path in pathway])
            pathway = '; '.join(pathway)
        else:
            metabs = df_dendro.index.values[df_dendro[sup_or_sub]==pathway]
        txt = pathway
        metabs = list(set(metabs).intersection(set(data.columns.values)))
        if len(metabs)==0:
            continue
        if len(pathway)>20:
            txt = txt[:20]
        if txt[-1] != 's':
            txt = txt + 's'
    #     sub_pathways = np.unique(df_dendro['SUB_PATHWAY'][df_dendro['SUPER_PATHWAY']==pathway])
        super_gp_size = len(metabs)
        y_start_sup = ypts_S[-1] + buffer
        y_end_sup = super_gp_size + y_start_sup
        y_mid_sup = super_gp_size/2 + y_start_sup
        print(y_mid_sup)
        xpt = 3

        hline_sup = [[xpt, y_mid_sup],[xpt-2, y_mid_sup]]
        vline_sup = [[xpt-2, y_end_sup],[xpt-2, y_start_sup]]
        hcoords.append(hline_sup)
        vcoords.append(vline_sup)
        hcolors.append('C' + str(j))
        vcolors.append('C' + str(j))
    #     vcolors.extend(['C' + str(j)]*2)
        if len(metabs)>1:
            order = np.array(get_metab_clustermap(data.loc[:,metabs], 'metabs'))
            order_dict[pathway] = np.array(metabs)[order]
        else:
            order_dict[pathway] = np.array(metabs)
        pathways.append(pathway)
#         sub_pathways = np.unique(df_dendro[sup_or_sub][df_dendro[sup_or_sub]==pathway])
        annotation.append(((xpt-0.1, y_mid_sup + 0.5),txt))

        ypts_S.append(y_end_sup)

    xyvals = np.concatenate(np.concatenate([hcoords, vcoords]))

    line_segments = LineCollection(hcoords, colors = hcolors)
    ax.add_collection(line_segments)
    ax.set_xlim(np.max(xyvals[:,0]),np.min(xyvals[:,0]))
    ax.set_ylim(np.min(xyvals[:,1]), np.max(xyvals[:,1]))

    line_segments_v = LineCollection(np.array(vcoords), colors = vcolors, linewidths = 5)
    ax.add_collection(line_segments_v)

    ax.set_frame_on(False)
    ax.axes.get_xaxis().set_visible(False)
    ax.axes.get_yaxis().set_visible(False)

    fig.tight_layout()
    return fig, ax, order_dict, pathways

def plot_heatmap(key, rownames, fig, ax_heat, ax_side, dl, figsize=12 * 10, weeks=[0, 1, 2],
                 dtype='data',
                 path_to_univariate_analysis = '/Users/jendawk/Dropbox (MIT)/C Diff Recurrence Paper/Analyses/univariate_analysis_control/',
                 path_to_save_data='/Users/jendawk/Dropbox (MIT)/C Diff Recurrence Paper/Main Figures/Figure Data/',
                 cmap_heat='vlag', cmap_sig='tab:purple'):
    df_side = make_side_heatmap(key, rownames,
                                path_to_univariate_analysis, plot_padj=True)
    import matplotlib.colors as colors
    df_all = np.concatenate([df_side[i].iloc[:, 0].values for i in df_side.keys()])
    df_all = df_all[~np.isnan(df_all)]
    vmins = []
    vmaxs = []
    for i, week in enumerate(weeks):
        d_week, outcomes = get_data(key, dl, week=week, dtype=dtype, features=rownames)
        if key == '16s':
            data_week = np.divide(d_week.T, np.sum(d_week, 1)).T

        else:
            epsilon = get_epsilon(d_week)
            transformed = np.log(d_week + epsilon)
            d_week = standardize(transformed)
        vmins.append(d_week.min().min())
        vmaxs.append(d_week.max().max())

    vmin = np.min(vmins)
    vmax = np.max(vmaxs)
    for i, week in enumerate(weeks):
        d_week, outcomes = get_data(key, dl, week=week, dtype=dtype, features=rownames)
        if key == '16s':
            data_week = np.divide(d_week.T, np.sum(d_week, 1)).T

        else:
            epsilon = get_epsilon(d_week)
            transformed = np.log(d_week + epsilon)
            d_week = standardize(transformed)
        #         g, rgb_outcome = get_pt_clustermap(d_week, outcomes, key)
        outcome_ixs = np.concatenate([np.where(outcomes.values == 'Recurrer')[0],
                                      np.where(outcomes.values == 'Non-recurrer')[0]])

        re_ixs = np.where(outcomes.values == 'Recurrer')[0]
        cl_ixs = np.where(outcomes.values == 'Non-recurrer')[0]
        ix1 = len(np.where(outcomes.values == 'Recurrer')[0])

        if len(rownames) == 0:
            data = d_week.iloc[outcome_ixs, :]
        else:
            data = d_week.loc[:, rownames].iloc[outcome_ixs, :]

        data = data.T
        if key == '16s':
            data.insert(len(re_ixs), 'zeros', -99 * np.ones(data.shape[0]))
        else:
            data.insert(len(re_ixs), 'zeros', 0 * np.ones(data.shape[0]))
        outcome_locs = [ix1 / 2, 1 + ix1 + len(np.where(outcomes.values == 'Non-recurrer')[0]) / 2]
        if i == 0:
            outcome_locs[0] = outcome_locs[0] - 2
            outcome_locs[1] = outcome_locs[1] + 2

        if not os.path.isdir(path_to_save_data):
            os.mkdir(path_to_save_data)
        if os.path.isfile(path_to_save_data + '/' + 'Fig3_data.xlsx'):
            with pd.ExcelWriter(path_to_save_data + '/' + 'Fig3_data.xlsx', mode='a') as writer:
                data.to_excel(writer, sheet_name=key)
        else:
            with pd.ExcelWriter(path_to_save_data + '/' + 'Fig3_data.xlsx', mode = 'w') as writer:
                data.to_excel(writer, sheet_name = key)
        if key != '16s':
            tmin, tmax, N = -2, 2, 512
            lowval = N * (abs(vmin - tmin) / (vmax - vmin))
            highval = N - N * (abs(vmax - tmax) / (vmax - vmin))
            temp = sns.color_palette(cmap_heat, int(highval - lowval), as_cmap=True)
            new_colors = np.zeros((N, 4))
            new_colors[:int(N / 2), :] = temp(0.9999999)
            new_colors[int(N / 2):, :] = temp(0)
            new_colors[int(lowval):int(highval), :] = temp(np.linspace(1,0, int(highval) - int(lowval)))
            new = ListedColormap(new_colors)
            pos = ax_heat[i].imshow(data, cmap=new,
                                    vmin=vmin, vmax=vmax, interpolation='nearest', aspect='auto');
        else:
            pos = ax_heat[i].imshow(data + 1, cmap=sns.light_palette(cmap_heat, as_cmap=True),
                                    interpolation='nearest', aspect='auto',
                                    norm=colors.LogNorm(vmin=1, vmax=vmax));

        ax_heat[i].set_frame_on(False)
        ax_heat[i].axes.get_yaxis().set_visible(False)
        ax_heat[i].grid(False, axis='x')
        if float(week) == 0:
            ax_heat[i].set_xlabel('Pre-antibiotic \n Treatment')
        else:
            ax_heat[i].set_xlabel('Week ' + str(week))
        ax_heat[i].xaxis.set_label_position('bottom')
        outcomes = np.expand_dims(outcomes, 0)
        ax_heat[i].set_xticks(outcome_locs)
        ax_heat[i].xaxis.tick_top()
        # if i == 0:
        #     ax_heat[i].set_xticklabels(['Recurrer       ', 'Non-recurrer'], ha='center')
        # else:
        ax_heat[i].set_xticklabels(['R', 'NR'], ha='center')
        ax_heat[i].tick_params(axis=u'both', which=u'both', length=0)
        colors2 = ['darkslategray', 'darkcyan']

        if 't-stat' in df_side[i].columns.values:
            df_side[i]['t-stat'] = df_side[i]['t-stat'].fillna(0)
        elif 'coef, outcome' in df_side[i].columns.values:
            df_side[i]['coef, outcome'] = df_side[i]['coef, outcome'].fillna(0)
        else:
            df_side[i].iloc[:, -1:] = df_side[i].iloc[:, -1:].fillna(0)

        df_side[i].iloc[:, 0] = df_side[i].iloc[:, 0].fillna(1)

        right = ax_side[i].imshow(df_side[i].iloc[:, 0:1], interpolation='nearest', aspect='auto',
                                  cmap=sns.light_palette(cmap_sig, reverse=True,
                                                                            as_cmap=True),
                                  norm=colors.LogNorm(vmin=1e-4, vmax=1))

        if 't-stat' in df_side[i].columns.values:
            test_stat_lab = 't-stat'
        elif 'test-statistic' in df_side[i].columns.values:
            test_stat_lab = 'test-statistic'
        elif 'test statistic' in df_side[i].columns.values:
            test_stat_lab = 'test statistic'
        elif 'log2fold' in df_side[i].columns.values:
            test_stat_lab = 'log2fold'
        elif 'coef, outcome' in df_side[i].columns.values:
            test_stat_lab = 'coef, outcome'

        if 'BH corrected' in df_side[i].columns.values:
            p_lab = 'BH corrected'
        elif 'BH Corrected' in df_side[i].columns.values:
            p_lab = 'BH Corrected'
        elif 'padj' in df_side[i].columns.values:
            p_lab = 'padj'
        else:
            p_lab = 'FDR, Outcome'
        labels = df_side[i][test_stat_lab].copy()
        labels[(df_side[i][test_stat_lab].astype('float64') < 0) * (df_side[i][p_lab].astype('float64') <= 0.05)] = '\u2193'
        labels[(df_side[i][test_stat_lab].astype('float64') > 0) * (df_side[i][p_lab].astype('float64') <= 0.05)] = '\u2191'
        labels[df_side[i][test_stat_lab].astype('float64') == 0] = ''
        labels[df_side[i][p_lab].astype('float64') > 0.05] = ''
        labels[np.isnan(df_side[i].iloc[:, -1])] = ''
        ax_side[i].set_yticks(np.arange(df_side[i].shape[0]))
        ax_side[i].set_yticklabels(labels)
        ax_side[i].tick_params(axis='y', which='major', pad=0.05, left=False, labelleft=False, labelright=True)
        ax_side[i].tick_params(axis=u'both', which=u'both', length=0)
        ax_side[i].yaxis.set_label_position('right')
        ax_side[i].axes.get_xaxis().set_visible(False)
        ax_side[i].set_frame_on(False)

    fig.tight_layout()
    fig.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=0.1, hspace=0.01)

    return fig, ax_heat, ax_side, right, pos

def lookup_by_names(tree, otu_dict, seq_to_otu,
                    path_to_univariate_analysis = '/Users/jendawk/Dropbox (MIT)/C Diff Recurrence Paper/Analyses/univariate_analysis/',
                    filenames = 'deseq2_16s',
                    branch_len=0.1):
    sub_gps_to_name = []
    for week in [0, 1, 2]:
        univ_anal = pd.read_csv(path_to_univariate_analysis + '/16s/' + filenames + str(week) +
                                '.csv', index_col=0)
        otus = univ_anal.index.values[univ_anal['padj'] < 0.1]
        sub_gps_to_name.extend(otus)
    sub_gps_to_name = np.unique(sub_gps_to_name)
    otu_to_name = [seq_to_otu[seq] for seq in sub_gps_to_name if seq in seq_to_otu.keys()]

    names = {}
    otu_order = []
    for clade in tree.find_clades():
        if clade.branch_length:
            if clade.branch_length > branch_len:
                clade.branch_length = branch_len
        if clade.name:
            if clade.name not in otu_dict.keys():
                dropped = tree.prune(clade)
                continue
            #             if clade.name in otu_to_name:
            #                 clade.color = 'red'
            otu_order.append(clade.name)
            #             if clade.name == 'ASV_83':
            #                 clade.name = otu_dict[clade.name]['tax'] + ',\n' + clade.name.replace('_','')
            if isinstance(otu_dict[clade.name]['tax'], str):
                if ' ' in otu_dict[clade.name]['tax']:
                    clade.name = '*' + otu_dict[clade.name]['tax'].replace(
                        '_', ' ') + ', ' + clade.name.replace('_', ' ')
                else:
                    clade.name = '**' + otu_dict[clade.name]['tax'].replace(
                        '_', ' ') + ', ' + clade.name.replace('_', ' ')
            else:
                clade.name = ' '.join(list(otu_dict[clade.name]['tax'].dropna())[5:]).replace(
                    '_', '') + ', ' + clade.name.replace('_', ' ')

            if clade.name == '**, ASV 89' or clade.name == '**, ASV 204':
                clade.name = '***Lachnospiraceae' + clade.name.split('**')[-1]
            if clade.name == '**, ASV 132':
                clade.name = '***Ruminococcaceae' + clade.name.split('**')[-1]

            names[clade] = clade.name

    return names

if __name__ == "__main__":

    # set data_path to point to directory with data
    save_path = '/Users/jendawk/Dropbox (MIT)/C Diff Recurrence Paper/'

    data_path = save_path + "Data/"

    # Option to change filtering criteria
    dl = dataLoader(path=data_path, pt_perc={'metabs': .25, '16s': .1, 'scfa': 0, 'toxin': 0}, meas_thresh=
    {'metabs': 0, '16s': 10, 'scfa': 0, 'toxin': 0},
                    var_perc={'metabs': 50, '16s': 5, 'scfa': 0, 'toxin': 0}, pt_tmpts=1)

    path_to_save = '/Users/jendawk/Dropbox (MIT)/C Diff Recurrence Paper/Analyses/'

    path_univariate = path_to_save + 'univariate_analysis/'

    feats_dict_sm = {}
    # feats_dict_labels = {}
    for dtype in ['16s', 'metabs']:
        feats = []
        imp_feats = []
        print(dtype)
        univariate = path_to_save + 'univariate_analysis/'
        for file in os.listdir(univariate + dtype):
            if '.csv' not in file or 'ecurrer' in file:
                continue
            if dtype == '16s' and 'deseq2' not in file:
                continue
            if dtype == 'metabs' and 'ttest' not in file:
                continue
            pvals = pd.read_csv(path_univariate + dtype + '/' + file, index_col=[0])
            if 'padj' in pvals.columns.values:
                pa = 'padj'
            else:
                pa = 'BH corrected'
            p_imp = pvals.index.values[pvals[pa] <= 0.05]
            imp_feats.extend(p_imp)

        feats_dict_sm[dtype] = np.unique(imp_feats)
        print(len(np.unique(imp_feats)))
        print('')

    ratios = {}
    for key in 'metabs', '16s', 'scfa':
        data, _ = get_data(key, dl, dtype='filtered_data')
        total = data.shape[0]
        ratios[key] = {}
        for week in [0, 1, 2]:
            data, _ = get_data(key, dl, week=week, dtype='filtered_data')
            ratios[key][week] = (data.shape[0] / total) * 14

    fig, ax = plt.subplots(1, 10, gridspec_kw={'width_ratios': [2, 4, ratios['metabs'][0], 0.5, 0.15,
                                                                ratios['metabs'][1], 0.5, 0.15, ratios['metabs'][2],
                                                                0.5]},
                           figsize=(7.5, 3.6875),
                           constrained_layout=False)

    set_font_sizes(None, {'font': 10, 'axes_title': 10, 'axes_label': 10,
                          'xtick_label': 8.5, 'ytick_label': 8,
                          'legend': 10, 'figure_title': 12})

    # ax_metabs = ax[0,[1,3,5]]
    # ax_dendro = ax[0,0]
    # ax_top = ax[1,[1,3,5]]
    # ax_side = ax[0,[2,4,6]]
    # ax_inv = ax[1,[0,2,4,6]]

    ax_metabs = ax[[2, 5, 8]]
    ax_dendro = ax[0]
    ax_side = ax[[3, 6, 9]]
    ax_inv = ax[[0, 1, 3, 6, 9]]
    ax_inv_bw = ax[[1, 4, 7]]

    for axx in ax_inv_bw:
        axx.set_visible(False)

    weeks = [0, 1, 2]

    custom_order = ['Hemoglobin and Porphyrin Metabolism', 'Secondary Bile Acid Metabolism',
                    ['Progestin Steroids', 'Estrogenic Steroids', 'Androgenic Steroids', 'Corticosteroids'],
                    'Endocannabinoid', ['Phosphatidylcholine (PC)', 'Lysophospholipid'],
                    'Sphingomyelins', 'Urea cycle; Arginine and Proline Metabolism', 'Carbohydrate',
                    'Food Component/Plant',
                    ['Drug - Antibiotic', 'Drug - Analgesics, Anesthetics']]
    # custom_order = np.flip(custom_order)

    col_mat_df = dl.col_mat_mets
    col_mat_sorted = col_mat_df.sort_values(by=['SUPER_PATHWAY', 'SUB_PATHWAY'])
    biochem_sorted = col_mat_sorted.index.values
    df_dendro = col_mat_sorted[['SUPER_PATHWAY', 'SUB_PATHWAY']].astype('category')

    # df_dendro = df_dendro.replace('Partially Characterized Molecules','Partially \nCharacterized \nMolecules')
    # df_dendro = df_dendro.replace('Cofactors and Vitamins', 'Cofactors \nand \nVitamins')

    dat, _ = get_data('metabs', dl, dtype='data', features=feats_dict_sm['metabs'])

    fig, ax_dendro, order_dict, pathways = make_metab_dendrogram(df_dendro, dat, fig, ax_dendro, custom_order)
    rownames = np.concatenate([order_dict[path] for path in pathways])
    # rownames = np.flip(rownames)

    # rownames = dat.columns.values
    fig, ax_metabs, ax_side, right, pos = plot_heatmap('metabs', rownames, fig, ax_metabs, ax_side, dl,
                                                       cmap_heat="vlag", cmap_sig='gold')
    # df_side = make_side_heatmap('metabs', rownames, fig, ax_side_strip,
    #                             path_to_save, colormap = 'PRGn', plot_padj = True)

    ax_metabs[0].axes.get_yaxis().set_visible(True)
    set_font_sizes(None, {'font': 4.5})
    ax_metabs[0].set_yticks(np.arange(len(rownames)))

    # slab = get_rownames(rownames)
    ax_metabs[0].set_yticklabels(rownames, fontsize=10)
    ax_metabs[0].tick_params('both', length=0, which='major')

    ax_dendro.set_frame_on(False)
    ax_dendro.axes.get_xaxis().set_visible(False)
    ax_dendro.axes.get_yaxis().set_visible(False)

    set_font_sizes(None, {'font': 8})
    arr_x = 0.96
    arr_y = 0.08
    # cbaxes = fig.add_axes([arr_x + .08, -.2, 0.04, 0.45])
    # cbaxes.set_xlabel('FDR', labelpad = 5, loc = 'left')
    # cb = fig.colorbar(right, cax = cbaxes)
    # # cbaxes.set_yticklabels([r'$10^{-24}$',r'$10^{-18}$',r'$10^{-12}$',r'$10^{-6}$','1'])
    # cbaxes.xaxis.set_label_position('top')

    cbaxes2 = fig.add_axes([arr_x + .081, 0.13, 0.032, 0.45])
    cb2 = fig.colorbar(pos, cax=cbaxes2)
    cbaxes2.set_xlabel('Standardized\nMetabolite\nLevels', loc='center')
    # cbaxes2.set_yticklabels([0,10,r'$10^{2}$',r'$10^{3}$',r'$10^{4}$'])
    cbaxes2.xaxis.set_label_position('top')
    fig.savefig(path_to_save + 'output_figures/Fig3/metabs_sm_005.pdf', bbox_inches='tight')


import time, datetime
from google.cloud import firestore
import os
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = './credentials/YOUR_GOOGLE_APPLICATION_CREDENTIALS_FILE.json'

PROJECT_ID = "spotify-co-featuring2gra-34f8c"
MODE = 'staging'
PATH_DASHBOARD = MODE + '-dashboard'
PATH_ARTIST_DATA = MODE + '-artist_data'
PATH_DUMP = MODE + '-exceeds_dump'
PATH_LOG = MODE + '-log'

NODE_LIMIT = 1000

def traverse(artist_id):
    db = firestore.Client(project=PROJECT_ID)
    transaction = db.transaction()
    artist_doc_ref = db.collection(PATH_ARTIST_DATA).document(artist_id)
    total_traverse_ref = db.collection(PATH_DASHBOARD).document('total_nodes_traversed')

    @firestore.transactional
    def update_traverse_stats(
        transaction,
        artist_doc_ref,
        total_traverse_ref
        ):
        snapshot_artist_doc = artist_doc_ref.get(transaction=transaction)
        snapshot_total_traversed = total_traverse_ref.get(transaction=transaction)
        total_traversed = snapshot_total_traversed.get('value')
        transaction.update(artist_doc_ref, {'is_traversed':True})
        transaction.update(total_traverse_ref, {'value':total_traversed+1})
        return True
    status = update_traverse_stats(
        transaction,
        artist_doc_ref,
        total_traverse_ref
    )
    return status

def is_data_exists(artist_id):
    db = firestore.Client(project=PROJECT_ID)
    dump_collection_ref = db.collection(PATH_DUMP)
    in_dump_collection = dump_collection_ref.document(artist_id).get().exists
    if (in_dump_collection):
        return True
    artist_collection_ref = db.collection(PATH_ARTIST_DATA)
    in_artist_collection = artist_collection_ref.document(artist_id).get().exists
    if (in_artist_collection):
        return True
    return False

def update_collaborators_of_an_artist(artist_id, collaborators):
    db = firestore.Client(project=PROJECT_ID)
    transaction = db.transaction()
    artist_collection_ref = db.collection(PATH_ARTIST_DATA)
    dump_collection_ref = db.collection(PATH_DUMP)

    @firestore.transactional
    def update_collaborators(
        transaction,
        artist_id,
        collaborators,
        artist_collection_ref,
        ):
        artist_doc_ref = artist_collection_ref.document(artist_id)
        snapshot_artist_doc = artist_doc_ref.get(transaction=transaction)
        existing_collaborators = snapshot_artist_doc.get('collaborators')
        new_collaborators = list(set(collaborators) - set(existing_collaborators))
        transaction.update(
            artist_doc_ref,
            {
                'collaborators':existing_collaborators + new_collaborators,
                'is_traversed': True
            }
        )
        return True

    update_success = update_collaborators(
        transaction=transaction,
        artist_id=artist_id,
        collaborators=collaborators,
        artist_collection_ref=artist_collection_ref,
        )
    return update_success

def pop_from_queue(count):
    db = firestore.Client(project=PROJECT_ID)
    transaction = db.transaction()
    queue_doc_ref = db.collection(PATH_DASHBOARD).document('queue')
    total_in_queue_ref = db.collection(PATH_DASHBOARD).document('total_in_queue')

    @firestore.transactional
    def pop_out_traverse_queue(
        transaction,
        count,
        queue_ref,
        total_in_queue_ref):

        snapshot_queue = queue_ref.get(transaction=transaction)
        existing_queue = snapshot_queue.get('ids')
        popped_out = []
        if len(existing_queue) >= count:
            for _ in range (count):
                popped_out.append(existing_queue.pop(0))
        else:
            for _ in range (len(existing_queue)):
                popped_out.append(existing_queue.pop(0))

        if len(popped_out) > 0:
            snapshot_total_in_queue = total_in_queue_ref.get(transaction=transaction)
            total_in_queue = snapshot_total_in_queue.get('value')
            transaction.update(queue_ref, {'ids':existing_queue})
            transaction.update(total_in_queue_ref,{'value':total_in_queue - len(popped_out)})
            return popped_out, True
        else:
            print("not traversable")
            return popped_out, False

    popped_out, is_traversable = pop_out_traverse_queue(
        transaction=transaction,
        count=count,
        queue_ref=queue_doc_ref,
        total_in_queue_ref=total_in_queue_ref
    )
    return popped_out, is_traversable

def add_artist_data(artist_id, data):
    db = firestore.Client(project=PROJECT_ID)
    transaction = db.transaction()

    artist_collection_ref = db.collection(PATH_ARTIST_DATA)
    queue_doc_ref = db.collection(PATH_DASHBOARD).document('queue')
    total_in_queue_ref = db.collection(PATH_DASHBOARD).document('total_in_queue')
    total_in_dump_ref = db.collection(PATH_DASHBOARD).document('total_in_dump')
    total_nodes_ref = db.collection(PATH_DASHBOARD).document('total_nodes')
    dump_collection_ref = db.collection(PATH_DUMP)

    @firestore.transactional
    def add_new_artist_data(
        transaction,
        artist_id,
        data,
        artist_collection_ref,
        total_in_queue_ref,
        total_in_dump_ref,
        total_nodes_ref,
        queue_ref,
        dump_collection_ref):
        artist_doc_ref = artist_collection_ref.document(artist_id)
        snapshot_artist_doc = artist_doc_ref.get(transaction=transaction)
        inside_artists_collection = snapshot_artist_doc.exists

        dump_doc_ref = dump_collection_ref.document(artist_id)
        snapshot_dump_doc = dump_doc_ref.get(transaction=transaction)
        inside_dump = snapshot_dump_doc.exists

        snapshot_total_in_queue = total_in_queue_ref.get(transaction=transaction)
        snapshot_total_nodes = total_nodes_ref.get(transaction=transaction)
        total_in_queue = snapshot_total_in_queue.get('value')
        total_nodes = snapshot_total_nodes.get('value')
        above_limit = (total_nodes) >= NODE_LIMIT
        # 1 add to artist_data
        if (not inside_artists_collection and not inside_dump and not above_limit):
            snapshot_queue = queue_ref.get(transaction=transaction)
            existing_queue = snapshot_queue.get('ids')
            # update queue --> since it's new
            existing_queue.append(artist_id)
            transaction.update(queue_ref, {'ids':existing_queue})
            transaction.set(artist_doc_ref, data)
            # update dashboard
            transaction.update(total_in_queue_ref,{'value':total_in_queue+1})
            transaction.update(total_nodes_ref,{'value':total_nodes+1})
            return True, "Addded to artist_data"

        # 2 add to dump
        elif (not inside_artists_collection and not inside_dump and above_limit):
            snapshot_total_in_dump = total_in_dump_ref.get(transaction=transaction)
            total_in_dump = snapshot_total_in_dump.get('value')
            transaction.update(total_in_dump_ref, {'value': total_in_dump+1})
            transaction.set(dump_doc_ref, data)
            # update dashboard
            return True, "Total nodes already "+str(NODE_LIMIT)+". Addded to dump"

        # 3 present in dump, below limit. move to artist_data
        elif (not inside_artists_collection and inside_dump and not above_limit):
            # do nothing
            # move data from dump into artist_data
            # - read on dump
            payload = snapshot_dump_doc.get('')
            # - update collaborators
            existing_collaborators = payload['collaborators']
            new_collaborators = list(set(data['collaborators']) - set(existing_collaborators))
            payload['collaborators'] = existing_collaborators + new_collaborators

            # update dashboard data
            # - queue data
            snapshot_queue = queue_ref.get(transaction=transaction)
            existing_queue = snapshot_queue.get('ids')
            existing_queue.append(artist_id)
            # - total in dump (decrement)
            snapshot_total_in_dump = total_in_dump_ref.get(transaction=transaction)
            total_in_dump = snapshot_total_in_dump.get('value')
            transaction.update(total_in_dump_ref, {'value': total_in_dump-1})
            transaction.update(queue_ref, {'ids':existing_queue})
            # update dashboard
            # - total in queue (increment)
            transaction.update(total_in_queue_ref,{'value':total_in_queue+1})
            # - total nodes (increment)
            transaction.update(total_nodes_ref,{'value':total_nodes+1})
            # - write to artist_data
            transaction.set(artist_doc_ref, payload)
            # - delete on dump
            transaction.delete(dump_doc_ref)
            return True, "In Dump. Moved to artist_data. Collaborators updated"

        # 4 update collaborators in dump
        elif (not inside_artists_collection and inside_dump and above_limit):
            existing_collaborators = snapshot_dump_doc.get('collaborators')
            new_collaborators = list(set(data['collaborators']) - set(existing_collaborators))
            if len(new_collaborators) > 0:
                transaction.update(dump_doc_ref, {'collaborators': existing_collaborators + new_collaborators})
                return True, 'Already in dump, collaborators updated'
            else:
                return True, 'Already in dump. No update needed'

        # 5-6 update collaborators in artist_data (NOT A TRAVERSAL)
        elif (inside_artists_collection and not inside_dump and (not above_limit or above_limit)):
            existing_collaborators = snapshot_artist_doc.get('collaborators')
            new_collaborators = list(set(data['collaborators']) - set(existing_collaborators))
            if len(new_collaborators) > 0:
                transaction.update(artist_doc_ref, {
                    'collaborators': existing_collaborators + new_collaborators,
                })
                return True, 'Already in artist_data. Collaborators updated'
            else:
                return True, 'Already in artist data. No update needed'

        # 7 IMPOSSIBLE: present in dump and artist_data, below limit
        elif (inside_artists_collection and inside_dump and not above_limit):
            return False, "ERROR: present in both artist_data & dump. nodes < "+str(NODE_LIMIT)
        # 8 IMPOSSIBLE: present in dump and artist data, above limit
        elif (inside_artists_collection and inside_dump and above_limit):
            return False, "ERROR: present in both artist_data & dump. nodes >= "+str(NODE_LIMIT)
        else:
            print(77*"=!=")
            print(f'inside_artists_collection: {inside_artists_collection}')
            print(f'inside_dump: {inside_dump}')
            print(f'total_nodes: {total_nodes}')
            return False, "ERROR: UNHANDLED CASE"
    status, message = add_new_artist_data(
        transaction = transaction,
        artist_id = artist_id,
        data = data,
        artist_collection_ref = artist_collection_ref,
        total_in_queue_ref = total_in_queue_ref,
        total_in_dump_ref = total_in_dump_ref,
        total_nodes_ref = total_nodes_ref,
        queue_ref = queue_doc_ref,
        dump_collection_ref = dump_collection_ref
    )
    time.sleep(0.05)
    return status, message

def log_info(error_data):
    db = firestore.Client(project=PROJECT_ID)
    transaction = db.transaction()

    errors_collection_ref = db.collection(PATH_LOG)

    @firestore.transactional
    def add_log(transaction, error_data, errors_collection_ref):
        timestamp = datetime.datetime.now()
        timestamp = str(timestamp)
        error_doc_ref = errors_collection_ref.document(timestamp)
        snapshot_errors_doc = error_doc_ref.get(transaction=transaction)
        transaction.set(error_doc_ref, error_data)
        print("Activity logged!")
        print(error_data)

    add_log(
        transaction=transaction,
        error_data=error_data,
        errors_collection_ref=errors_collection_ref
    )


from __future__ import print_function
import sys
import xml
import xml.dom
import xml.dom.minidom
import math
import planetMakerHelper
stardatafile=None
arg=0
remakePlanets=0
while (arg<len(sys.argv)):
	match=0
	if sys.argv[arg].find("-csv")==0:
		stardatafile=sys.argv[arg][4:]
		match=1
	if sys.argv[arg].find("-replanet")==0:
		remakePlanets=1
		match=1
	if (match==1):
		del sys.argv[arg]
		arg-=1
	arg+=1
def getParentVal(node,val):
	i = node.parentNode
	while(i):
		j = i.firstChild
		while (j):
			try:
				if (j.tagName!='var'):
					return None
				if (j.getAttribute('name')==val):
					return j.getAttribute('value')
			except:
				pass
			j = j.nextSibling
		i = node.parentNode
	return getParentVal(node,val)
def toPair (s):
	if (not s):
		return None
	k=s.split(' ')
	return (float(k[0]),float(k[1]),float(k[2]))
def getVal(node,val):
	for i in node.getElementsByTagName('var'):
		if (i.getAttribute('name')==val):
			return i.getAttribute('value')
	return getParentVal(node,val)
def getValND(node,val):
	for i in node.getElementsByTagName('var'):
		if (i.getAttribute('name')==val):
			return i.getAttribute('value')
	return None
def removeVal(node,val):
	for i in node.getElementsByTagName('var'):
		if (i.getAttribute('name')==val):
			node.removeChild(i)
fil = open(sys.argv[1],"r")
g = xml.dom.minidom.parseString(fil.read());
fil.close()
planets= g.getElementsByTagName('planet')
systems = g.getElementsByTagName('system')
stardata=None
starcoords=[]

if (stardatafile):
	fsd = open(stardatafile)
	stardatalines=fsd.readlines()
	stardatalines=stardatalines[1:]
	for i in range(len(stardatalines)):
		stardatalines[i]=stardatalines[i].strip().split(',')
		if (len(stardatalines[i])<4):
			starcoords.append((1./0.000001,1./0.00000001,1./0.000000000001))
			print('error '+str(stardatalines[i]))
			continue
		rad = float(stardatalines[i][1])
		asc = float(stardatalines[i][2])
		dec = float(stardatalines[i][3])
		radcos = rad*math.cos(dec);
		coord = (radcos*math.sin(asc),radcos*math.cos(asc),rad*math.sin(dec))
		starcoords.append(coord)
	stardata=stardatalines
if (stardata):
	for s in systems:
		removeVal(s,'designation')
		coord = toPair(getVal(s,'xyz'))
		if (not coord):
			continue
		for k in range(len(starcoords)):
			c=starcoords[k]
			x=c[0]-coord[0]
			y=c[1]-coord[1]
			z=c[2]-coord[2]
			if (x*x+y*y+z*z<.000001):
				if (not len(stardata[k][0])):
					break;
				print(s.getAttribute('name') +' same as '+stardata[k][0]);
				newchild = xml.dom.minidom.Element('var')
				newchild.setAttribute('name','designation')
				newchild.setAttribute('value',stardata[k][0])
				s.insertBefore(newchild,s.firstChild)
				break;

if (remakePlanets):
	for s in systems:
		removeVal(s,'planets')
		newchild = xml.dom.minidom.Element('var')
		newchild.setAttribute('name','planets')
		newchild.setAttribute('value',planetMakerHelper.getPlanetsString(getVal(s,'faction'),planets,float(getVal(s,"sun_radius")),s.getAttribute('name')))
		s.insertBefore(newchild,s.firstChild)
fil = open (sys.argv[2],"w")
fil.write(g.toxml())
fil.close()


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

import os
import web
import math
import datetime
import time
import urllib

from util import render_mako
from util import StaticDirHandler
from util import StaticFileHandler

def localtime(d):
  return d + datetime.timedelta(seconds=28800)

def gmttime(d):
  return d - datetime.timedelta(seconds=28800)

curdir = os.path.dirname(__file__)

#Mako templates
render = render_mako(
  directories=[os.path.join(curdir, 'templates').replace('\\','/'),],
           input_encoding='utf-8',
           output_encoding='utf-8',)

#connect to db
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, subqueryload, contains_eager
engine = create_engine('sqlite:///%s/blog_beta.db'%curdir, convert_unicode=True,
      encoding='utf-8',
      echo=True,
      )
Session = sessionmaker(bind=engine)

#ORM domains
from domains import *
import config

import markupsafe
def myhtml(text):
  tempText = markupsafe.escape(text)
  return ('%s' % tempText).replace('\n','
')

def LoadInfo():
  d = {}
  d['localtime'] = localtime
  d['site_url'] = config.site_url
  d['site_generator'] = config.site_generator
  d['site_name'] = config.site_name;
  d['site_subname'] = config.site_subname;
  d['site_title'] = config.site_name + ' - ' + config.site_subname;
  d['site_keywords'] = config.site_keywords;
  d['site_description'] = config.site_description;
  d['myhtml'] = myhtml;

  #get latest items
  session = Session()
  try:
    d['catlist'] = session.query(ItemCat).\
              order_by(ItemCat.orders.asc()).\
              all()
    d['newitemlist'] =  session.query(Item).\
          join(Item.itemcat).\
          filter(Item.pubdate < datetime.datetime.utcnow() ).\
          filter(Item.ispass == True).\
          options(contains_eager(Item.itemcat)).\
          order_by(Item.pubdate.desc())\
          [:15]
    d['newcommentlist'] =  session.query(ItemComment).\
          join(ItemComment.item).\
          filter(Item.pubdate < datetime.datetime.utcnow() ).\
          filter(ItemComment.ispass == True).\
          options(contains_eager(ItemComment.item)).\
          order_by(ItemComment.adddate.desc())\
          [:15]
  finally:
    session.close()
  return d

def GetClass(className, startIndex = 0, pageSize = 25, session = None):
  needClose = False
  if not session:
    needClose = True
    session = Session()
  try:
    return session.query(Item).\
        join(Item.itemcat).\
        filter(ItemCat.beta == className.lower()).\
        filter(Item.pubdate < datetime.datetime.utcnow() ).\
        options(contains_eager(Item.itemcat)).\
        order_by(Item.pubdate.desc())\
        [startIndex:startIndex+pageSize]
  finally:
    if needClose:
      session.close()

#class list
class ClassList:
  def GET(self, className, page = 1):
    d = LoadInfo()
    session = Session()
    d['recodecount'] = 0
    d['pagecount'] = 0
    page = int(page)
    pageSize = 5

    startIndex = pageSize*(page-1)
    d['itemlist'] = GetClass(className, startIndex , pageSize)

    if web.ctx.env.get('HTTP_HOST','').startswith('localhost'):
      d['debug'] = True
    if(d['itemlist']):
      d['itemCat'] = d['itemlist'][0].itemcat
      if page==0: page=1
      d['recodecount'] = session.query(Item).\
          filter(Item.pubdate < datetime.datetime.utcnow() ).\
          filter(Item.itemcat_id == d['itemCat'].id).count()
      d['pagecount'] =  int(math.ceil( d['recodecount'] / float(pageSize)))
      if d['pagecount']==0: d['pagecount']=1
    else:
      try:
        d['recodecount'] = 0
        d['pagecount'] = 0
        d['itemCat'] = session.query(ItemCat).\
          filter(ItemCat.beta == className.lower()).\
          first();
      finally:
        session.close()

    if d.has_key('itemCat') and d['itemCat']:
      d['site_title'] = u'%s-%s:第%s页' % (d['itemCat'].name, config.site_name, page)
      d['site_keywords'] = d['itemCat'].name
      d['site_description'] = d['itemCat'].name + u'相关'
      d['page'] = page
      return render.classify(**d)
    return web.notfound(self)#not found

def quote_u(a):
  return urllib.quote(a.encode('utf-8'))

#item detail
class ItemDetail:
  def GET(self, itembeta, page=1):
    d = LoadInfo()
    session = Session()
    try:
      d['item'] = session.query(Item).\
        join(Item.itemcat).\
        filter(Item.pubdate < datetime.datetime.utcnow() ).\
        filter(Item.beta == itembeta).\
        filter(Item.ispass == True).\
        options(contains_eager(Item.itemcat)).\
        first()

      if(d['item']):
        d['site_title'] = d['item'].title + config.site_name;
        d['site_description'] = d['item'].content[:100];

        d['recodecount'] = 0
        d['pagecount'] = 0
        page = int(page)
        pageSize = 10

        startIndex = pageSize*(page-1)

        #comment total
        d['commentcount'] =  session.query(ItemComment).\
          join(ItemComment.item).\
          filter(ItemComment.item_id == d['item'].id).\
          options(contains_eager(ItemComment.item)).\
          count()

        d['page'] = 0
        if d['commentcount'] and d['commentcount'] > 0:
          #comment list
          d['commentlist'] =  session.query(ItemComment).\
            join(ItemComment.item).\
            options(contains_eager(ItemComment.item)).\
            filter(ItemComment.item_id == d['item'].id).\
            filter(ItemComment.ispass == True).\
            order_by(ItemComment.adddate.desc())\
            [startIndex:startIndex+pageSize]
          if page==0: page=1
          d['pagecount'] =  int(math.ceil( d['commentcount'] / float(pageSize)));
          if d['pagecount']==0: d['pagecount']=1
          d['page'] = page

        if web.ctx.env.get('HTTP_HOST','').startswith('localhost'):
          d['debug'] = True

        return render.details(**d)
    finally:#end try
      session.close()
    return web.notfound(self)#not found

  def POST(self, itembeta, page=0):
    d = LoadInfo()
    i = web.input()
    if not i.author.strip():
      d['msg'] = u'
名字必须填写!
'
      return render.msgshow(**d)
    if not i.email.strip():
      d['msg'] = u'
邮箱必须填写!
'
      return render.msgshow(**d)
    if not i.comment.strip():
      d['msg'] = u'
评论内容必须填写!
'
      return render.msgshow(**d)
    session = Session()
    try:
      itemv = session.query(Item).\
        filter(Item.pubdate < datetime.datetime.utcnow() ).\
        filter(Item.beta == itembeta).\
        first()
      ip = web.ctx.ip
      comment1 = ItemComment(i.author, i.email, i.comment, datetime.datetime.utcnow(), ip, itemv.id)
      session.add(comment1)
      session.commit()
    finally:
      session.close()
    d['msg'] = u'
保存成功!
'
    return render.msgshow(**d)

#main index
class Index:
  def GET(self, page = 1 ):
    d = LoadInfo()
    pageSize = 5
    page = int(page)
    startIndex = pageSize*(page-1)
    session = Session()
    try:
      d['itemlist'] =  session.query(Item).\
        join(Item.itemcat).\
        filter(Item.pubdate < datetime.datetime.utcnow() ).\
        filter(Item.ispass == True).\
        options(contains_eager(Item.itemcat)).\
        order_by(Item.pubdate.desc())\
        [startIndex:startIndex+pageSize]
      if page==0: page=1
      #total
      d['recodecount'] =  session.query(Item).\
        join(Item.itemcat).\
        filter(Item.pubdate < datetime.datetime.utcnow() ).\
        filter(Item.ispass == True).\
        options(contains_eager(Item.itemcat)).\
        count()
      d['pagecount'] =  int(math.ceil( d['recodecount'] / float(pageSize)));
      if d['pagecount']==0: d['pagecount']=1
      d['page'] = page
      d['itemcat'] = session.query(ItemCat).all()
    finally:
      session.close()
    #local not show stat
    if web.ctx.env.get('HTTP_HOST','').startswith('localhost'):
      d['debug'] = True
    return render.index(**d)

#Rss page
class RssIndex:
  def GET(self):
    d = LoadInfo()
    #web.header('Content-Type','text/xml; charset=utf-8', unique=True)
    #web.header('Transfer-Encoding', 'chunked')
    d['now'] = datetime.datetime.utcnow()
    session = Session()
    try:
      pageSize = 20
      d['itemlist'] = session.query(Item).\
        filter(Item.pubdate < datetime.datetime.utcnow() ).\
        filter(Item.ispass == True).\
        join(Item.itemcat).\
        options(subqueryload('itemcat')).\
        order_by(Item.pubdate.desc())\
        [:pageSize]
    finally:
      session.close()
    return render.rss(**d)

class Ico(StaticFileHandler):
  file_path = "favicon.ico"
  content_type = "image/vnd.microsoft.icon"

class AdminCatDel:
  def GET(self,catid=0):
    d = LoadInfo()
    if catid == 0:
      d['msg'] = u'
找不到该分类
'
      d['returnurl'] = u'/admin/catlist/'
      return render.msgshow(**d)
    session = Session()
    try:
      catcount =session.query(Item).\
        join(Item.itemcat).\
        filter(ItemCat.id == int(catid)).\
        options(contains_eager(Item.itemcat)).\
        count()
    finally:
      session.close()
    if catcount:
      d['msg'] = u'
分类下还有文章,不能删除!
'
      d['returnurl'] = u'/admin/catlist/'
      return render.msgshow(**d)
    else:
      #delete
      catitems = [x for x in d['catlist'] if int(x.id)==int(catid)]
      if catitems:
        session = Session()
        try:
          session.delete(catitems[0])
          session.commit()
        finally:
          session.close()
        d['msg'] = u'
删除成功
'
        d['returnurl'] = u'/admin/catlist/'
        return render.msgshow(**d)
      else:
        d['msg'] = u'
找不到该分类
'
        d['returnurl'] = u'/admin/catlist/'
        return render.msgshow(**d)

class AdminCatList:
  def GET(self, catid=0):
    d = LoadInfo()
    d['myitem'] = ""
    if d['catlist']:
      if catid>0:
        catitems = [x for x in d['catlist'] if int(x.id)==int(catid)]
        if catitems:
          d['myitem'] = catitems[0]
      return render.admin_catlist(**d)
    return web.notfound(self)

  def POST(self):
    d = LoadInfo()
    i = web.input()
    if not i.myid.strip():
      d['msg'] = u'
id必须填写!
'
      return render.msgshow(**d)
    if not i.mname.strip():
      d['msg'] = u'
名字必须填写!
'
      return render.msgshow(**d)
    if not i.mbeta.strip():
      d['msg'] = u'
beta必须填写!
'
      return render.msgshow(**d)
    if not i.morders.strip():
      d['msg'] = u'
orders必须填写!
'
      return render.msgshow(**d)
    session = Session()
    try:
      myid = int(i.myid)
      name = i.mname.strip()
      beta = i.mbeta.strip()
      orders = int(i.morders.strip())
      keyword = i.mkeyword.strip()
      description = i.mdescription.strip()
      ishide = i.mishide.strip() == 'true'
      if myid !=0:
        itemv = session.query(ItemCat).\
          filter(ItemCat.id == myid).\
          first()
        itemv.name = name
        itemv.beta = beta
        item.orders = orders
        item.keyword = keyword
        item.description = description
        item.ishide = ishide
      else:
        itemv = ItemCat(name, beta, orders, keyword, description, ishide)
      session.add(itemv)
      session.commit()
    finally:
      session.close()
    d['msg'] = u'
保存成功!
'
    d['returnurl'] = u'/admin/catlist/'
    return render.msgshow(**d)

class AdminItemList:
  def GET(self, page = 1 ):
    d = LoadInfo()
    pageSize = 20
    page = int(page)
    startIndex = pageSize*(page-1)
    session = Session()
    try:
      d['itemlist'] =  session.query(Item).\
        join(Item.itemcat).\
        options(contains_eager(Item.itemcat)).\
        order_by(Item.pubdate.desc())\
        [startIndex:startIndex+pageSize]
      if page==0: page=1
      #总数
      d['recodecount'] =  session.query(Item).\
        join(Item.itemcat).\
        options(contains_eager(Item.itemcat)).\
        count()
      d['pagecount'] =  int(math.ceil( d['recodecount'] / float(pageSize)));
      if d['pagecount']==0: d['pagecount']=1
      d['page'] = page
      d['itemcat'] = session.query(ItemCat).all()
    finally:
      session.close()
    return render.admin_itemlist(**d)

class AdminCommentList:
  def GET(self, page = 1 ):
    d = LoadInfo()
    pageSize = 20
    page = int(page)
    startIndex = pageSize*(page-1)
    session = Session()
    try:
      d['commentlist'] =  session.query(ItemComment).\
        options(contains_eager(ItemComment.item)).\
        order_by(ItemComment.adddate.desc())\
        [startIndex:startIndex+pageSize]
      if page==0: page=1
      #总数
      d['recodecount'] =  session.query(ItemComment).\
        options(contains_eager(ItemComment.item)).\
        count()
      d['pagecount'] =  int(math.ceil( d['recodecount'] / float(pageSize)));
      if d['pagecount']==0: d['pagecount']=1
      d['page'] = page
    finally:
      session.close()
    return render.admin_commentlist(**d)

class AdminItemEdit:
  def GET(self, itemid = 0 ):
    d = LoadInfo()
    session = Session()
    try:
      d['myitem'] =  session.query(Item).\
          filter(Item.id == int(itemid)).\
          first()
    finally:
      session.close()
    if not d['myitem']:
      d['myitem'] = Item('', '', '', datetime.datetime.utcnow(), datetime.datetime.utcnow(), False)
      d['myitem'].id = 0
    return render.admin_itemedit(**d)

  def POST(self, itemid = 0):
    d = LoadInfo()
    i = web.input()
    if not i.myid.strip():
      d['msg'] = u'
id必须填写!
'
      return render.msgshow(**d)
    if not i.mtitle.strip():
      d['msg'] = u'
title必须填写!
'
      return render.msgshow(**d)
    if not i.mbeta.strip():
      d['msg'] = u'
beta必须填写!
'
      return render.msgshow(**d)
    if not i.mcategory.strip():
      d['msg'] = u'
category必须填写!
'
      return render.msgshow(**d)
    session = Session()
    try:
      itemv = Item(None, None, None, None, None, None)
      if int(i.myid)!=0:
        itemv = session.query(Item).\
          filter(Item.id == int(i.myid)).\
          first()
      itemv.title = i.mtitle.strip()
      itemv.beta = i.mbeta.strip()

      catitems = [x for x in d['catlist'] if int(x.id)==int(i.mcategory)]
      if catitems:
        itemv.itemcat = catitems[0]
      else:
        itemv.itemcat = d['catlist'][0]
      fmr = '%Y-%m-%d %H:%M:%S'

      adddate = time.strptime(i.madddate.strip(), fmr)
      adddate = gmttime(datetime.datetime(*adddate[:6]))
      itemv.adddate = adddate

      pubdate = time.strptime(i.mpubdate.strip(), fmr)
      pubdate = gmttime(datetime.datetime(*pubdate[:6]))
      itemv.pubdate = pubdate
      itemv.ispass = i.mispass.strip() == 'true'
      itemv.content = i.mcontent.strip()
      session.add(itemv)
      session.commit()
    finally:
      session.close()
    if int(i.myid)==0:
      d['msg'] = u'
添加成功!
'
    else:
      d['msg'] = u'
修改保存成功!
'
    d['returnurl'] = u'/admin/itemlist/'
    return render.msgshow(**d)

class AdminCommentPass:
  def GET(self, cid=0):
    d = LoadInfo()
    if cid == 0:
      d['msg'] = u'
找不到该评论
'
      d['returnurl'] = u'/admin/commentlist/'
      return render.msgshow(**d)
    session = Session()
    #delete
    try:
      comment =  session.query(ItemComment).\
        filter(ItemComment.id == int(cid)).\
        first()
      if comment:
        comment.ispass = not comment.ispass
        session.add(comment)
        session.commit()
        d['msg'] = u'
修改成功
'
        d['returnurl'] = u'/admin/commentlist/'
      else:
        d['msg'] = u'
找不到该评论
'
        d['returnurl'] = u'/admin/commentlist/'
      return render.msgshow(**d)
    finally:
      session.close()


from hartree_fock.rhf import RHF
from quantum_systems import (
    TwoDimensionalHarmonicOscillator,
    SpatialOrbitalSystem,
)
from quantum_systems import construct_pyscf_system_ao
import numpy as np
from hartree_fock.mix import AlphaMixer, DIIS
import pytest

def test_rhf():

    molecule = "li 0.0 0.0 0.0; h 3.08 0.0 0.0"

    basis = "cc-pvdz"

    system = construct_pyscf_system_ao(
        molecule,
        basis=basis,
        np=np,
        verbose=False,
        add_spin=False,
        anti_symmetrize=False,
    )

    rhf = RHF(system, verbose=False)
    rhf.compute_ground_state(tol=1e-12, change_system_basis=False)

    e_rhf = rhf.total_energy.real
    e_rhf_2 = (
        rhf.compute_one_body_expectation_value(system.h)
        + rhf.compute_two_body_expectation_value(system.u)
        + system.nuclear_repulsion_energy
    )

    assert abs(e_rhf - e_rhf_2) < 1e-12

    dip_mom_rhf_x = rhf.compute_one_body_expectation_value(
        system.dipole_moment[0]
    ).real
    dip_mom_rhf_y = rhf.compute_one_body_expectation_value(
        system.dipole_moment[1]
    ).real
    dip_mom_rhf_z = rhf.compute_one_body_expectation_value(
        system.dipole_moment[2]
    ).real

    import pyscf

    # Build molecule in AO-basis
    mol = pyscf.gto.Mole()
    mol.verbose = 0
    mol.unit = "bohr"
    mol.build(atom=molecule, basis=basis)
    nuclear_repulsion_energy = mol.energy_nuc()

    hf = pyscf.scf.RHF(mol)
    hf.conv_tol = 1e-12
    hf.kernel()
    Cocc = hf.mo_coeff[:, : system.n]
    D = 2 * np.einsum("sj,rj->sr", Cocc.conj(), Cocc)
    dipole_integrals = -mol.intor("int1e_r").reshape(3, system.l, system.l)

    dip_mom_pyscf_x = np.trace(np.dot(D, dipole_integrals[0]))
    dip_mom_pyscf_y = np.trace(np.dot(D, dipole_integrals[1]))
    dip_mom_pyscf_z = np.trace(np.dot(D, dipole_integrals[2]))

    assert abs(dip_mom_pyscf_x - dip_mom_rhf_x) < 1e-5
    assert abs(dip_mom_pyscf_y - dip_mom_rhf_y) < 1e-5
    assert abs(dip_mom_pyscf_z - dip_mom_rhf_z) < 1e-5
    assert abs(e_rhf - hf.e_tot) < 1e-5

def test_h2o_rhf():

    r = 1.871
    molecule = f"O; H, 1, {r};  H 1 {r} 2 100.0"
    basis = "sto-3g"

    system = construct_pyscf_system_ao(
        molecule,
        basis=basis,
        np=np,
        verbose=False,
        add_spin=False,
        anti_symmetrize=False,
    )

    rhf = RHF(system, verbose=True).compute_ground_state(tol=1e-12)

    e_rhf = rhf.compute_energy().real
    e_rhf_2 = (
        rhf.compute_one_body_expectation_value(system.h)
        + rhf.compute_two_body_expectation_value(system.u)
        + system.nuclear_repulsion_energy
    ).real
    e_hf_pyscf = -74.965_900_173_175_2

    assert abs(e_rhf - e_rhf_2) < 1e-12
    assert abs(e_rhf - e_hf_pyscf) < 1e-10

def test_tdho_rhf():
    n = 2
    l = 12
    radius = 10
    num_grid_points = 401

    tdho = SpatialOrbitalSystem(
        2, TwoDimensionalHarmonicOscillator(l, radius, num_grid_points)
    )

    rhf = RHF(tdho, verbose=True).compute_ground_state(tol=1e-10)

    assert abs(rhf.compute_energy() - 3.162_691) < 1e-6


import json
import glob, os
import dash
import datetime
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
from django_plotly_dash import DjangoDash
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from Exp_Main.models import DAF
from plotly.subplots import make_subplots
from Analysis.Osz_Drop import *
from Lab_Misc import Load_Data
from Lab_Misc import General

def conv(x):
    return x.replace(',', '.').encode()

class Gen_fig():
    def __init__(self, target_id):
        entry = DAF.objects.get(id = target_id)
        self.entry = entry
        self.data = Load_Data.Load_from_Model('DAF', target_id)
        os.chdir(cwd)

    def CA_Time(self):
        fig = go.Figure()
        fig.add_trace(go.Scattergl(x=self.data['Age'], y=self.data['CA_L'],
                    mode='markers',
                    name='CA receding')
        )
        fig.add_trace(go.Scattergl(x=self.data['Age'], y=self.data['CA_R'],
                    mode='markers',
                    name='CA advancing')
        )
        fig.update_layout(  xaxis_title='Time [sec]',
                            yaxis_title='Contact angle [°]')
        return fig

    def F_Time(self):
        fig = go.Figure()
        fig.update_layout(  xaxis_title='Time [sec]',
                            yaxis_title='Force [mu N]')
        try:
            y_val = 1000*self.data['force / mN']
        except:
            try:
                y_val = self.data['deflection / mm']
                fig.update_layout(yaxis_title='Deflection [mm]')
            except:
                y_val = self.data['deflection']
                fig.update_layout(yaxis_title='Deflection [pix]')
        fig.add_trace(go.Scattergl(x=self.data['Age'], y=y_val,
                    mode='markers',
                    name='Force')
        )
        return fig

    def WL_Time(self):
        fig = go.Figure()
        fig.update_layout(  xaxis_title='Time [sec]',
                            yaxis_title='Drop Size [mm]')
        try:
            length = self.data['BI_right'] - self.data['BI_left']
            try:
                width = self.data['width / mm']
            except:
                pass
        except:
            length = self.data['contactpointright'] - self.data['contactpointleft']
            fig.update_layout(yaxis_title='Drop Size [pix]')
            try:
                width = self.data['P_width']
            except:
                pass
        fig.add_trace(go.Scattergl(x=self.data['Age'], y=length,
                    mode='markers',
                    name='Drop Length')
        )
        try:
            fig.add_trace(go.Scattergl(x=self.data['Age'], y=width,
                        mode='markers',
                        name='Drop Width')
            )
        except:
            pass
        return fig

    def CL_pos_Time(self):
        fig = go.Figure()
        fig.update_layout(  xaxis_title='Time [sec]',
                            yaxis_title='CL Position [mm]')
        try:
            left = self.data['BI_left'] - self.data['BI_left'].to_numpy()[0]
            right = self.data['BI_right'] - self.data['BI_right'].to_numpy()[0]
        except:
            left = self.data['contactpointleft'] - self.data['contactpointleft'].to_numpy()[0]
            right = self.data['contactpointright'] - self.data['contactpointright'].to_numpy()[0]
            fig.update_layout(yaxis_title='CL Position [pix]')
        fig.add_trace(go.Scattergl(x=self.data['Age'], y=left,
                    mode='markers',
                    name='CL left')
        )
        fig.add_trace(go.Scattergl(x=self.data['Age'], y=right,
                    mode='markers',
                    name='CL right')
        )
        return fig

value = 'temp'

app = DjangoDash(name='dash_plot_DAF', id='target_id')

cwd = os.getcwd()
rel_path = General.get_BasePath()

fig = fig = {
                'data': [{
                    'y': [1]
                }],
                'layout': {
                    'height': 800
                }
            }

app.layout = html.Div(children=[
    html.Div([dcc.Dropdown(id='my-dropdown1',
                                                           options=[{'label': 'Force / Time', 'value': 'F/Time'},
                                                                    {'label': 'CA / Time', 'value': 'CA/Time'},
                                                                    {'label': 'Drop Size / Time', 'value': 'WL/Time'},
                                                                    {'label': 'CL Position / Time', 'value': 'CL_pos/Time'},
                                                                   ],
                                                           value='F/Time',
                                                           className='col-md-12',
                                                          ),
                                              html.Div(id='test-output-div')
                                             ]),

    dcc.Input(id='target_id', type='hidden', value='1'),
    dcc.Graph(
        id='example-graph',
        figure=fig
    )
])

@app.expanded_callback(
    Output(component_id='example-graph', component_property='figure'),
    [Input(component_id='target_id', component_property='value'),
    Input('my-dropdown1', 'value')]
    )

def update_figure(target_id, Graph_select, *args,**kwargs):
    GenFig = Gen_fig(target_id)

    if Graph_select == 'F/Time':
        fig = GenFig.F_Time()
    elif Graph_select == 'CA/Time':
        fig = GenFig.CA_Time()
    elif Graph_select == 'WL/Time':
        fig = GenFig.WL_Time()
    elif Graph_select == 'CL_pos/Time':
        fig = GenFig.CL_pos_Time()

    return fig


from CONST import *

class Pathfinder(object):

    def __init__(self, depart, arrivee, plateau):

        self.plateau = plateau
        self.depart = depart
        self.arrivee = arrivee

        self.listeVoisin = []
        self.listeCircuit = [[self.depart]]
        self.circuit = []

    def findGoodPath(self):
        self.findAllPath(0)
        for path in self.listeCircuit:
            if self.arrivee in path:
                self.circuit = path
        return None

    def bindPath(self):
        if len(self.circuit) is None:
            return
        else:
            for cell in self.circuit:
                self.plateau.setCell(cell[1], cell[2], PATH)

    def getValideCell(self, coord):
        self.getVoisin(coord)
        caseValide = []

        for eachCase in self.listeVoisin:
            if eachCase[0] == VOID:
                caseValide.append(eachCase)

        return caseValide if len(caseValide) != 0 else None

    def findAllPath(self, id):
        """
        on verifie si il y a d'autre case dispo
            si non
                on remonte
            si oui, recursivité avec caseActuel sur chacun des cases
                on ajoute a la liste actuelle
        """

        cases = self.getValideCell(self.listeCircuit[id][-1])

        for case in cases:
            if case in self.listeCircuit[id]:
                cases.remove(case)

        # self.plateau.setCell(self.listeCircuit[id][-1][1], self.listeCircuit[id][-1][2], 2)
        # self.plateau.afficher("###", "   ", " . ")
        # self.plateau.setCell(self.listeCircuit[id][-1][1], self.listeCircuit[id][-1][2], 0)

        if len(cases) == 0:
            return
        elif len(cases) == 1:
            self.listeCircuit[id].append(cases[0])
            self.findAllPath(id)
        else:
            for embranchement in cases:
                i = len(self.listeCircuit)
                self.listeCircuit.append(self.listeCircuit[id].copy())
                self.listeCircuit[i].append(embranchement)
                self.findAllPath(i)
            return

    def getVoisin(self, coord):
        self.listeVoisin.clear()

        if coord[1] > 0:
            self.listeVoisin.append(
                (self.plateau.get((coord[1] - 1, coord[2])), coord[1] - 1, coord[2]))

        if coord[2] < self.plateau.getTailleY() - 1:
            self.listeVoisin.append(
                (self.plateau.get((coord[1], coord[2] + 1)), coord[1], coord[2] + 1))

        if coord[1] < self.plateau.getTailleX() - 1:
            self.listeVoisin.append(
                (self.plateau.get((coord[1] + 1, coord[2])), coord[1] + 1, coord[2]))

        if coord[2] > 0:
            self.listeVoisin.append(
                (self.plateau.get((coord[1], coord[2] - 1)), coord[1], coord[2] - 1))


import json

from decouple import Config
from decouple import DEFAULT_ENCODING
from decouple import RepositoryEnv
from decouple import RepositoryIni

from .extensions import ConfigByModel
from .repositories import RepositoryAWSParameterStore
from .repositories import RepositoryAWSSecrets

class CRUDConfig(Config):
    """
    CRUD Extension of python-decouple's Config class.

    Works the same as its parent method, but allows you to set, update, and delete keys.
    """

    def __iter__(self):
        return self.repository.__iter__()

    # CREATE method
    def set(self, key, value):
        if key not in self.repository:
            self.repository.set(key, value)
        else:
            raise ValueError("Error: Key already exists in the config")

    # LIST method
    def list(self):
        return self.repository.list()

    # UPDATE method
    def update(self, key, new_value):
        if key in self.repository:
            self.repository.set(key, new_value)
        else:
            raise KeyError("Error: There is no such key")

    # DELETE method
    def delete(self, key):
        if key in self.repository:
            del self.repository[key]
        else:
            raise KeyError("Error: There is no such key")

class CRUDConfigByModel(ConfigByModel, CRUDConfig):
    """Works the same as CRUDConfig but casts the values to the model."""

class CRUDBaseRepositoryMixin:
    def list(self):
        return list(self.data.keys())

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

    def __delitem__(self, __name: str) -> None:
        return self.delete(__name)

class CRUDRepositoryEnv(RepositoryEnv, CRUDBaseRepositoryMixin):
    """
    CRUD extension of python-decouple's RepositoryEnv class.

    Works the same as its parent method, but allows you to set, update, and delete keys.
    """

    def __init__(self, source, encoding=DEFAULT_ENCODING):
        self.source = source
        self.encoding = encoding
        super().__init__(source, encoding)

    def set(self, key, value):
        if not isinstance(value, str):
            try:
                value = json.dumps(value)
            except TypeError:
                raise TypeError("Error: Value must be a string or a JSON serializable object")

        self.data[key] = value
        with open(self.source, "a", encoding=self.encoding) as file_:
            file_.write(f"{key}={value}\n")

    def delete(self, key):
        if key in self.data:
            del self.data[key]
            # This will rewrite the entire file without the deleted key
            with open(self.source, "w", encoding=self.encoding) as file_:
                for k, v in self.data.items():
                    file_.write(f"{k}={v}\n")

class CRUDRepositoryIni(RepositoryIni, CRUDBaseRepositoryMixin):
    """
    CRUD extension of python-decouple's RepositoryIni class.

    Works the same as its parent method, but allows you to set, update, and delete keys.
    """

    def __init__(self, source, encoding=DEFAULT_ENCODING):
        self.source = source
        self.encoding = encoding
        super().__init__(source, encoding)

    def list(self):
        if self.parser.has_section(self.SECTION):
            return self.parser.options(self.SECTION)
        else:
            return []

    def set(self, key, value):
        if not isinstance(value, str):
            try:
                value = json.dumps(value)
            except TypeError:
                raise TypeError("Error: Value must be a string or a JSON serializable object")

        self.parser.set(self.SECTION, key, value)
        with open(self.source, "w", encoding=self.encoding) as file_:
            self.parser.write(file_)

    def delete(self, key):
        if self.parser.has_option(self.SECTION, key):
            self.parser.remove_option(self.SECTION, key)
            with open(self.source, "w", encoding=self.encoding) as file_:
                self.parser.write(file_)

class CRUDBaseAWSRepositoryMixin:
    def set(self, key, value):
        # Check value
        if not isinstance(value, str):
            try:
                value = json.dumps(value)
            except TypeError:
                raise TypeError("Error: Value must be a string or a JSON serializable object")

        self.data[key] = value
        self._save_data()

    def delete(self, key):
        if key in self.data:
            del self.data[key]
            self._save_data()

class CRUDRepositoryAWSSecrets(RepositoryAWSSecrets, CRUDBaseAWSRepositoryMixin, CRUDBaseRepositoryMixin):
    """
    CRUD extension of our own RepositoryAWSSecrets class.

    Works the same as its parent method, but allows you to set, update, and delete keys.
    """

    def _save_data(self):
        self.client.put_secret_value(SecretId=self.secret_name, SecretString=json.dumps(self.data))

class CRUDRepositoryAWSParameterStore(RepositoryAWSParameterStore, CRUDBaseAWSRepositoryMixin, CRUDBaseRepositoryMixin):
    """
    CRUD extension of our own RepositoryAWSParameterStore class.

    Works the same as its parent method, but allows you to set, update, and delete keys.
    """

    def _save_data(self):
        self.client.put_parameter(Name=self.parameter_store_name, Value=json.dumps(self.data), Type="SecureString", Overwrite=True)


import random
from string import ascii_lowercase

def table_from_corpus():
    return [8.1, 1.5, 2.8, 4.2, 12.7, 2.2, 2.0, 6.1, 7.0,
            0.2, 0.8, 4.0, 2.4, 6.7, 7.5, 1.9, 0.1, 6.0,
            6.3, 9.0, 2.8, 1.0, 2.4, 0.2, 2.0, 0.1]

def encode(n, s):
    # this function takes as input n, the shift factor and
    # plain text s and returns the encoded string.
    list = ''
    for letter in s:
        if letter not in ascii_lowercase:
            list += letter
        else:
            list += chr(((ord(letter) - ord('a') + n) % 26) + ord('a'))
    return (list)

def count(c, s): # part of frequencies function
    ## This functions takes as input a character c and a string s and returns the
    ## the number of times c appears in s
    counter = 0
    for letter in s:
        if letter == c:
            counter += 1
    return counter

def num_lower_case(s): # part of frequencies function
    ## This function takes as input a string s and returns the
    ## number of lower-case letters in s
    counter = 0
    for letter in s:
        if letter in ascii_lowercase:
            counter += 1
    return counter

def frequencies(s):
    ## This function takes as input a string s and returns the
    ## frequency list for s, containing 26 percentages, one for
    ## each lower-case letter. The percentage should be taken
    ## over the number of lower-case letters in s.
    xs = []
    total_letters = num_lower_case(s)
    for letter in ascii_lowercase:
        count1 = count(letter, s)
        percentage = 100* count1/total_letters
        xs.append (percentage)
    return xs

def chisqr(os, es):
    ## returns the chi-square statistic for os and es
    chi_statistic = 0
    counter = 0
    for items in os:
        estimated_list_position = es[counter]
        chi_statistic += ((items - estimated_list_position) ** 2)/estimated_list_position
        counter += 1
    return chi_statistic

def rotate(n, xs):
    ## rotates list xs by n positions
    # might have to create a new list variable xs
    xs = xs[-n:] + xs[:-n]
    return xs

def chisquare_statistic(xs, es):
    ## This computes 26 ch-square statistics in a list, one for each
    ## list obtained by rotating os by n = 0 to 25.
    frequency_list = []
    for x in range(0,26):
        frequency_list.append(chisqr(rotate(x,xs),es))
    return frequency_list

def crack(s):
    # This function takes as input a coded text and returns the plain text
    # for the coded text
    frequency_list = chisquare_statistic(frequencies(s),table_from_corpus())
    match = min(frequency_list)
    shift_factor = frequency_list.index(match)

    return encode(shift_factor, s)

def main():
    while True:
        plain = input("\nEnter plain text: ")
        if plain == "exit":
            break
        shift = random.randint(2, 25)
        cipher = encode(shift, plain)
        print("Coded text: ", cipher)
        recovered = crack(cipher)
        print("Cracked text: ", recovered)

main()


"""
Assignment: Fraction calculator
Description: An calculator that takes 2 fraction in form of numerator and denominator as an input along with
the operation to be performed from the user and calculates the result of the operation
Name: Harish Balasubramanian
CWID: 10474753"""

# A Global value used to store list of allowed operations
allowed_operation: str = ['+', '-', '*', '/', '==']

class Fractions:
    """Fraction class containing of methods used to operate on the input fraction"""

    def __init__(self, numerator: int, denominator: int) -> str:

    # Check if denominator is 0 and throw an error (Since denominator of a fraction cannot be 0)
        if denominator == 0:
            raise ValueError("Denominator of the fraction cannot be 0.")
        self.numerator = numerator
        self.denominator = denominator

    def plus(self, other: "Fractions") -> "Fractions":
        """Method used to add two fraction and return the resultant value"""

        new_numerator: int = (self.numerator * other.denominator) + (self.denominator * other.numerator)
        new_denominator: int = self.denominator * other.denominator
        return Fractions(new_numerator, new_denominator)

    def minus(self, other: "Fractions") -> "Fractions":
        """Method used to subtract two fraction and return the resultant value"""

        new_numerator: int = (self.numerator * other.denominator) - (self.denominator * other.numerator)
        new_denominator: int = self.denominator * other.denominator
        return Fractions(new_numerator, new_denominator)

    def times(self, other: "Fractions") -> "Fractions":
        """Method used to multiply two fraction and return the resultant value"""
        new_numerator: int = self.numerator * other.numerator
        new_denominator: int = self.denominator * other.denominator
        return Fractions(new_numerator, new_denominator)

    def divide(self, other: "Fractions") -> "Fractions":
        """Method used to divide two fraction and return the resultant value"""
        new_numerator: int = self.numerator * other.denominator
        new_denominator: int = self.denominator * other.numerator
        return Fractions(new_numerator, new_denominator)

    def equal(self, other: "Fractions") -> bool:
        """Method used to check if two fractions are equal or not and return the resultant value"""
        return self.numerator * other.denominator == self.denominator * other.numerator

    def __str__(self) -> str:

        return f"{self.numerator}/{self.denominator}"

def handle_input(prompt) -> int:
    """Function used to handle input and check if its a int"""

    while True:
        user_input = input(prompt)
        try:
            return int(user_input)
        except ValueError:
            print(f"{user_input} is not a number. please enter the number again.")

def get_fraction_from_user(fraction_label: str) -> "Fractions":
    """Function used to take input from user and convert to a fraction"""

    while True:
        numerator = handle_input(f"Enter {fraction_label} numerator: ")
        denominator = handle_input(f"Enter {fraction_label} denominator: ")
        try:
            fraction = Fractions(numerator, denominator)
            return fraction
        except ValueError as e:
            print(e)

def compute(f1, operator, f2) -> "Fractions":
    """Function used to compute the result of operation performed on fractions"""

    result: "Fractions" = None
    try:
        if operator == '+':
            result = f1.plus(f2)

        elif operator == '-':
            result = f1.minus(f2)

        elif operator == '*':
            result = f1.times(f2)

        elif operator == '/':
            result = f1.divide(f2)

        elif operator == '==':
            result = f1.equal(f2)

        else:
            print('Invalid operation!')

    except ValueError as e:
        print(e)
        return result

def main():
    """Function used for interactive fraction calculation"""
    print("---- Interactive ---- \n Welcome to the fraction calculator! \n ")

    fraction_1: "Fractions" = get_fraction_from_user("fraction 1")

    while True:
        try:
            operation_selected = input(f"Select an operation to be performed {allowed_operation}:")
            allowed_operation.index(operation_selected)

        except ValueError:
            print(f"{operation_selected} is an invalid operator!")

        else:
            break

    fraction_2: "Fractions" = get_fraction_from_user("fraction 2")

    result: "Fractions" = compute(fraction_1, operation_selected, fraction_2)

    if result:
        print(f"The result is => {fraction_1} {operation_selected} {fraction_2} = {result}")

def test_suite():
    """Function used as a test case"""

    print("-----TEST SUITE 1 ----- \n Welcome to the fraction calculator! \n ")

    fraction_1: "Fractions" = Fractions(1, 2)
    fraction_2: "Fractions" = Fractions(4, 4)
    fraction_3: "Fractions" = Fractions(12, 8)
    fraction_4: "Fractions" = Fractions(3, 2)
    fraction_5: "Fractions" = Fractions(1, 1)

    result1: "Fractions" = compute(fraction_1, allowed_operation[0], fraction_1)
    if result1:
        print(f"Expected result 1/2 + 1/2 = 4/4")
        print(f"Computed result {fraction_1} {allowed_operation[0]} {fraction_1} = {result1} \n")

    result2: "Fractions" = compute(fraction_2, allowed_operation[1], fraction_1)
    if result2:
        print(f"Expected result 4/4 - 1/2 = 4/8")
        print(f"Computed result {fraction_2} {allowed_operation[1]} {fraction_1} = {result2} \n")

    result3: "Fractions" = compute(fraction_1, allowed_operation[0], fraction_2)
    if result3:
        print(f"Expected result 1/2 + 4/4 = 12/8")
        print(f"Computed result {fraction_1} {allowed_operation[0]} {fraction_2} = {result3} \n")

    result4: "Fractions" = compute(fraction_3, allowed_operation[4], fraction_4)
    if result4:
        print(f"Expected result 12/8 == 3/2 = True")
        print(f"Computed result {fraction_3} {allowed_operation[4]} {fraction_4} = {result4} \n")

    result5: "Fractions" = compute(fraction_1, allowed_operation[2], fraction_4)
    if result5:
        print(f"Expected result 1/2 * 3/2 = 3/4")
        print(f"Computed result {fraction_1} {allowed_operation[2]} {fraction_4} = {result5} \n")

    result6: "Fractions" = compute(fraction_2, allowed_operation[3], fraction_2)
    if result6:
        print(f"Expected result 4/4 / 4/4 = 16/16")
        print(f"Computed result {fraction_2} {allowed_operation[3]} {fraction_2} = {result6} \n")
        print(f"Expected result 1/2 / 0/1  Error")

    result7: "Fractions" = compute(fraction_1, allowed_operation[3], fraction_5)
    if result7:
        print(f"Computed result {fraction_1} {allowed_operation[3]} {fraction_5} = {result7} \n")

def test_suite_2():
    """Function used as a test case for adding 3 fractions"""

    print("-----TEST SUITE 2 ----- \n Welcome to the fraction calculator! \n")

    fraction_1: "Fractions" = Fractions(1, 2)
    fraction_2: "Fractions" = Fractions(3, 4)
    fraction_3: "Fractions" = Fractions(4, 4)
    result1: "Fractions" = compute(fraction_1, allowed_operation[0], fraction_2)
    result2: "Fractions" = compute(result1, allowed_operation[0], fraction_3)

    if result2:
        print(f"Expected result 1/2 + 3/4 + 4/4 = 72/32")
        print(f"Computed result {fraction_1} {allowed_operation[0]} {fraction_2} {allowed_operation[0]} {fraction_3} = {result2} \n")

if __name__ == '__main__':
    test_suite()
    #test_suite_2()
    main()


from os.path import join
from glob import glob

import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
    name="xsInterface",
    version="0.3.2",
    author="Dan Kotlyar",
    author_email="dan.kotlyar@me.gatech.edu",
    description="Cross Section Interface for multi-group XS manipulation ",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/CORE-GATECH-GROUP/xs-interfaces",
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires='>=3.6',
    packages=['xsInterface',
              'xsInterface.containers', 'xsInterface.debug',
              'xsInterface.errors', 'xsInterface.examples',
              'xsInterface.functions', 'xsInterface.inputsets',
              'xsInterface.inputsets', 'xsInterface.otemplates',
              'xsInterface.tests', 'xsInterface.workshops' ],
)


# -*- coding: gb18030 -*-
#
# $Id: RedeemPanel.py,v 1.10 2008-08-19 06:11:20 fangpengjun Exp $
from guis import *
from guis.controls.Control import Control
from guis.controls.TabCtrl import TabPanel
from guis.controls.Button import Button
from WareItem import WareItem
from BuyPanel import ItemInfo
from LabelGather import labelGather
import GUIFacade

class RedeemPanel( TabPanel ):
	"""
	赎回物品界面
	"""

	_item_dragMark = DragMark.NPC_TRADE_REDEEM

	def __init__( self, itemsPanel = None ):
		TabPanel.__init__( self, itemsPanel )
		self.__pyItems = {}
		self.dropFocus = True
		self.__RedeemItems = [] #记录赎回物品列表
		self.__initItemsPanel( itemsPanel )
		self.__triggers = {}
		self.__registerTriggers()

	def __initItemsPanel( self, itemsPanel ):
		self.__pyRedeemBtn = Button( itemsPanel.redeemBtn )
		self.__pyRedeemBtn.setStatesMapping( UIState.MODE_R4C1 )
		self.__pyRedeemBtn.onLClick.bind( self.__onRedeem )

		# ---------------------------------------------
		# 设置标签
		# ---------------------------------------------
		labelGather.setPyBgLabel( self.__pyRedeemBtn, "TradeWindow:redeemPanel", "btnRedeem" ) # 购回

		self.__initItems( itemsPanel )

	def __initItems( self, itemsPanel ):
		for name, item in itemsPanel.children:
			if "item_" not in name : continue
			index = int( name.split( "_" )[1] )
			pyItem = WareItem( item, self._item_dragMark, self )
			pyItem.index = index
			self.__pyItems[index] = pyItem
	# -----------------------------------------------------------
	# private
	# -----------------------------------------------------------
	def __registerTriggers( self ) :
		self.__triggers["EVT_ON_REDEEM_INFO_CHANGED"] = self.__onRedeemChange
		self.__triggers["EVT_ON_REDEEM_REEDEM_ITEM"] =  self.__onRedeemItem
		for key in self.__triggers :
			GUIFacade.registerEvent( key, self )

	def __deregisterTriggers( self ) :
		for key in self.__triggers.iterkeys() :
			GUIFacade.registerEvent( key, self )
	# -------------------------------------------------------
	def __onRedeem( self ):# 赎回
		for pyItem in self.__pyItems.itervalues():
			if pyItem.selected:
				GUIFacade.redeemItem( pyItem.uid )

	def __onRedeemChange( self, index, itemInfo ):
		if self.__pyItems.has_key( index ):
			itemInfo = ItemInfo( itemInfo, index )
			self.__pyItems[index].update( itemInfo  )

	def __onRedeemItem( self, uid, itemInfo ):
		itemList = []
		for index, item in self.__pyItems.iteritems():
			if item.uid == uid:
				if not itemInfo:
					item.selected = False
				itemInfo = ItemInfo( itemInfo, index )
				self.__pyItems[index].update( itemInfo )
			if item.getObjectItem().itemInfo:
				itemList.append( item.getObjectItem() )
		while len( itemList ) < 7:
			itemList.append( None )

		for index, item in self.__pyItems.iteritems():
			item.selected = False
			if itemList[index]:
				itemInfo = ItemInfo( itemList[index].itemInfo, index )
				self.__pyItems[index].update( itemInfo )
			else:
				itemInfo = ItemInfo( None, index )
				self.__pyItems[index].update( itemInfo )

	def __onReSetItem( self, itemInfo ):
		"""
		进入到这个函数时，说明赎回物品界面已在此前被清空
		"""
		for index, item in self.__pyItems.iteritems():
			if item.uid < 0 or item.uid == itemInfo.uid:
				itemInfo = ItemInfo( itemInfo, index )
				self.__pyItems[index].update( itemInfo )
				break

	# --------------------------------------------------------
	# public
	# --------------------------------------------------------
	def onEvent( self, eventMacro, *args ):
		self.__triggers[eventMacro]( *args )

	def reSetItem( self, itemInfo ):
		self.__onReSetItem( itemInfo )

	def clearItems( self ):
		for index, item in self.__pyItems.iteritems():
			itemInfo = ItemInfo( None, index )
			item.update( itemInfo )

	def addRedeemItem( self, pyItem ):
		if pyItem not in self.__RedeemItems:
			self.__RedeemItems.append( pyItem )

	def areItemsEmpty( self ):
		"""
		判断面板中是否还有可赎回物品
		"""
		for index, item in self.__pyItems.iteritems():
			if item.uid >= 0:
				return False
		return True

	def delRedeemItem( self, pyItem ):
		if pyItem in self.__RedeemItems:
			self.__RedeemItems.remove( pyItem )

	def initePanelBorder( self ):
		parentGui = self.pyTopParent.getGui()
		rt = parentGui.elements["frm_rt"]
		r = parentGui.elements["frm_r"]
		rb = parentGui.elements["frm_rb"]
		rTop = rt.position.y + rt.size.y
		r.position.y = rTop - 1.0
		r.size.y = rb.position.y - rTop


from sklearn.datasets import load_iris
import numpy as np

def train_test():

    # load the data
    iris = load_iris()
    X = iris.data[:, 0:4]  # X for the set of features
    y = iris.target  # for the set of labels
    print(np.unique(y))  # to print the labels

    # One Hot Encode Y: necessary for classification. It seems that this step encode the labels (1, 2, 3)
    # into binary coding
    from sklearn.preprocessing import LabelBinarizer
    encoder = LabelBinarizer()
    Y = encoder.fit_transform(y)

    # train the network
    neural_network = create_network()
    neural_network.fit(X, Y, epochs=500, batch_size=10)

    # test the model
    np.set_printoptions(suppress=True)
    predictions = neural_network.predict(X[0:10], batch_size=32, verbose=0)
    print(predictions)

def create_network():
    """
    This function defines the structure of the neural network
    :return:
    """
    from keras.models import Sequential
    from keras.layers import Dense
    from keras.optimizers import SGD

    model = Sequential()
    model.add(Dense(5, input_shape=(4, ), activation='relu'))
    model.add(Dense(3, activation='softmax'))

    # stochastic gradient descent
    sgd = SGD(lr=0.001, decay=1e-6, momentum=0.9, nesterov=True)
    model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])

    return model

def main():
    train_test()

if __name__ == '__main__':
    main()


# Write your code here
import random
import sqlite3

class BankAccount:
    def __init__(self, *args, **kwargs):
        if args:
            self.account_number = args[0][1]
            self.pin = args[0][2]
            self.balance = args[0][3]
        elif kwargs:
            print(kwargs)
        else:
            temp_account = "400000" + "{:09d}".format(random.randint(0, 999999999))
            all_n = 0.0
            for i in range(len(temp_account)):
                if (i + 1) % 2 == 1:
                    temp = int(temp_account[i])*2
                else:
                    temp = int(temp_account[i])
                if temp > 9:
                    temp -= 9
                all_n += temp
            check_sum = 10 - (all_n % 10)
            if check_sum == 10:
                check_sum -= 10

            self.account_number = temp_account + "{:01d}".format(int(check_sum))
            self.pin = "{:04d}".format(random.randint(0, 9999))
            self.balance = 0

    def get_account_number(self):
        return self.account_number

    def get_pin(self):
        return self.pin

    def get_balance(self):
        return self.balance

    def add_income(self, income):
        self.balance += income

    def transfer_out(self, amount):
        if amount > self.balance:
            return False
        else:
            self.balance -= amount
            return True

def luhn_algorithm(card_number):
    all_n = 0
    for i in range(len(card_number)):
        if (i + 1) % 2 == 1:
            temp = int(card_number[i]) * 2
        else:
            temp = int(card_number[i])
        if temp > 9:
            temp -= 9
        all_n += temp
    if all_n % 10 == 0:
        return True
    else:
        return False

random.seed()
accounts = dict()

conn = sqlite3.connect('card.s3db')
cur = conn.cursor()

cur.execute('''CREATE TABLE IF NOT EXISTS card(
    id INTEGER,
    number TEXT,
    pin TEXT,
    balance INTEGER DEFAULT 0)''')
conn.commit()

running = True
while running:
    print("1. Create an account")
    print("2. Log into account")
    print("0. Exit")
    user_input = int(input())
    if user_input == 1:
        unique_account = False
        while not unique_account:
            account = BankAccount()
            cur.execute('''
            SELECT EXISTS(
                SELECT * FROM card
                WHERE
                number = {account_n}
                )'''.format(account_n=account.get_account_number()))
            status = cur.fetchone()[0]
            if not status:
                accounts[account.get_account_number()] = account
                print("Your card has been created")
                print("Your card number:")
                print(account.get_account_number())
                print("Your card PIN:")
                print(account.get_pin())
                unique_account = True
                cur.execute('''
                SELECT MAX(id) FROM card''')
                entries = cur.fetchone()[0]
                if not entries:
                    print(entries)
                    entries = 0
                cur.execute('''
                INSERT INTO card
                (id, number, pin, balance)
                VALUES
                ({0},{1},{2},{3});
                '''.format(entries+1, account.get_account_number(), account.get_pin(), 0))
                conn.commit()
    elif user_input == 2:
        print("Enter your card number:")
        user_account_n = input()
        print("Enter your PIN:")
        user_pin = input()
        #  Search thorough the database for the account
        cur.execute('''
        SELECT id, number, pin, balance
        FROM card
        WHERE
        number='{account_in}' AND
        pin='{pin_in}' '''.format(account_in=user_account_n, pin_in=user_pin))
        account_out = cur.fetchone()
        if not account_out:
            print("Wrong card number or pin")
        else:
            account = BankAccount(account_out)
            print("You have logged in successfully")
            logged_in = True
            while logged_in:
                print("1. Balance")
                print("2. Add income")
                print("3. Do transfer")
                print("4. Close account")
                print("5. Log out")
                print("0. Exit")
                user_input = int(input())
                if user_input == 1:
                    print(account.get_balance())
                elif user_input == 2:
                    print("Enter income:")
                    user_input = int(input())
                    account.add_income(user_input)
                    cur.execute('''UPDATE card
                    SET
                    balance={new_balance}
                    WHERE
                    number='{account_in}'
                    AND pin='{pin_in}'
                    '''.format(account_in=account.get_account_number(), new_balance=int(account.get_balance()),
                               pin_in=account.get_pin()))
                    conn.commit()
                    print("Income added")
                elif user_input == 3:
                    print("Transfer")
                    print("Enter card number")
                    account_in = input()
                    if luhn_algorithm(account_in):
                        if account_in == account.get_account_number():
                            print("You can't transfer money to the same account!")
                        else:
                            cur.execute('''
                            SELECT EXISTS(
                            SELECT * FROM
                            card
                            WHERE
                            number = {transfer_account}
                            )'''.format(transfer_account=account_in))
                            status = cur.fetchone()[0]
                            if status:
                                print("Enter how much money you want to transfer:")
                                transfer_amount = int(input())
                                if account.transfer_out(transfer_amount):
                                    cur.execute('''
                                    SELECT balance FROM card
                                    WHERE
                                    number = {transfer_account}'''.format(transfer_account=account_in))
                                    temp_balance = cur.fetchone()[0]
                                    temp_balance += transfer_amount
                                    cur.execute('''
                                    UPDATE card
                                    SET balance = {new_balance}
                                    WHERE
                                    number = {transfer_account}'''.format(new_balance=temp_balance,
                                                                          transfer_account=account_in))
                                    conn.commit()
                                    cur.execute('''UPDATE card
                                    SET
                                    balance={new_balance}
                                    WHERE
                                    number='{account_in}'
                                    AND pin='{pin_in}'
                                    '''.format(account_in=account.get_account_number(),
                                               new_balance=int(account.get_balance()),
                                               pin_in=account.get_pin()))
                                    conn.commit()
                                    print("Success!")
                                else:
                                    print("Not enough money!")
                            else:
                                print("Such a card does not exist")
                    else:
                        print("Probably you made a mistake in the card number. Please try again!")
                elif user_input == 4:
                    cur.execute('''
                    DELETE FROM card
                    WHERE
                    number = {delete_account}'''.format(delete_account=account.get_account_number()))
                    print("The account has been closed.")
                    conn.commit()
                    logged_in = False
                elif user_input == 5:
                    logged_in = False
                elif user_input == 0:
                    logged_in = False
                    running = False
    elif user_input == 0:
        running = False
print("Bye!")
conn.close()