import argparse
import numpy as np
import scipy.stats
from statsmodels.stats.proportion import *
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
from matplotlib.patches import Patch
import matplotlib.patches as mpatches
matplotlib.rcParams['font.family'] = 'Arial'

def get_conf_int_stats(obs_count, total_count, method='jeffreys'):
    pref_value = obs_count/total_count
    ci_lower, ci_upper = proportion_confint(obs_count, total_count, alpha=0.05, method=method)
    return pref_value, [ci_lower, ci_upper]

def plot_rs_by_test_suite_grid_5_by_6(rs, models, human_data, test_names, model2run_indice, model2color, model2name, add_test_name=True, savepath=None):
    # Plot results as bar graph grid 5*6
    n_row = 5
    n_col = 6

    bar_width = 0.75

    fig, axs = plt.subplots(n_row, n_col, figsize=(8, 6.5), sharey='row', sharex='col')

    plt.subplots_adjust(wspace=0.4, hspace=0.4)

    for k, test_name in enumerate(test_names):

        row_id = k // n_col
        col_id = k % n_col

        axs[row_id, col_id].set_title('Test {}'.format(k+1), fontsize=12)
        axs[row_id, col_id].set_ylim(0,1)
        axs[row_id, col_id].set_xlim(-1.75,len(models)-0.25)
        axs[row_id, col_id].set_xticks(np.arange(0, len(models)))
        axs[row_id, col_id].set_yticks(np.arange(0, 1.2, 0.25))
        axs[row_id, col_id].set_xticklabels([])
        axs[row_id, col_id].spines['right'].set_visible(False)
        axs[row_id, col_id].spines['top'].set_visible(False)
        axs[row_id, col_id].grid(linestyle='--', alpha=0.5, zorder=0, axis='y')
        axs[row_id, col_id].set_axisbelow(True)

        axs[row_id, col_id].errorbar(-1, human_data[test_name]['acc_value'], yerr=[[human_data[test_name]['acc_value'] - human_data[test_name]['acc_lower']], [human_data[test_name]['acc_upper'] - human_data[test_name]['acc_value']]], label='Human', color='black', marker='None', linestyle='none')
        axs[row_id, col_id].bar(-1, human_data[test_name]['acc_value'], label='Human', width=bar_width, color='white', edgecolor='k')

        for i, model in enumerate(models):
            data = np.array([rs[model][run_index][test_name]['item_acc_list'] for run_index in model2run_indice[model]], dtype='float')
            score_averaged_across_run = np.mean(data, axis=0)
            y_mean = np.mean(score_averaged_across_run)
            yerr = 1.96*(np.std(score_averaged_across_run)/np.sqrt(len(score_averaged_across_run)))
            axs[row_id, col_id].bar(i, y_mean, label=model, width=bar_width, color=model2color[model], yerr=yerr)

    for index in range(k+1, n_row*n_col):
        row_id = index // n_col
        col_id = index % n_col
        axs[row_id, col_id].set_axis_off()

    ax = axs[4, 5]
    ax.bar(0, 0, label='Human', width=0.35, color='black', fill=False)
    for i, model in enumerate(models):
        ax.bar(i+1, 0, label=model2name[model], width=0.35, color=model2color[model])
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.spines['left'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.legend(loc = 'center', bbox_to_anchor=(-1.2, 0.5), ncol=2, fontsize=12)

    fig.text(0.06, 0.5, 'Test Accuracy Score', ha='center', va='center', rotation='vertical')

    if add_test_name:
        textstr = '\n'.join(['({}) {}'.format(k+1, test_name2pretty_name[test_name]) for k, test_name in enumerate(test_names)])
        props = dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.5, ec='lightgray')
        fig.text(0.94, 0.5, textstr, fontsize=10,
                verticalalignment='center', bbox=props, linespacing = 1.65)

    if savepath is not None:
        plt.savefig(savepath, bbox_inches='tight')

    plt.show(block=False)
    plt.pause(1)
    plt.close()

def plot_rs_by_test_suite_grid_3_by_9(rs, models, human_data, test_names, model2run_indice, model2color, model2name, savepath=None):
    # Plot results as bar graph grid 3*9
    n_row = 3
    n_col = 9

    bar_width = 0.75

    fig, axs = plt.subplots(n_row, n_col, figsize=(11, 3.6), sharey='row', sharex='col')

    plt.subplots_adjust(wspace=0.4, hspace=0.4)

    for k, test_name in enumerate(test_names):

        row_id = k // n_col
        col_id = k % n_col

        axs[row_id, col_id].set_title('Test {}'.format(k+1), fontsize=10)
        axs[row_id, col_id].set_ylim(0,1)
        axs[row_id, col_id].set_xlim(-1.75,len(models)-0.25)
        axs[row_id, col_id].spines['right'].set_visible(False)
        axs[row_id, col_id].spines['top'].set_visible(False)
        axs[row_id, col_id].grid(linestyle='--', alpha=0.5, zorder=0, axis='y')
        axs[row_id, col_id].set_xticks(np.arange(0, len(models)))
        axs[row_id, col_id].set_yticks(np.arange(0, 1.2, 0.25))
        axs[row_id, col_id].set_xticklabels([])
        axs[row_id, col_id].set_axisbelow(True)

        axs[row_id, col_id].errorbar(-1, human_data[test_name]['acc_value'], yerr=[[human_data[test_name]['acc_value'] - human_data[test_name]['acc_lower']], [human_data[test_name]['acc_upper'] - human_data[test_name]['acc_value']]], color='black', marker='None', linestyle='none')
        axs[row_id, col_id].bar(-1, human_data[test_name]['acc_value'], label='Human', width=bar_width, color='white', edgecolor='k')

        for i, model in enumerate(models):
            data = np.array([rs[model][run_index][test_name]['item_acc_list'] for run_index in model2run_indice[model]], dtype='float')
            score_averaged_across_run = np.mean(data, axis=0)
            y_mean = np.mean(score_averaged_across_run)
            yerr = 1.96*(np.std(score_averaged_across_run)/np.sqrt(len(score_averaged_across_run)))

            # bar plot
            axs[row_id, col_id].bar(i, y_mean, label=model2name[model], width=bar_width, color=model2color[model], yerr=yerr)

        if k == 22:
            axs[row_id, col_id].legend(loc='center', bbox_to_anchor=(0.5, -0.35), ncol=5, fontsize=10)

    for index in range(k+1, n_row*n_col):
        row_id = index // n_col
        col_id = index % n_col
        axs[row_id, col_id].set_axis_off()

    fig.text(0.08, 0.5, 'Test Accuracy Score', ha='center', va='center', rotation='vertical')

    if savepath is not None:
        plt.savefig(savepath, bbox_inches='tight')

    plt.show(block=False)
    plt.pause(1)
    plt.close()

def plot_aggregated_rs(rs, models, human_data, test_names, model2run_indice, model2color, model2name, savepath=None):
    # Plot averaged performance over all the test suites
    plt.figure(figsize=(2.5,2.5))
    ax = plt.gca()
    bar_width = 0.75

    # Use asymptotic confidence interval
    human_acc_by_test_suite = [human_data[test_name]['acc_value'] for test_name in test_names]
    human_acc_mean = np.mean(human_acc_by_test_suite)
    yerr = 1.96*(np.std(human_acc_by_test_suite)/np.sqrt(len(human_acc_by_test_suite)))
    ax.bar(-1, human_acc_mean, label='Human', width=bar_width, color='black', fill=False, yerr=yerr)
    print('Human average acc: {}'.format(human_acc_mean))

    for i, model in enumerate(models):
        data = [[rs[model][run_index][test_name]['acc'] for test_name in test_names] for run_index in model2run_indice[model]]
        test_suite_acc_list_averaged_across_run = np.mean(data, axis=0)
        mean_test_suite_acc = np.mean(test_suite_acc_list_averaged_across_run)
        yerr = 1.96*(np.std(test_suite_acc_list_averaged_across_run)/np.sqrt(len(test_suite_acc_list_averaged_across_run)))
        ax.bar(i, mean_test_suite_acc, label=model2name[model], width=bar_width, color=model2color[model], yerr=yerr)

    ax.set_ylim(0,1)
    ax.set_xlim(-1.75,len(models)-0.25)
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.set_xticks(np.arange(-1, len(models)))
    ax.set_yticks(np.arange(0, 1.2, 0.25))
    ax.set_xticklabels([])

    plt.ylabel('Accuracy Score')
    plt.legend(loc = 'center', bbox_to_anchor=(1.45, 0.5))

    if savepath is not None:
        plt.savefig(savepath, bbox_inches='tight')

    plt.show(block=False)
    plt.pause(1)
    plt.close()

def plot_summmary_across_model_conditions(exp_data_all, model_conditions, savepath=None):
    fig = plt.figure(constrained_layout=False, figsize=(7.2,2.4))

    hatch_style_list = [{'hatch':None}, {'hatch':'///'}, {'hatch':'.'}]
    model_condition2style = dict(zip(['finetune', 'nyt_from_scratch', 'bllip_from_scratch'], hatch_style_list))

    gs = fig.add_gridspec(nrows=1, ncols=4, width_ratios=[0.25, 0.8, 0.8, 0.8], wspace=0.1)
    bar_width = 0.75

    ax = fig.add_subplot(gs[0])
    human_acc_by_test_suite = [human_data[test_name]['acc_value'] for test_name in test_names]
    human_acc_mean = np.mean(human_acc_by_test_suite)
    yerr = 1.96*(np.std(human_acc_by_test_suite)/np.sqrt(len(human_acc_by_test_suite)))
    ax.bar(0, human_acc_mean, label='Human', width=bar_width, color='black', fill=False, yerr=yerr)
    print('Human average acc: {}'.format(human_acc_mean))
    ax.set_ylim(0,1)
    ax.set_xlim(-0.75,0.75)
    ax.set_yticks(np.arange(0, 1.2, 0.25))
    ax.set_ylabel('Accuracy', fontsize=10)
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.set_xticks([])
    ax.set_xticklabels([])

    for model_cond_idx, model_condition in enumerate(model_conditions):
        ax = fig.add_subplot(gs[model_cond_idx+1])
        rs, models, model2run_indice, model2name, model2color = exp_data_all[model_condition]

        for i, model in enumerate(models):
            data = [[rs[model][run_index][test_name]['acc'] for test_name in test_names] for run_index in model2run_indice[model]]
            test_suite_acc_list_averaged_across_run = np.mean(data, axis=0)
            mean_test_suite_acc = np.mean(test_suite_acc_list_averaged_across_run)
            yerr = 1.96*(np.std(test_suite_acc_list_averaged_across_run)/np.sqrt(len(test_suite_acc_list_averaged_across_run)))
            ax.bar(i, mean_test_suite_acc, label=model2name[model], width=bar_width, color=model2color[model], yerr=yerr, **model_condition2style[model_condition])

        ax.set_ylim(0,1)
        ax.set_xlim(-0.75,len(models)-0.25)
        ax.spines['left'].set_visible(False)
        ax.set_yticks([])
        ax.set_yticklabels([])

        ax.spines['right'].set_visible(False)
        ax.spines['top'].set_visible(False)
        ax.set_xticks([])
        ax.set_xticklabels([])

        if model_cond_idx == 2:
            colors =['C{}'.format(k) for k in range(4)]
            model_names = ['GibbsComplete', 'InfillT5', 'InfillBART', 'ILM']
            model_condition_names = ['Pretrain/Fine-tune', 'From scratch (NYT)', 'From scratch (BLLIP)']
            color_legend = plt.legend(handles=[mpatches.Patch(facecolor='white', edgecolor='k', label='Human')]+[mpatches.Patch(facecolor=colors[k], edgecolor=colors[k], label=model_names[k]) for k in range(len(model_names))], loc='upper left', bbox_to_anchor=(1.15, 1.05), ncol=1, fontsize=10)
            hatch_legend = plt.legend(handles=[mpatches.Patch(facecolor='lightgray', edgecolor='k', linewidth=0, label=model_condition_names[k], **hatch_style_list[k]) for k in range(len(hatch_style_list))], loc='upper left', bbox_to_anchor=(1.15, 0.41), ncol=1,  fontsize=10)

            ax.add_artist(color_legend)
            ax.add_artist(hatch_legend)

    if savepath is not None:
        plt.savefig(savepath, bbox_inches='tight')

    plt.show(block=False)
    plt.pause(1)
    plt.close()

def run_paired_t_tests(exp_data_all, model_conditions):
    for model_cond_idx, model_condition in enumerate(model_conditions):
        rs, models, model2run_indice, model2name, model2color = exp_data_all[model_condition]

        model_acc_list_all = []

        for i, model in enumerate(models):
            data = [[rs[model][run_index][test_name]['acc'] for test_name in test_names] for run_index in model2run_indice[model]]
            model_acc_list_all.append(np.mean(data, axis=0))

        print('{:<22} {:<15} {:<15} {:<6}  {:<6}'.format('Learning setup', 'Model name', 'Model name', 't_stat', 'p_value'))
        print('-'*70)
        for i in range(len(models)):
            for j in range(i+1, len(models)):
                d1 = np.array(model_acc_list_all[i])
                d2 = np.array(model_acc_list_all[j])
                t_stat, p_value = scipy.stats.ttest_rel(d1, d2, alternative='two-sided')
                print('{:<22} {:<15} {:<15} {:<6.3f}  {:<6.3f}'.format(model_condition, model2name[models[i]], model2name[models[j]], t_stat, p_value))

        for i in range(len(models)):
            d1 = np.array(model_acc_list_all[i])
            d2 = [human_data[test_name]['acc_value'] for test_name in test_names]
            t_stat, p_value = scipy.stats.ttest_rel(d1, d2, alternative='two-sided')
            print('{:<22} {:<15} {:<15} {:<6.3f}  {:<6.3f}'.format(model_condition, model2name[models[i]], 'Human', t_stat, p_value))
        print()

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Analyze results in Evaluation III.')
    parser.add_argument('--rerank', action='store_true', help='Plot results from directly specialized models with reranking.')
    args = parser.parse_args()

    DO_RERANK='rerank' if args.rerank else 'norerank'

    DATA_DIR='data/exp1'

    test_names = ["agreement_subj", "agreement_subj-long", "agreement_emb-subj-long", "agreement_subj-with-coord", "agreement_subj-with-PP",
                    "clause_VP","clause_VP-with-PP-adjunct", "clause_VP-with-adjunct-long",
                    "clause_VP-with-complement", "clause_VP-with-complement-long", "clause_VP-gerund",
                    "clause_phrasal-verb", "clause_phrasal-verb-with-subj",
                    "clause_resultative", "clause_resultative-long",
                    "coord_S", "coord_VP", "coord_emb-NP", "coord_emb-VP",
                    "coord_either", "coord_neither", "coord_gap-NP", "gap_adjunct", "gap_obj", "gap_subj", "gap_phrasal-verb"]

    pretty_test_names = ["Number Agreement", "Number Agreement (Long Subject)", "Number Agreement (Embedded Clause)",
                        "Number Agreement (Coordination)", "Number Agreement (with PP)", "Clausal Structure", "Clausal Structure (PP Adjunct)",
                        "Clausal Structure (Long Adjunct)", "Clausal Structure (Complement)", "Clausal Structure (Long Complement)",
                        "Gerund", "Phrasal Verb", "Phrasal Verb (with NP)", "Resultative", "Resultative (Long NP)", "S Coordiation",
                        "VP Coordination", "Embedded NP Coordination", "Embedded VP Coordination", "Coordination (either)",
                        "Coordination (neither)", "Coordination in wh-clause", "Filler-Gap (Adjunct)", "Filler-Gap (Object)",
                        "Filler-Gap (Subject)", "Filler-Gap (Phrasal Verb)"]

    test_name2pretty_name = dict(zip(test_names, pretty_test_names))

    stimuli_example = {}

    for test_name in test_names:
        stimuli_path = '../stimuli/exp1/{}.txt'.format(test_name)
        with open(stimuli_path) as f:
            line = f.readline()
        stimuli_example[test_name] = line.strip().replace('%%', '____')

    # Load human behavioral results
    with open('{}/results/human_eval_rs.txt'.format(DATA_DIR)) as f:
        lines = f.readlines()

    lines = [line.strip().split() for line in lines if line.strip() != '']
    human_data = {}
    for line in lines:
        test_name = line[1]
        human_data[test_name] = {}
        human_data[test_name]['acc'] = float(line[2])
        proportions1 = [float(item) for item in line[3].split('/')]
        proportions2 = [float(item) for item in line[4].split('/')]

        acc_value, [acc_lower, acc_upper] = get_conf_int_stats(proportions1[0] + proportions2[0], proportions1[1] + proportions2[1], method='jeffreys')

        human_data[test_name]['acc_value'] = acc_value
        human_data[test_name]['acc_lower'] = acc_lower
        human_data[test_name]['acc_upper'] = acc_upper

    exp_data_all = {}

    fig_dir = 'fig/exp1/'

    model_name_list = ['GibbsComplete', 'InfillT5', 'InfillBART', 'ILM']
    model_color_list = ['C0', 'C1', 'C2', 'C3']

    model_conditions = ['finetune', 'nyt_from_scratch', 'bllip_from_scratch']

    model_condition2dir_name = dict(zip(model_conditions, ['pretrain-finetune', 'nyt-lg', 'bllip-lg']))

    for model_condition in model_conditions:

        if model_condition == 'nyt_from_scratch':
            # Load and visualize results for models trained from scratch on a subset of NYT
            models = ['gibbscomplete-nyt-lg', 't5-nyt-lg', 'bart-nyt-lg', 'ilm-nyt-lg']
            model2run_indice = {'gibbscomplete-nyt-lg':['0001', '0002', '0003'], 't5-nyt-lg':['0001', '0002', '0003'],
                                'bart-nyt-lg':['0001', '0002', '0003'], 'ilm-nyt-lg':['0001', '0002', '0003']}
        elif model_condition == 'finetune':
            # Load and visualize results for pretrained models finetuned on a subset of NYT 2007
            models = ['gibbscomplete', 't5-finetune', 'bart-finetune', 'ilm']

            if DO_RERANK == 'rerank':
                model2run_indice = {'gibbscomplete':['0001', '0002', '0003'], 't5-finetune':['1001', '1002', '1003'],
                                'bart-finetune':['1001', '1002', '1003'], 'ilm':['1001', '1002', '1003']}
            else:
                model2run_indice = {'gibbscomplete':['0001', '0002', '0003'], 't5-finetune':['0001', '0002', '0003'],
                    'bart-finetune':['0001', '0002', '0003'], 'ilm':['0001', '0002', '0003']}

        elif model_condition == 'bllip_from_scratch':
            # Load and visualize results for models trained from scratch on BLLIP-lg
            models = ['gibbscomplete-bllip-lg', 't5-bllip-lg', 'bart-bllip-lg', 'ilm-bllip-lg']
            model2run_indice = {'gibbscomplete-bllip-lg':['0101', '0102', '0103'], 't5-bllip-lg':['0001', '0002', '0003'],
                                'bart-bllip-lg':['0001', '0002', '0003'], 'ilm-bllip-lg':['0001', '0002', '0003']}

        model2name = dict(zip(models, model_name_list))
        model2color = dict(zip(models, model_color_list))

        rs = {}
        for model in models:
            rs[model] = {}
            for run_index in model2run_indice[model]:
                rs[model][run_index] = {}
                for test_name in test_names:
                    rs[model][run_index][test_name] = {'acc':None, 'item_acc_list':[]}

                if model.startswith('gibbscomplete'):
                    path = '{}/results/{}/{}_{}_eval_rs.txt'.format(DATA_DIR, model_condition2dir_name[model_condition], model, run_index)
                else:
                    if DO_RERANK == 'rerank':
                        path = '{}/results/{}/{}_rerank_{}_eval_rs.txt'.format(DATA_DIR, model_condition2dir_name[model_condition], model, run_index)
                    else:
                        path = '{}/results/{}/{}_{}_eval_rs.txt'.format(DATA_DIR, model_condition2dir_name[model_condition], model, run_index)
                lines = open(path).readlines()
                lines = [line.strip().split() for line in lines]
                for line in lines:
                    if len(line) < 1:
                        continue
                    test_name = line[0]
                    item_acc = float(line[2])
                    rs[model][run_index][test_name]['item_acc_list'].append(item_acc)

                for test_name in test_names:
                    rs[model][run_index][test_name]['acc'] = np.mean(rs[model][run_index][test_name]['item_acc_list'])

        plot_rs_by_test_suite_grid_5_by_6(rs, models, human_data, test_names, model2run_indice, model2color, model2name, savepath='{}/exp1_{}_{}_eval_grid_bar_5x6.pdf'.format(fig_dir, DO_RERANK, model_condition))
        plot_rs_by_test_suite_grid_3_by_9(rs, models, human_data, test_names, model2run_indice, model2color, model2name, savepath='{}/exp1_{}_{}_eval_grid_bar_3x9.pdf'.format(fig_dir, DO_RERANK, model_condition))
        # plot_aggregated_rs(rs, models, human_data, test_names, model2run_indice, model2color, model2name, savepath='{}/exp1_{}_{}_eval_bar_average_score.pdf'.format(fig_dir, model_condition, DO_RERANK))

        exp_data_all[model_condition] = [rs, models, model2run_indice, model2name, model2color]

    run_paired_t_tests(exp_data_all, model_conditions)

    plot_summmary_across_model_conditions(exp_data_all, model_conditions, savepath='{}/exp1_{}_overall_summary.pdf'.format(fig_dir, DO_RERANK))


# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html

# -- Path setup --------------------------------------------------------------

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import shutil
import sys

sys.path.insert(0, os.path.abspath("../.."))  # path to the actual project root folder

# -- Project information -----------------------------------------------------

project = "Spotted dmi bot"
copyright = "2021, Tend, drendog, alepiaz, Helias"
author = "Tend, drendog, alepiaz, Helias"

# -- General configuration ---------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.doctest",  # to run doctests
    "sphinx.ext.napoleon",  # to use NumPy and Google style docstrings
    "sphinx.ext.githubpages",  # generates the .nojekyll file
    "sphinx.ext.viewcode",  # add source code links to the documentation
    "sphinx_rtd_dark_mode",  # dark mode for ReadTheDocs
    "sphinx_autodoc_typehints",  # improves the type hinting
    "sphinx.ext.viewcode",  # add source code links to the documentation
    "sphinx.ext.coverage",  # add coverage links to the documentation
    "sphinx.ext.intersphinx",  # add external mapping to other documentation
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []

# -- Options for HTML output -------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_rtd_theme"  # [optional, to use the far superior Read the Docs theme]

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
html_css_files = [
    "css/dark.css",
]

html_logo = "_static/img/spotted-logo.jpg"

# -- Extension configuration -------------------------------------------------

# -- Configuration of "sphinx_autodoc_typehints" -----------------------------
typehints_use_rtype = False
typehints_defaults = "comma"

# -- Run sphinx-apidoc -------------------------------------------------------
# This hack is necessary since RTD does not issue `sphinx-apidoc` before running
# `sphinx-build -b html . _build/html`. See Issue:
# https://github.com/readthedocs/readthedocs.org/issues/1139
# DON'T FORGET: Check the box "Install your project inside a virtualenv using
# setup.py install" in the RTD Advanced Settings.
# Additionally it helps us to avoid running apidoc manually

try:  # for Sphinx >= 1.7
    from sphinx.ext import apidoc
except ImportError:
    from sphinx import apidoc

output_dir = os.path.join(os.path.dirname(__file__), "api")
module_dir = os.path.join(os.path.dirname(__file__), "../../src/spotted")
try:
    shutil.rmtree(output_dir)
except FileNotFoundError:
    pass

try:
    import sphinx

    cmd_line = f"sphinx-apidoc --implicit-namespaces -t templates -f -o {output_dir} {module_dir}"

    args = cmd_line.split(" ")
    if tuple(sphinx.__version__.split(".")) >= ("1", "7"):
        # This is a rudimentary parse_version to avoid external dependencies
        args = args[1:]

    apidoc.main(args)
except Exception as e:
    print("Running `sphinx-apidoc` failed!\n{}".format(e))

# -- External mapping --------------------------------------------------------
python_version = ".".join(map(str, sys.version_info[0:2]))
intersphinx_mapping = {
    "sphinx": ("https://www.sphinx-doc.org/en/master", None),
    "python": ("https://docs.python.org/" + python_version, None),
    "matplotlib": ("https://matplotlib.org", None),
    "numpy": ("https://numpy.org/doc/stable", None),
    "sklearn": ("https://scikit-learn.org/stable", None),
    "pandas": ("https://pandas.pydata.org/pandas-docs/stable", None),
    "scipy": ("https://docs.scipy.org/doc/scipy/reference", None),
    "setuptools": ("https://setuptools.pypa.io/en/stable/", None),
    "pyscaffold": ("https://pyscaffold.org/en/stable", None),
    "telegram": ("https://docs.python-telegram-bot.org/en/stable/", None),
}


from CrearPreguntas import *
import random

class Partida:
	def __init__(self):
		self._puntaje = 0
		self._preguntas_partida = []
		self._nombre = ""
		self._nivel = 0
		self._respuesta = 0
		self._vivo = True

	def get_puntaje(self):
		return self._puntaje

	def get_vivo(self):
		return self._vivo

	def get_nivel(self):
		return self._nivel

	def set_nombre(self, nombre):
		self._nombre = nombre

	def get_nombre(self):
		return self._nombre

	def aumentar_puntaje(self):
		self._puntaje += 10

	def mostrar_puntaje(self):
		print("TU PUNTAJE ES: ", self._puntaje, "\n")

	def configurar_juego(self):
		lista_preguntas = CrearPreguntas()
		lista = lista_preguntas.get_lista_preguntas()
		dificultad = 1
		primera_ves = True
		categoria = ""

		while len(self._preguntas_partida) < 5:
			numero = random.randint(0, 24)
			if primera_ves == True:
				if lista[numero].dificultad == dificultad:
					self._preguntas_partida.append(lista[numero])
					dificultad += 1
					categoria = lista[numero].categoria
					primera_ves = False
					print("\nLa categoria de las preguntas es: ", categoria)
			else:
				if lista[numero].dificultad == dificultad and lista[numero].categoria == categoria:
					self._preguntas_partida.append(lista[numero])
					dificultad += 1
					primera_ves = False

	def jugar(self):
		print(self._preguntas_partida[self._nivel].pregunta)
		print()
		print("1. " + self._preguntas_partida[self._nivel].opcion1)
		print("2. " + self._preguntas_partida[self._nivel].opcion2)
		print("3. " + self._preguntas_partida[self._nivel].opcion3)
		print("4. " + self._preguntas_partida[self._nivel].opcion4)

	def subir_nivel(self):
 		self._nivel += 1

	def responder(self, respuesta):
 		self._respuesta = respuesta
 		if self._respuesta == self._preguntas_partida[self._nivel].respuesta:
 			print("\n¡CORRECTO!")
 			self.aumentar_puntaje()
 			self.mostrar_puntaje()
 		else:
 			print("\n¡INCORRECTO!")
 			self._vivo = False
 			self.mostrar_puntaje()


_base_ = '../yolov5/yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py'

data_root = 'data/Ver3/output/'  # Root path of data
# Path of train annotation file
train_ann_file = 'train.json'
train_data_prefix = 'train/'  # Prefix of train image path
# Path of val annotation file
val_ann_file = 'val.json'
val_data_prefix = 'val/'  # Prefix of val image path

class_name = ('ebike', ) # the name of classes
num_classes = len(class_name) # the number of classes
metainfo = dict(classes=class_name, palette=[(20, 220, 60)])

train_batch_size_per_gpu = 32
max_epochs = 400  # Maximum training epochs

# ========================modified parameters======================
deepen_factor = 0.67
widen_factor = 0.75
lr_factor = 0.1
affine_scale = 0.9
loss_cls_weight = 0.3
loss_obj_weight = 0.7
mixup_prob = 0.1
load_from = "/mnt/c/Users/ShengRen/wise_transportation/mmyolo/work_dirs/MMYolo/yolov5_m-v61_syncbn_fast_8xb16-300e_coco_20220917_204944-516a710f.pth"

# =======================Unmodified in most cases==================
num_det_layers = _base_.num_det_layers
img_scale = _base_.img_scale

model = dict(
    backbone=dict(
        frozen_stages=4,
        deepen_factor=deepen_factor,
        widen_factor=widen_factor,
    ),
    neck=dict(
        deepen_factor=deepen_factor,
        widen_factor=widen_factor,
    ),
    bbox_head=dict(
        head_module=dict(widen_factor=widen_factor,
                         num_classes=num_classes),
        loss_cls=dict(loss_weight=loss_cls_weight *
                      (num_classes / 80 * 3 / num_det_layers)),
        loss_obj=dict(loss_weight=loss_obj_weight *
                      ((img_scale[0] / 640)**2 * 3 / num_det_layers))))

pre_transform = _base_.pre_transform
albu_train_transforms = _base_.albu_train_transforms

mosaic_affine_pipeline = [
    dict(
        type='Mosaic',
        img_scale=img_scale,
        pad_val=114.0,
        pre_transform=pre_transform),
    dict(
        type='YOLOv5RandomAffine',
        max_rotate_degree=0.0,
        max_shear_degree=0.0,
        scaling_ratio_range=(1 - affine_scale, 1 + affine_scale),
        # img_scale is (width, height)
        border=(-img_scale[0] // 2, -img_scale[1] // 2),
        border_val=(114, 114, 114))
]

# enable mixup
train_pipeline = [
    *pre_transform, *mosaic_affine_pipeline,
    dict(
        type='YOLOv5MixUp',
        prob=mixup_prob,
        pre_transform=[*pre_transform, *mosaic_affine_pipeline]),
    dict(
        type='mmdet.Albu',
        transforms=albu_train_transforms,
        bbox_params=dict(
            type='BboxParams',
            format='pascal_voc',
            label_fields=['gt_bboxes_labels', 'gt_ignore_flags']),
        keymap={
            'img': 'image',
            'gt_bboxes': 'bboxes'
        }),
    dict(type='YOLOv5HSVRandomAug'),
    dict(type='mmdet.RandomFlip', prob=0.5),
    dict(
        type='mmdet.PackDetInputs',
        meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'flip',
                   'flip_direction'))
]

train_dataloader = dict(dataset=dict(pipeline=train_pipeline,
                                     data_root=data_root,
                                     metainfo=metainfo,
                                     ann_file=train_ann_file,
                                     data_prefix=dict(img=train_data_prefix),),
                        batch_size=train_batch_size_per_gpu)
val_dataloader = dict(dataset=dict(data_root=data_root,
                                     metainfo=metainfo,
                                     data_prefix=dict(img=val_data_prefix),
                                     ann_file=val_ann_file,))
test_dataloader = val_dataloader
val_evaluator = dict(ann_file=data_root + val_ann_file)
test_evaluator = val_evaluator
default_hooks = dict(param_scheduler=dict(lr_factor=lr_factor,
                                          max_epochs=max_epochs))
train_cfg = dict(max_epochs=max_epochs)
visualizer = dict(vis_backends=[dict(type='LocalVisBackend'), dict(type='TensorboardVisBackend')])


# -*- coding: utf-8 -*-
"""
Created on Fri Sep 16 13:22:19 2016

@author: Zhaoyi.Shen
"""

import numpy as np

def swobs_col(filename):
    npz = np.load(filename)
    swup_toa = npz['swup_toa']
    swdn_toa = npz['swdn_toa']
    swup_sfc = npz['swup_sfc']
    swdn_sfc = npz['swdn_sfc']
    return swdn_toa-swdn_sfc+swup_sfc-swup_toa

def swobs_col_clr(filename):
    npz = np.load(filename)
    swup_toa = npz['swup_toa_clr']
    swdn_toa = npz['swdn_toa_clr']
    swup_sfc = npz['swup_sfc_clr']
    swdn_sfc = npz['swdn_sfc_clr']
    return swdn_toa-swdn_sfc+swup_sfc-swup_toa

def lwobs_col(filename):
    npz = np.load(filename)
    olr = npz['olr']
    lwup_sfc = npz['lwup_sfc']
    lwdn_sfc = npz['lwdn_sfc']
    return -lwdn_sfc+lwup_sfc-olr

def lwobs_col_clr(filename):
    npz = np.load(filename)
    olr = npz['olr_clr']
    lwup_sfc = npz['lwup_sfc_clr']
    lwdn_sfc = npz['lwdn_sfc_clr']
    return -lwdn_sfc+lwup_sfc-olr


import pandas as pd
import streamlit as st
import numpy as np

from common import session_manager
ssm = session_manager.st_session()
# ssm.write_session_info()

st.title("表を描画する")

# データフレームを元に表を表示する
df = pd.DataFrame({
  'first column': [1, 2, 3, 4],
  'second column': [10, 20, 30, 40]
})

st.write("write関数")
st.write(df)

st.write("table関数")
st.table(df)

st.write("dataframe関数、writeとほとんど同じ？")
st.dataframe(df)

#
df2 = np.random.randn(10, 20)
st.dataframe(df2)

#
dataframe = pd.DataFrame(
    np.random.randn(10, 20),
    columns=('col %d' % i for i in range(20)))  # col名を設定

# highlight_max（最大値にハイライトする）、axis=0（インデックス（列？）の対して評価する、１とすると行になる）
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.io.formats.style.Styler.highlight_max.html#pandas.io.formats.style.Styler.highlight_max
st.dataframe(dataframe.style.highlight_max(axis=1,color="red"))

#
st.table(dataframe.style.highlight_max(axis=1,color="red"))


import cv2
import numpy
import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold
import os
import random
from tqdm import tqdm
from segment_anything import SamAutomaticMaskGenerator, sam_model_registry, SamPredictor
import argparse
from utils.utils import *
import time

def get_embedding(img, predictor):
    predictor.set_image(img)
    img_emb = predictor.get_image_embedding()
    return img_emb

def train(args, predictor):
    data_path = args.data_path
    assert os.path.exists(data_path), 'data path does not exist!'

    num_image = args.k

    fnames = os.listdir(os.path.join(data_path, 'images'))
    # get 20 random indices from fnames
    random.shuffle(fnames)
    fnames = fnames[:num_image]
    image_embeddings = []
    labels = []

    # get the image embeddings
    print('Start training...')
    t1 = time.time()
    i = 0
    for fname in tqdm(fnames):
        # read data
        image = cv2.imread(os.path.join(data_path, 'images', fname))
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mask = cv2.imread(os.path.join(data_path, 'masks', fname))
        mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
        _, mask = cv2.threshold(mask, 128, 1, cv2.THRESH_BINARY) # threshold the mask to 0 and 1
        downsampled_mask = cv2.resize(mask, dsize=(64, 64), interpolation=cv2.INTER_NEAREST)

        img_emb = get_embedding(image, predictor)
        img_emb = img_emb.cpu().numpy().transpose((2, 3, 1, 0)).reshape((64, 64, 256)).reshape(-1, 256)
        image_embeddings.append(img_emb)

        labels.append(downsampled_mask.flatten())

        i += 1
        if i > num_image: break
    t2 = time.time()
    print("Time used: {}m {}s".format((t2 - t1) // 60, (t2 - t1) % 60))
    image_embeddings_cat = np.concatenate(image_embeddings)
    labels = np.concatenate(labels)

    # Create a linear regression model and fit it to the training data
    model = LogisticRegression(max_iter=1000)
    model.fit(image_embeddings_cat, labels)

    return model

def test_visualize(args, model, predictor):
    data_path = args.data_path

    num_image = args.k
    fnames = os.listdir(os.path.join(data_path, 'images'))
    random.shuffle(fnames)
    fnames = fnames[num_image:]
    num_visualize = args.visualize_num

    dice_linear = []
    dice1 = []
    dice2 = []
    dice3 = []
    i = 0

    for fname in tqdm(fnames[:num_visualize]):
        # read data
        image = cv2.imread(os.path.join(data_path, 'images', fname))
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mask = cv2.imread(os.path.join(data_path, 'masks', fname))
        mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
        _, mask = cv2.threshold(mask, 128, 1, cv2.THRESH_BINARY)
        H, W, _ = image.shape

        # get the image embedding and flatten it
        img_emb = get_embedding(image, predictor)
        img_emb = img_emb.cpu().numpy().transpose((2, 3, 1, 0)).reshape((64, 64, 256)).reshape(-1, 256)

        # get the mask predicted by the linear classifier
        y_pred = model.predict(img_emb)
        y_pred = y_pred.reshape((64, 64))
        # mask predicted by the linear classifier
        mask_pred_l = cv2.resize(y_pred, (mask.shape[1], mask.shape[0]), interpolation=cv2.INTER_NEAREST)

        # use distance transform to find a point inside the mask
        fg_point = get_max_dist_point(mask_pred_l)
        # Define the kernel for dilation
        kernel = np.ones((5, 5), np.uint8)
        eroded_mask = cv2.erode(mask_pred_l, kernel, iterations=3)
        mask_pred_l = cv2.dilate(eroded_mask, kernel, iterations=5)

        # set the image to sam
        predictor.set_image(image)

        # prompt the sam with the point
        input_point = np.array([[fg_point[0], fg_point[1]]])
        input_label = np.array([1])
        masks_pred_sam_prompted1, _, _ = predictor.predict(
            point_coords=input_point,
            point_labels=input_label,
            box=None,
            multimask_output=False,
        )

        # prompt the sam with the bounding box
        y_indices, x_indices = np.where(mask_pred_l > 0)
        if np.all(mask_pred_l == 0):
            bbox = np.array([0, 0, H, W])
        else:
            x_min, x_max = np.min(x_indices), np.max(x_indices)
            y_min, y_max = np.min(y_indices), np.max(y_indices)
            H, W = mask_pred_l.shape
            x_min = max(0, x_min - np.random.randint(0, 20))
            x_max = min(W, x_max + np.random.randint(0, 20))
            y_min = max(0, y_min - np.random.randint(0, 20))
            y_max = min(H, y_max + np.random.randint(0, 20))
            bbox = np.array([x_min, y_min, x_max, y_max])
        masks_pred_sam_prompted2, _, _ = predictor.predict(
            point_coords=None,
            point_labels=None,
            box=bbox[None, :],
            multimask_output=False,)

        # prompt the sam with both the point and bounding box
        masks_pred_sam_prompted3, _, _ = predictor.predict(
            point_coords=input_point,
            point_labels=input_label,
            box=bbox[None, :],
            multimask_output=False,)

        dice_l = dice_coef(mask, mask_pred_l)
        dice_p = dice_coef(mask, masks_pred_sam_prompted1[0])
        dice_b = dice_coef(mask, masks_pred_sam_prompted2[0])
        dice_i = dice_coef(mask, masks_pred_sam_prompted3[0])
        dice_linear.append(dice_l)
        dice1.append(dice_p)
        dice2.append(dice_b)
        dice3.append(dice_i)

        # plot the results
        fig, ax = plt.subplots(1, 5, figsize=(15, 10))
        ax[0].set_title('Ground Truth')
        ax[0].imshow(mask)
        ax[1].set_title('Linear + e&d')
        ax[1].plot(fg_point[0], fg_point[1], 'r.')
        ax[1].imshow(mask_pred_l)
        ax[2].set_title('Point')
        ax[2].plot(fg_point[0], fg_point[1], 'r.')
        ax[2].imshow(masks_pred_sam_prompted1[0])
        ax[3].set_title('Box')
        show_box(bbox, ax[3])
        ax[3].imshow(masks_pred_sam_prompted2[0])
        ax[4].set_title('Point + Box')
        ax[4].plot(fg_point[0], fg_point[1], 'r.')
        show_box(bbox, ax[4])
        ax[4].imshow(masks_pred_sam_prompted3[0])
        [axi.set_axis_off() for axi in ax.ravel()]

        if os.path.exists(args.save_path) == False:
            os.mkdir(args.save_path)
        plt.savefig(os.path.join(args.save_path, fname.split('.')[0]+str(i)))

    mdice0 = round(sum(dice_linear)/float(len(dice_linear)), 5)
    mdice1 = round(sum(dice1)/float(len(dice1)), 5)
    mdice2 = round(sum(dice2)/float(len(dice2)), 5)
    mdice3 = round(sum(dice3)/float(len(dice3)), 5)

    print('For the first {} images: '.format(num_visualize))
    print('mdice(linear classifier: )', mdice0)
    print('mDice(point prompts): ', mdice1)
    print('mDice(bbox prompts): ', mdice2)
    print('mDice(points and boxes): ', mdice3)

def test(args, predictor):
    data_path = args.data_path
    images = []
    masks = []
    fnames = os.listdir(os.path.join(data_path, 'images'))
    print(f'loading images from {data_path}...')
    for fname in tqdm(fnames):
        # read data
        image = cv2.imread(os.path.join(data_path, 'images', fname))
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mask = cv2.imread(os.path.join(data_path, 'masks', fname))
        mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
        _, mask = cv2.threshold(mask, 128, 1, cv2.THRESH_BINARY)
        images.append(image)
        masks.append(mask)

    kf = KFold(n_splits=5, shuffle=True, random_state=42)
    for train_index, text_index in kf.split(images):
        train_images = [images[i] for i in train_index]
        train_masks = [masks[i] for i in train_index]
        test_images = [images[i] for i in text_index]
        test_masks = [masks[i] for i in text_index]

        # train the linear classifier
        k = args.k
        random_indices = random.sample(range(len(train_images)), k)
        image_embeddings = []
        labels = []
        for idx in random_indices:
            image = train_images[idx]
            mask = train_masks[idx]
            downsampled_mask = cv2.resize(mask, dsize=(64, 64), interpolation=cv2.INTER_NEAREST)

            img_emb = get_embedding(image)
            img_emb = img_emb.cpu().numpy().transpose((2, 3, 1, 0)).reshape((64, 64, 256)).reshape(-1, 256)
            image_embeddings.append(img_emb)
            labels.append(downsampled_mask.flatten())

        image_embeddings_cat = numpy.concatenate(image_embeddings)
        labels = numpy.concatenate(labels)

        model = LogisticRegression(max_iter=1000) # how to set parameters?? C, max_iter, verbose, solver
        model.fit(image_embeddings_cat, labels)

        # test
        dice_linear=[]
        dice1=[]
        dice2=[]
        dice3=[]
        for idx in range(len(test_images)):
            image = test_images[idx]
            mask = test_masks[idx]
            H, W, _ = image.shape

            img_emb = get_embedding(image)
            img_emb = img_emb.cpu().numpy().transpose((2, 3, 1, 0)).reshape((64, 64, 256)).reshape(-1, 256)

            # ger the mask predicted by the linear classifier
            y_pred = model.predict(img_emb)
            y_pred = y_pred.reshape((64,64))
            mask_pred_l = cv2.resize(y_pred, (mask.shape[1], mask.shape[0]), interpolation=cv2.INTER_NEAREST)

            # use distance transform to find a point inside the mask
            fg_point = get_max_dist_point(mask_pred_l)

            # Define the kernel for dilation
            kernel = np.ones((5, 5), np.uint8)

            eroded_mask = cv2.erode(mask_pred_l, kernel, iterations=3)
            mask_pred_l = cv2.dilate(eroded_mask, kernel, iterations=5)

            # set the image to sam
            predictor.set_image(image)

            # prompt sam with the point
            input_point = np.array([[fg_point[0], fg_point[1]]])
            input_label = np.array([1])
            masks_pred_sam_prompted1, _, logits = predictor.predict(
            point_coords=input_point,
            point_labels=input_label,
            box=None,
            multimask_output=False,)

            # prompt sam with the bbox
            y_indices, x_indices = np.where(mask_pred_l > 0)
            if np.all(mask_pred_l==0):
                bbox = np.array([0 ,0, H, W])
            else:
                x_min, x_max = np.min(x_indices), np.max(x_indices)
                y_min, y_max = np.min(y_indices), np.max(y_indices)
                H, W = mask_pred_l.shape
                x_min = max(0, x_min - np.random.randint(0, 20))
                x_max = min(W, x_max + np.random.randint(0, 20))
                y_min = max(0, y_min - np.random.randint(0, 20))
                y_max = min(H, y_max + np.random.randint(0, 20))
                bbox = np.array([x_min, y_min, x_max, y_max])
                masks_pred_sam_prompted2, _, _ = predictor.predict(
                point_coords=None,
                point_labels=None,
                box=bbox[None, :],
                multimask_output=False,)

                masks_pred_sam_prompted3, _, _,= predictor.predict(
                point_coords=input_point,
                point_labels=input_label,
                box=bbox[None, :],
                multimask_output=False,)

                dice_l = dice_coef(mask, mask_pred_l)
                dice_p = dice_coef(mask, masks_pred_sam_prompted1[0])
                dice_b = dice_coef(mask, masks_pred_sam_prompted2[0])
                dice_c = dice_coef(mask, masks_pred_sam_prompted3[0])
                dice_linear.append(dice_l)
                dice1.append(dice_p)
                dice2.append(dice_b)
                dice3.append(dice_c)

        mdice0 = round(sum(dice_linear)/float(len(dice_linear)), 5)
        mdice1 = round(sum(dice1)/float(len(dice1)), 5)
        mdice2 = round(sum(dice2)/float(len(dice2)), 5)
        mdice3 = round(sum(dice3)/float(len(dice3)), 5)

        print('mdice(linear classifier: )', mdice0)
        print('mDice(point prompts): ', mdice1)
        print('mDice(bbox prompts): ', mdice2)
        print('mDice(points and boxes): ', mdice3)
        print('\n')

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument('--device', type=str, default='cuda:0', help='device')
    parser.add_argument('--k', type=int, default=10, help='number of pics')
    parser.add_argument('--data_path', type=str, default='./data/Kvasir-SEG', help='path to train data')
    parser.add_argument('--model_type', type=str, default='vit_b', help='SAM model type')
    parser.add_argument('--checkpoint', type=str, default='./checkpoints/sam_vit_b_01ec64.pth', help='SAM checkpoint')
    parser.add_argument('--visualize', type=bool, default=True, help='visualize the results')
    parser.add_argument('--save_path', type=str, default='./results', help='path to save the results')
    parser.add_argument('--visualize_num', type=int, default=30, help='number of pics to visualize')
    args = parser.parse_args()

    # set random seed
    random.seed(42)

    # register the SAM model
    sam = sam_model_registry[args.model_type](checkpoint=args.checkpoint).to(args.device)
    global predictor
    predictor = SamPredictor(sam)
    print('SAM model loaded!', '\n')

    if args.visualize:
        model = train(args, predictor)
        test_visualize(args, model, predictor)
    else:
        test(args, predictor)

if __name__ == '__main__':
    main()


from pyspark.sql import Window
import pyspark.sql.functions as f
from app import columns

class QueryManager:
    def __init__(self, spark, trip_fare_df, trip_data_df):
        self.spark = spark
        self.trip_fare_df = trip_fare_df
        self.trip_data_df = trip_data_df

    def trips_count(self, date_column):
        """
        Args:
            date_column: desired date column in dataframe
        Returns:
            dataframe which has three columns
            1. Vendor_ID
            2. Day of Week
            3. Count (count of trips)
        """
        trip_df = self.trip_data_df.withColumn('dayofweek',
                                               f.date_format(self.trip_data_df[date_column], 'EEEE'))
        trips_by_week = (trip_df.filter(f.col(columns.vendor_id).isNotNull()).groupBy(columns.vendor_id, 'dayofweek').
                         count().orderBy(f.desc(columns.vendor_id), f.desc('count')).withColumn('max_trip_count',
                                                                                f.max('count').over(
                                                                                Window.partitionBy(
                                                                                'vendor_id')))
                         .filter(f.col('count') == f.col('max_trip_count')).drop('max_trip_count'))
        return trips_by_week

    def total_revenue(self):
        """ Calculates the total revenue of each vendor
             Returns:
            DataFrame: A DataFrame containing the total revenue for each vendor.
        """
        dataframe = (self.trip_fare_df.filter(f.col(columns.vendor_id).isNotNull()).groupBy(columns.vendor_id)
                     .agg(f.format_number(f.sum(columns.total_amount), 2).alias('total revenue')))
        return dataframe

    def avg_trip_distance(self):
        """
            Calculates the average trip distance for different numbers of passengers.

            Returns:
                DataFrame: A DataFrame containing the average trip distance for each combination of vendor
                and passenger count.
        """
        dataframe = (self.trip_data_df.filter(f.col(columns.passenger_count).
                                              isNotNull()).groupBy(columns.vendor_id, columns.passenger_count).
                     agg(f.avg(columns.trip_distance)).orderBy(f.desc(columns.passenger_count)))
        return dataframe

    def simultaneous_trips(self):
        """
        Calculates the maximum number of simultaneous trips that happened on the same day.

        Returns:
            DataFrame: A DataFrame containing the maximum number of simultaneous trips for the top 5 days.
        """
        pickup_dataframe = (self.trip_data_df.filter(f.col(columns.pickup_datetime).isNotNull()).
                            select(f.col(columns.pickup_datetime).alias('event_time'),
                                   f.lit(1).alias('event_count')))
        dropoff_dateframe = (self.trip_data_df.filter(f.col(columns.dropoff_datetime).isNotNull()).
                             select(f.col(columns.dropoff_datetime).alias('event_time'),
                                    f.lit(-1).alias('event_count')))
        event_dateframe = pickup_dataframe.union(dropoff_dateframe)
        dataframe = event_dateframe.withColumn('sum', f.sum('event_count').over(Window.partitionBy('event_time')
                                                                                .orderBy(f.asc('event_time'))))
        dataframe = dataframe.groupBy(f.date_format('event_time', 'yyyy-MM-dd').alias('day')
                                      ).agg(f.max('sum').alias('simultaneous_trips')).orderBy(
            f.desc(f.col('simultaneous_trips'))).limit(5)
        return dataframe

    def most_expensive_trips(self):
        """
        Calculates the most expensive trips for each vendor.

        Returns:
            DataFrame: A DataFrame containing the most expensive trips for each vendor.
        """
        dataframe = (self.trip_fare_df.filter(f.col(columns.vendor_id).isNotNull())
                     .groupBy(columns.vendor_id).agg(f.max(columns.total_amount).
                                                     alias(columns.total_amount)))
        return dataframe

    def avg_amount_rate_code(self):
        """
        Calculates the count of trips with a tip above the average tip amount for trips with different rate codes.

        Returns:
            DataFrame: A DataFrame containing the count of such trips for each rate code.
        """
        dataframe = self.trip_fare_df.join(self.trip_data_df, ['medallion', 'hack_license', 'vendor_id',
                                                               'pickup_datetime'], 'inner')

        average_tip_amounts = dataframe.groupBy(columns.rate_code).agg(f.avg(columns.tip_amount)
                                                                       .alias('avg_tip_amount'))

        joined_data = dataframe.join(average_tip_amounts, [columns.rate_code], 'inner')
        dataframe = joined_data.withColumn('tip_above_avg', f.col('tip_amount') > f.col('avg_tip_amount'))
        dataframe = (dataframe.groupBy(columns.rate_code).count().withColumnRenamed('count', 'trip_count').
                     orderBy(f.desc('trip_count')))
        return dataframe

    def tips_count(self):
        """ Identifies the specific day of the week when each vendor tends to receive the highest amount of tips.

        Returns:
            DataFrame: A DataFrame containing the day of the week and the corresponding highest amount of tips received
            for each vendor.
        """
        window_spec = Window.partitionBy(columns.vendor_id).orderBy(f.col("total_tips").desc())

        dataframe = (self.trip_fare_df.withColumn("day_of_week", f.date_format(columns.pickup_datetime, 'EEEE'))
                     .groupBy(columns.vendor_id, "day_of_week")
                     .agg(f.format_number(f.sum(columns.tip_amount), 2).alias("total_tips"))
                     .withColumn("rank", f.row_number().over(window_spec))
                     .filter(f.col("rank") == 1)
                     .select(columns.vendor_id, "day_of_week", "total_tips"))

        return dataframe

    def avg_fare_amount_payment(self):
        """ Calculates the average fare amount for each payment type.

        Returns:
            DataFrame: A DataFrame containing the average fare amount for each payment type.
        """
        dataframe = (self.trip_fare_df.groupBy(columns.payment_type)
                     .agg(f.format_number(f.avg(columns.fare_amount), 2).alias("average_fare_amount"))
                     .orderBy(f.desc("average_fare_amount")))

        return dataframe

    def top_vendor_drivers(self):
        """ Identifies the top 10 drivers for each vendor based on average trip distance and total tip amount.

        Returns:
            DataFrame: A DataFrame containing the vendor ID, unique driver license, average mileage covered, total tip
            amount received and the corresponding rank.
        """
        joined_df = (self.trip_data_df.withColumnRenamed(columns.vendor_id, "vendor")
                     .join(self.trip_fare_df, [columns.hack_license, columns.pickup_datetime],
                           'inner'))

        window_spec = Window.partitionBy("vendor").orderBy(f.desc("average mileage"), f.desc("total tip amount"))
        dataframe = (joined_df.groupBy(["vendor", columns.hack_license])
                     .agg(f.format_number(f.avg(columns.trip_distance), 2).alias('average mileage'),
                          f.format_number(f.sum(columns.tip_amount), 2).alias('total tip amount'))
                     .withColumn("rank", f.rank().over(window_spec))
                     .filter(f.col("rank") <= 10))

        return dataframe

    def percentage_long_trips(self):
        """ Calculates the percentage of trips with a duration greater than 30 minutes for each vendor.

        Returns:
            DataFrame: A DataFrame containing the vendor ID, total trips executed for each vendor, amount of trips whose
            duration greater than 30 minutes and percentage of these trips.
        """
        dataframe = (self.trip_data_df.filter(f.col(columns.vendor_id) != 'None')
                     .groupBy(columns.vendor_id)
                     .agg(f.count("*").alias("total_trips"),
                          f.count(f.when(f.col(columns.trip_time_in_secs) > 1800, True))
                          .alias("long_trips"))
                     .withColumn("percentage_long_trips",
                                 f.format_number((f.col("long_trips") /
                                                  f.col("total_trips")) * 100, 2)))

        return dataframe

    def top_tips_in_cash(self):
        """ Calculates top 5 biggest tips for each vendor if the user paid in cash.

        Returns:
            DataFrame: A DataFrame containing the vendor ID and top 5 largest tips paid in cash for each vendor.
        """
        window_spec = Window.partitionBy(columns.vendor_id).orderBy(f.desc(columns.tip_amount))
        dataframe = (self.trip_fare_df.filter(f.col(columns.payment_type) == "CSH")
                     .withColumn("rank", f.dense_rank().over(window_spec))
                     .filter(f.col("rank") <= 5).select(columns.vendor_id, columns.tip_amount, "rank"))

        return dataframe

    def trips_weekdays_weekend(self):
        """ Calculates the number of trips occurred on weekend and weekdays for each vendor.

        Returns:
            DataFrame: A DataFrame containing the number of trips executed on weekdays and weekends for each vendor.
        """
        weekdays = [2, 3, 4, 5, 6]

        dataframe = self.trip_fare_df.withColumn("day_of_week", f.dayofweek(f.col(columns.pickup_datetime)))
        dataframe = (dataframe.withColumn("day_type", f.when(f.col("day_of_week")
                                                             .isin(weekdays), "weekday").otherwise("weekend"))
                     .groupBy(columns.vendor_id, "day_type")
                     .count()
                     .orderBy(columns.vendor_id, "day_type"))

        return dataframe

    def trips_with_tip_mount_greater_than_fare_amount(self):
        """ Data of trips with tips amount greater than the fare amount.

        Returns:
            dataframe with columns:
            medallion, hack_license, vendor_id, pickup_datetime, payment_type, fare_amount, tip_amount.
        """

        result_columns_names = [columns.medallion, columns.hack_license, columns.vendor_id, columns.pickup_datetime,
                                columns.payment_type, columns.fare_amount, columns.tip_amount]
        trips_with_tip_mount_greater_than_fare_amount = (
            self.trip_fare_df.filter(f.col(columns.fare_amount) < f.col(columns.tip_amount))
            .select(*result_columns_names)
        )
        return trips_with_tip_mount_greater_than_fare_amount

    def total_earnings_of_each_vendor_for_first_seven_days_of_january(self):
        """ Sum of earning of each vendor for trips that started on each of the first seven days of January 2013.

        Returns:
            dataframe with columns:
            vendor_id, date(in format yyyy-MM-dd), total_earnings.
        """
        column_date = 'date'
        column_total_earnings = 'total_earnings'
        start_date_string = '2012-12-31 23:59:59.59'
        end_date_string = '2013-01-07 23:59:59.59'

        total_earnings_of_each_vendor_for_first_seven_days_of_january = (
            self.trip_fare_df
            .withColumn(column_date, f.date_format(self.trip_fare_df[columns.pickup_datetime], 'yyyy-MM-dd'))
            .filter(f.col(column_date).between(start_date_string, end_date_string))
            .orderBy(columns.vendor_id, column_date)
            .groupBy(columns.vendor_id, column_date)
            .agg(f.sum(columns.total_amount).alias(column_total_earnings))
        )
        return total_earnings_of_each_vendor_for_first_seven_days_of_january

    def driver_of_each_day(self):
        """ Driver who received the biggest amount of tips for each day
        (tips are considered received when the trip is over).

        Returns:
            dataframe with columns:
            date, hack_licence, vendor_id, tips_sum.
        """
        column_date = 'date'
        column_tips_sum = 'tips_sum'
        column_max_tips_sum = 'max_tips_sum'
        join_column_names = [columns.vendor_id, columns.medallion, columns.hack_license, columns.pickup_datetime]
        joined_df = self.trip_fare_df.join(self.trip_data_df, join_column_names, 'inner')
        drivers = (
            joined_df.withColumn('date', f.date_format(joined_df[columns.dropoff_datetime], 'yyyy-MM-dd'))
            .groupBy(columns.vendor_id, columns.hack_license, column_date)
            .agg(f.sum(columns.tip_amount).alias(column_tips_sum))
            .orderBy(column_date, f.desc(column_tips_sum))
            .withColumn(column_max_tips_sum, f.max(f.col(column_tips_sum))
                        .over(Window.partitionBy(column_date)).alias(column_max_tips_sum))
            .filter(f.col(column_max_tips_sum) == f.col(column_tips_sum))
            .select(column_date, columns.hack_license, columns.vendor_id, column_tips_sum)
        )
        return drivers

    def price_per_second_of_drive_for_each_vendor(self):
        """ Average price per second of drive for each vendor.

        Returns:
            dataframe with columns:
            vendor_id, average_fare_per_second
        """
        column_sum_fare_amount = 'sum_fare_amount'
        column_sum_trip_time_in_secs = 'sum_trip_time_in_secs'
        column_average_fare_per_second = 'average_fare_per_second'
        join_column_names = [columns.vendor_id, columns.medallion, columns.hack_license, columns.pickup_datetime]
        joined_df = self.trip_fare_df.join(self.trip_data_df, join_column_names, 'inner')
        price_per_second_of_drive_for_each_vendor = (
            joined_df.groupBy('vendor_id')
            .agg(f.sum(columns.fare_amount).alias(column_sum_fare_amount),
                 f.sum(columns.trip_time_in_secs).alias(column_sum_trip_time_in_secs))
            .withColumn(column_average_fare_per_second,
                        f.col(column_sum_fare_amount) / f.col(column_sum_trip_time_in_secs))
            .select(columns.vendor_id, column_average_fare_per_second)
        )
        return price_per_second_of_drive_for_each_vendor

    def top_vendor_for_each_payment_type(self):
        """ Vendor who received the biggest amount of money for each payment type.

        Returns:
            dataframe with columns:
            payment_type, vendor_id, sum_total_amount.
        """
        column_sum_total_amount = 'sum_total_amount'
        column_max_for_payment_type = 'max_for_payment_type'
        top_vendor_for_each_payment_type = (
            self.trip_fare_df.groupBy(columns.vendor_id, columns.payment_type)
            .agg(f.sum(columns.total_amount).alias(column_sum_total_amount))
            .orderBy(columns.payment_type, f.desc(column_sum_total_amount))
            .withColumn(column_max_for_payment_type,
                        f.max(f.col(column_sum_total_amount))
                        .over(Window.partitionBy(columns.payment_type)))
            .filter(f.col(column_sum_total_amount) == f.col(column_max_for_payment_type))
            .select(columns.payment_type, columns.vendor_id, column_sum_total_amount)
        )
        return top_vendor_for_each_payment_type

    def top_five_drivers_with_greatest_sum_of_time_in_trip(self):
        """ Top 5 drivers with greatest sum of time spent in trips.

        Returns:
            dataframe with columns:
            vendor_id, hack_license, sum_trip_time_in_secs
        """
        column_sum_trip_time_in_secs = 'sum_trip_time_in_secs'
        top_five_drivers_with_greatest_sum_of_time_in_trip = (
            self.trip_data_df.groupBy(columns.vendor_id, columns.hack_license)
            .agg(f.sum(f.col(columns.trip_time_in_secs)).alias(column_sum_trip_time_in_secs))
            .orderBy(f.desc(column_sum_trip_time_in_secs))
        ).limit(5)

        return top_five_drivers_with_greatest_sum_of_time_in_trip

    def most_popular_payment_type(self):
        """
        Calculates the most popular payment type.

        Returns:
            DataFrame: A DataFrame containing only one row with the most popular payment type.
        """

        dataframe = (
            self.trip_fare_df.groupBy(columns.payment_type)
            .count()
            .orderBy('count', ascending=False)
            .limit(1)
        )

        return dataframe

    def highest_fare_amount(self):
        """
        Calculates the highest fare when vendor is VTS.

        Returns:
            DataFrame: A DataFrame containing only one row with the highest fare amount for VTS.
        """

        dataframe = (
            self.trip_fare_df.filter(f.col(columns.vendor_id) == 'VTS')
            .orderBy(columns.fare_amount, ascending=False)
            .limit(1)
        )

        return dataframe

    def top_total_amount(self):
        """
        Calculates the top 10 total_amount values for drivers when passengers count > 5.

        Returns:
            DataFrame: A DataFrame containing 10 rows with biggest total_amount values for drivers
            when passengers count > 5.
        """
        dataframe = (
            self.trip_fare_df.join(self.trip_data_df, [columns.medallion, columns.hack_license,
                                                       columns.pickup_datetime], 'inner')
            .filter(f.col(columns.passenger_count) > 5)
            .groupBy(columns.medallion, columns.hack_license, columns.passenger_count)
            .agg(f.max(columns.total_amount))
            .orderBy(f.col(f'max({columns.total_amount})'), ascending=False)
            .limit(10)
        )

        return dataframe

    def total_revenue_per_day(self):
        """
        Calculates the total revenue for each day of the week, categorized by payment type.

        Returns:
            DataFrame: A DataFrame with columns: 'pickup_datetime', 'payment_type', 'total_amount',
            and 'total_revenue_per_day'.
        """

        dataframe = self.trip_fare_df.withColumn('day_num', f.dayofweek(columns.pickup_datetime))

        window_spec = (
            Window.partitionBy(
                f.col('day_num'),
                f.col(columns.payment_type)
            ).orderBy(f.col('day_num'))
        )

        dataframe = dataframe.withColumn('total_revenue_per_day', f.sum(f.col(columns.total_amount)).over(window_spec))

        return dataframe

    def tip_percentage(self):
        """
        Calculates percentage of tip to total_amount if payment type not cash.
        Returns:
            DataFrame: A DataFrame with new column tips_percentages and only rides which were paid not in cash.
        """
        window_spec = Window.partitionBy(columns.medallion, columns.hack_license, columns.pickup_datetime)

        dataframe = self.trip_fare_df.filter(f.col(columns.payment_type) != 'CSH')
        dataframe = dataframe.withColumn('tips_percetages',
                                         (f.sum(columns.tip_amount).over(window_spec) /
                                          f.sum(columns.total_amount).over(window_spec)) * 100)

        return dataframe

    def avg_trip_duration(self):
        """
        Calculates the average trip duration for different rate codes.
        Returns:
            DataFrame: A DataFrame grouped by rate codes and found avg trip duration time for them
        """
        dataframe = (
            self.trip_data_df
            .filter(f.col(columns.rate_code).isNotNull())
            .groupBy(columns.rate_code)
            .agg(
                f.avg(columns.trip_time_in_secs)
                .alias('avg_trip_duration')
            ).orderBy(f.asc(columns.rate_code))
        )

        return dataframe


import django_filters
from teachers.models import Teacher

class TeacherFilter(django_filters.FilterSet):
    class Meta:
        model = Teacher
        fields = {
            'age': ['gte', 'lte', 'exact'],
            'first_name': ['icontains'],
            'last_name': ['icontains'],
            'occupation': ['icontains']
        }


import requests
import json
from PIL import Image, ImageTk
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time
import os
from bs4 import BeautifulSoup
import tkinter as tk
import io

# Custom Exceptions Start
class invalidInputInfo(Exception):
    pass
class clearList(Exception):
    pass
class checkFaild(Exception):
    pass
class siteUnreachable(Exception):
    pass
class screenshotSelectedElementError(Exception):
    pass
class imageCropError(Exception):
    pass
class displayError(Exception):
    pass
# Custom Exceptions End

# Player API Start
def playerUUIDgui():
    homeUnpack()
    # Frame init
    mapiot.geometry('1000x600')
    canvasFrame = tk.Frame(mapiot)
    infoFrame = tk.Frame(mapiot)
    # one time search
    def startThisFunc():
        # Clear previous canvas in frame
        try:
            for skinC in canvasFrame.winfo_children():
                skinC.destroy()
        except:
            pass
        # get info from gui
        uI = usrInput.get()
        # processing
        try:
            # Info processing
            getInfo = playerAPI(uI)
            outBlock.set(getInfo[0])
            # image processing
            url = str("https://minecraftskinstealer.com/api/v1/skin/render/fullbody/" + getInfo[1] + "/700")
            skinImage = ImageTk.PhotoImage(Image.open(io.BytesIO(requests.get(url).content)))
            skinCanvas = tk.Label(canvasFrame, image=skinImage, bg="white")
            skinCanvas.image = skinImage
            skinCanvas.pack()
        except invalidInputInfo:
            outBlock.set("Invalid Info")
        except Exception:
            outBlock.set("Something went wrong")
    # dynamic info init
    outBlock = tk.StringVar()
    # Default Image init
    defaultImageUrl = "https://upload.wikimedia.org/wikipedia/en/5/51/Minecraft_cover.png"
    skinImage = ImageTk.PhotoImage(Image.open(io.BytesIO(requests.get(defaultImageUrl).content)))
    skinCanvas = tk.Label(canvasFrame, image=skinImage, bg="white")
    skinCanvas.image = skinImage
    skinCanvas.pack()
    canvasFrame.pack()
    # button init
    outLable = tk.Label(infoFrame, textvariable=outBlock, font=('Arial', 14))
    outLable.pack()
    usrInput = tk.Entry(infoFrame, show=None, font=('Arial', 14))
    usrInput.pack()
    startIt = tk.Button(infoFrame, text = 'Search', command=startThisFunc)
    startIt.pack()
    infoFrame.pack()
    # exit init
    def fucExit():
        homePack()
        try:
            infoFrame.pack_forget()
            canvasFrame.destroy()
        except:
            outBlock.set("Something went wrong")
    buttonExit = tk.Button(infoFrame, text = 'Back to home', command=fucExit)
    buttonExit.pack()
def formatUUID(uuid):
    outLst = [alphabit for alphabit in uuid if alphabit != "-"]
    return "".join(outLst)
def testUUID(uuid):
    fullURL = "https://api.minetools.eu/profile/" + uuid
    content = requests.get(url=fullURL)
    result = json.loads(content.text)
    try:
        if str(result["decoded"]) == "None":
            return False
        else:
            return True
    except:
        return False
def playerAPI(infoIn):
    toolDict = {
            "MoJangAPI": "https://api.mojang.com/user/profiles/",
            # "MineToolsEU": "https://api.minetools.eu/profile/"
        }
    if testUUID(infoIn) is False:
        raise invalidInputInfo()
    for tool in toolDict.keys():
        if tool == "MoJangAPI":
            infoNeeded = formatUUID(infoIn)
            FullURL = toolDict[tool] + infoNeeded + "/names"
            content = requests.get(url=FullURL)
            nameLst = json.loads(content.text)
            if len(nameLst) > 1:
                infoA = nameLst[-1]["name"]
                previousName = []
                for name in nameLst[:-1]:
                    previousName.append(name["name"])
                infoB = "Used IDs: " + "; ".join(previousName)
            if len(nameLst) == 1:
                infoA = nameLst[0]["name"]
    returnLst = []
    returnLst.append(str("-=" * 15))
    returnLst.append(str("Current ID: " + infoA))
    returnLst.append(infoB)
    returnLst.append(str("-=" * 15))
    return "\n".join(returnLst), infoA
# Player API End

# Server API Start
def serverAPIgui():
    homeUnpack()
    def startThisFunc():
        uI = usrInputIP.get()
        uI2 = usrInputPort.get()
        try:
            outBlock.set(serverAPI(uI, uI2))
        except invalidInputInfo:
            outBlock.set("Invalid Info")
    outBlock = tk.StringVar()
    outBlock.set("Ip in upper box \nport in lower box \ntype 0 indicate default port")
    outLable = tk.Label(mapiot, textvariable=outBlock, font=('Arial', 14))
    outLable.pack()
    usrInputIP = tk.Entry(mapiot, show=None, font=('Arial', 14))
    usrInputIP.pack()
    usrInputPort = tk.Entry(mapiot, show=None, font=('Arial', 14))
    usrInputPort.pack()
    startIt = tk.Button(mapiot, text = 'Search', command=startThisFunc)
    startIt.pack()
    def fucExit():
        homePack()
        buttonExit.pack_forget()
        usrInputIP.pack_forget()
        usrInputPort.pack_forget()
        startIt.pack_forget()
        outLable.pack_forget()
    buttonExit = tk.Button(mapiot, text = 'Back to home', command=fucExit)
    buttonExit.pack()
def minecraftColorcodeTranslate(letter):
    mcFontDict = {
        "DARK_RED": ["\u00A74", "&4"],
        "RED": ["\u00A7c", "&c"],
        "GOLD": ["\u00A76", "&6"],
        "YELLOW": ["\u00A7e", "&e"],
        "DARK_GREEN": ["\u00A72", "&2"],
        "GREEN": ["\u00A7a", "&a"],
        "AQUA": ["\u00A7b", "&b"],
        "DARK_AQUA": ["\u00A73", "&3"],
        "DARK_BLUE": ["\u00A71", "&1"],
        "BLUE": ["\u00A79", "&9"],
        "LIGHT_PURPLE": ["\u00A7d", "&d"],
        "DARK_PURPLE": ["\u00A75", "&5"],
        "WHITE": ["\u00A7f", "&f"],
        "GRAY": ["\u00A77", "&7"],
        "DARK_GRAY": ["\u00A78", "&8"],
        "BLACK": ["\u00A70", "&0"],
        "FONT_RESET": ["\u00A7r", "&r"],
        "FONT_BOLD": ["\u00A7l", "&l"],
        "FONT_ITALIC": ["\u00A7o", "&o"],
        "FONT_UNDERLINE": ["\u00A7n", "&n"],
        "FONT_STRIKE": ["\u00A7m", "&m"]
    }
    for colorCodes in mcFontDict.keys():
        letter = letter.replace(mcFontDict[colorCodes][0], mcFontDict[colorCodes][1])
    letter = letter.replace(">>>", ">>>")
    return letter
def serverAPI(infoIn, gamePort):
    toolDict = {
        "mcsrvstat": "https://api.mcsrvstat.us/2/",
        "mcapi": "https://mcapi.us/server/status?ip=",
    }
    dumpLst = []
    outLst = []
    def getConent(fullURL):
        content = requests.get(url=fullURL)
        formated = json.loads(content.text)
        dumpLst.append([tool, formated])
    try:
        if int(gamePort) == 0:
            for tool in toolDict.keys():
                fullURL = toolDict[tool] + infoIn
                getConent(fullURL)
        else:
            for tool in toolDict.keys():
                fullURL = toolDict[tool] + infoIn + "&port=" + gamePort
                getConent(fullURL)
    except:
        raise invalidInputInfo
    if dumpLst[0][1]["online"] == True:
        outLst.append(str("-=" * 15))
        outLst.append("Stat: Serving")
        outLst.append(f"Ping: {int(dumpLst[1][1]['duration']) / 1000000:.2f} ms")
        outLst.append(f"IP:{dumpLst[0][1]['hostname']} ({dumpLst[0][1]['ip']})")
        outLst.append(f'Port: {dumpLst[0][1]["port"]}')
        try:
            outLst.append(f'Motd Line A: {minecraftColorcodeTranslate(dumpLst[0][1]["motd"]["clean"][0]).strip()}')
        except:
            outLst.append(f'Motd Line A: NoInfo')
        try:
            outLst.append(f'Motd Line B: {minecraftColorcodeTranslate(dumpLst[0][1]["motd"]["clean"][1]).strip()}')
        except:
            outLst.append(f'Motd Line B: NoInfo')
        outLst.append(f"Players: {dumpLst[0][1]['players']['online']} / {dumpLst[0][1]['players']['max']}")
        outLst.append(str("-=" * 15))
    else:
        outLst.append(str("-=" * 15))
        outLst.append(f"IP:{dumpLst[0][1]['hostname']} ({dumpLst[0][1]['ip']})")
        outLst.append("Stat: Down")
        outLst.append(str("-=" * 15))
    return "\n".join(outLst)
# Server API End

# Slime Chunck Finder Start
def slimeCFgui():
    homeUnpack()
    mapiot.geometry('1000x600')
    slimeImgFrame = tk.Frame(mapiot)
    slimeImgFrame.pack()
    infoFrame = tk.Frame(mapiot)
    infoFrame.pack()

    def startSearch():
        try:
            try:
                for slimeImg in slimeImgFrame.winfo_children():
                    slimeImg.destroy()
            except:
                pass
            try:
                slimeFilePath = slimeChunckFinder(seedInputEntry.get(), xLocateEntry.get(), yLocateEntry.get())
                slimeImageCall = tk.PhotoImage(file=slimeFilePath)
                slimeImageDisplay = tk.Label(slimeImgFrame, image=slimeImageCall)
                slimeImageDisplay.image = slimeImageCall
                slimeImageDisplay.pack()
            except:
                raise displayError
        except checkFaild:
            errorTextVar.set("checkFaild")
        except siteUnreachable:
            errorTextVar.set("siteUnreachable")
        except screenshotSelectedElementError:
            errorTextVar.set("screenshotSelectedElementError")
        except imageCropError:
            errorTextVar.set("imageCropError")
        except displayError:
            errorTextVar.set("displayError")

    errorTextVar = tk.StringVar()
    errorTextVar.set("First Line: Minecraft Seed \nSecond Line: X Location \nThird Line: Y Location")
    errorNoticeBlock = tk.Label(infoFrame, textvariable=errorTextVar, font=('Arial', 14))
    errorNoticeBlock.pack()
    seedInputEntry = tk.Entry(infoFrame, show=None, font=('Arial', 14))
    seedInputEntry.pack()
    xLocateEntry = tk.Entry(infoFrame, show=None, font=('Arial', 14))
    xLocateEntry.pack()
    yLocateEntry = tk.Entry(infoFrame, show=None, font=('Arial', 14))
    yLocateEntry.pack()
    searchStartButton = tk.Button(infoFrame, text="Search 5x5 Chunks", command=startSearch)
    searchStartButton.pack()

    def exitSearch():
        infoFrame.pack_forget()
        slimeImgFrame.pack_forget()
        homePack()
    exitButton = tk.Button(infoFrame, text = 'Back to home', command=exitSearch)
    exitButton.pack()
def slimeChunckFinder(seedInput, locationX, locationY):
    baseURL = "http://mineatlas.com/?levelName=Random&seed="
    uselessArg = [
        "&mapZoom=18",
        "&pos=",
        "&Player=true",
        "&Spawn=true",
        "&Likely+Villages=false",
        "&Ocean+Monuments=false",
        "&Jungle+Temples=false",
        "&Desert+Temples=false",
        "&Witch+Huts=false",
        "&Slime+Chunks=true"
    ]
    otherAttri = ''.join(uselessArg)
    try:
        driver = visitSite(baseURL + seedInput + locationX + locationY + otherAttri)
    except:
        raise siteUnreachable
    webXPATH = '/html/body/div/div[2]/div[1]/div[2]'
    try:
        slimeCanvas = driver.find_element(By.XPATH,webXPATH)
    except:
        raise checkFaild
    try:
        slimeFilePath = os.path.expandvars('$HOME') + "/Downloads/mapiot"
        if not os.path.exists(slimeFilePath):
            os.makedirs(slimeFilePath)
        slimeFile = slimeFilePath + "/slimeChunks.png"
        slimeCanvas.screenshot(slimeFile)
    except:
        raise screenshotSelectedElementError
    driver.quit()
    try:
        slimeCanvasScreenShot = Image.open(slimeFile)
        originalWidth, originalHeight = slimeCanvasScreenShot.size
        width = originalWidth / 2 - 60
        top = originalWidth / 2 - 60
        right = originalHeight / 2 + 60
        bottom = originalHeight / 2 + 60
        slimeResult = slimeCanvasScreenShot.crop((width, top, right, bottom))
        slimeResult.save(slimeFile)
        return slimeFile
    except:
        raise imageCropError

# Slime Chunck Finder End

# Major Bug Checker Start
def majorBugGUI():
    textBlockA = tk.Label(mapiot, text = 'This may take seconds to load, pls wait', font=('Arial', 14))
    textBlockA.pack()
    homeUnpack()
    textBlockB = tk.Listbox(mapiot, yscrollcommand = scrollB.set, font=('Arial', 14), height=10, width=50)
    for eachEr in checkMajorBug():
        textBlockB.insert("end", eachEr + "\n")
    textBlockB.pack()
    # Finish loading
    textBlockA.pack_forget()
    def fucExit():
        homePack()
        buttonExit.pack_forget()
        textBlockB.pack_forget()
    buttonExit = tk.Button(mapiot, text = 'Back to home', command=fucExit)
    buttonExit.pack()
def checkMajorBug():
    mojangBugURL = "https://bugs.mojang.com/issues/"
    jqlArg = "?jql=project%20%3D%20MC%20AND%20status%20%3D%20%22In%20Progress%22%20ORDER%20BY%20votes%20DESC%2C%20updated%20DESC"
    mojangBugReportURL = mojangBugURL + jqlArg
    siteXPATH = '//*[@id="main"]/div/div[2]/div/div/div/div/div/div[1]/div[1]/div/div[1]/div[2]/div/ol'
    driver = visitSite(mojangBugReportURL)
    inProgressBugLst = driver.find_element(By.XPATH,siteXPATH)
    lstHTML = inProgressBugLst.get_attribute('innerHTML')
    bfObject = BeautifulSoup(str(lstHTML), features="lxml")
    preBugLst = bfObject.find_all('li')
    guiDisplay = []
    for preBug in preBugLst:
        guiDisplay.append(str("━" * 70))
        guiDisplay.append(f"\t[{preBug.get('data-key')}] \t{preBug.get('title')}")
    driver.quit()
    return guiDisplay
# Major Bug Checker End

# Spigot Resource Checker Start
def spigotCheckerGUI():
    homeUnpack()
    processLst = []
    def inCheck(usrIn):
        try:
            testA = usrIn.find("-")
        except:
            raise invalidInputInfo
        if len(usrIn) < 3:
            raise invalidInputInfo
        if usrIn == "clear":
            raise clearList
        return usrIn
    def addToProcessLst():
        try:
            processLst.append(inCheck(usrInputId.get()))
            outBlock.set("\n".join(processLst))
        except invalidInputInfo:
            outBlock.set("Invalid Resource Info")
        except clearList:
            for i in range(len(processLst)):
                processLst.pop(0)
            outBlock.set("Cleared List")
    def startThisFunc():
        try:
            outBlock.set(spigotResourceChecker(processLst))
        except invalidInputInfo:
            outBlock.set("Invalid Info")
    def seeList():
        outBlock.set("\n".join(processLst))
    # Display
    outBlock = tk.StringVar()
    outBlock.set("type in the format of <spigotID>[dash]<version>, click add")
    outLable = tk.Label(mapiot, textvariable=outBlock, font=('Arial', 14))
    outLable.pack()
    usrInputId = tk.Entry(mapiot, show=None, font=('Arial', 14))
    usrInputId.pack()
    addTrigger = tk.Button(mapiot, text = 'Add to List', command=addToProcessLst)
    addTrigger.pack()
    curLst = tk.Button(mapiot, text = 'Current List', command=seeList)
    curLst.pack()
    startIt = tk.Button(mapiot, text = 'Check', command=startThisFunc)
    startIt.pack()
    # Exit Button
    def fucExit():
        homePack()
        buttonExit.pack_forget()
        usrInputId.pack_forget()
        addTrigger.pack_forget()
        startIt.pack_forget()
        outLable.pack_forget()
        curLst.pack_forget()
    buttonExit = tk.Button(mapiot, text = 'Back to home', command=fucExit)
    buttonExit.pack()
def spigotResourceChecker(resDetail):
    returnLst = []
    try:
        for spigotPlugin in resDetail:
            versionPosition = spigotPlugin.find("-")
            versionId = spigotPlugin[versionPosition+1:]
            resId = spigotPlugin[:versionPosition]
            fullURL = "https://api.spigotmc.org/legacy/update.php?resource=" + resId
            spigotAPI = requests.get(url=fullURL)
            if str(spigotAPI.text) != versionId:
                yesOrNoUTD = "X"
            else:
                yesOrNoUTD = "√"
            returnLst.append(str("-" * 70))
            returnLst.append(f"Resource ID: {resId} | Your Version: {versionId} | Newest: {str(spigotAPI.text)} | Uptodate: {yesOrNoUTD}")
        return "\n".join(returnLst)
    except:
        return "empty list"
# Spigot Resource Checker Stop

# Environment Start
def chromeSetting():
    options = webdriver.ChromeOptions()
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-gpu')
    options.add_argument('window-size=1920x1080')
    options.add_argument('--hide-scrollbars')
    options.add_argument('--headless')
    options.add_experimental_option("excludeSwitches", ["ignore-certificate-errors", "enable-automation"])
    return options
def visitSite(FullURL):
    driver = webdriver.Chrome(options=options, service=Service(ChromeDriverManager().install()))
    driver.get(FullURL)
    time.sleep(2)
    return driver
def excecutePath():
    preworkPath = "C:/Program Files/mapiot" if os.name=='nt' else str(os.environ['HOME'] + "/Downloads/mapiot")
    if not os.path.exists(preworkPath):
        os.makedirs(preworkPath)
    return preworkPath + "/"
# Environment End

# GUI Start
def homeUnpack():
    # Frame Unpack
    homeMenu.pack_forget()
def homePack():
    # Frame Pack, init window size
    mapiot.geometry('500x300')
    homeMenu.pack()
# GUI End

# Script Start
if __name__ == '__main__':
    # Headless Browser Init
    options = chromeSetting()
    # GUI Init
    mapiot = tk.Tk()
    mapiot.title("Mapiot v1.0.0")
    mapiot.geometry('500x300')
    scrollB= tk.Scrollbar(mapiot)
    scrollB.pack(side="right", fill="y")
    # Buttons
    homeMenu = tk.Frame(mapiot)
    nameDisplay = tk.Label(homeMenu, text = 'Thank you for using Mapiot.', font=('Arial', 20), width=30, height=2)
    buttonUUID = tk.Button(homeMenu, text = 'Player UUID Checker', command=playerUUIDgui)
    buttonMajorBugGUI = tk.Button(homeMenu, text = 'Mojang Bugs Checker', command=majorBugGUI)
    buttonServerAPI = tk.Button(homeMenu, text = 'Server Stats Checker', command=serverAPIgui)
    buttonSpigotChecker = tk.Button(homeMenu, text = 'Spigot Resources Checker', command=spigotCheckerGUI)
    slimeChecker = tk.Button(homeMenu, text = 'Slime Chunk Finder', command=slimeCFgui)
    buttonQuit = tk.Button(homeMenu, text = 'Quit', command=quit)
    # Button Install
    nameDisplay.pack()
    buttonMajorBugGUI.pack()
    buttonUUID.pack()
    buttonServerAPI.pack()
    buttonSpigotChecker.pack()
    slimeChecker.pack()
    buttonQuit.pack()
    # Frame Install
    homePack()
    # GUI Loop
    mapiot.mainloop()


#!/usr/bin/env python

"""
https://www.codewars.com/kata/520b9d2ad5c005041100000f/python
"""
import ipdb
import pytest

"""
pig_it('Pig latin is cool') # igPay atinlay siay oolcay
pig_it('Hello world !')     # elloHay orldway !
"""

# from codewars solution
def pig_it(text):
    lst = text.split()
    return ' '.join( [word[1:] + word[:1] + 'ay' if word.isalpha() else word for word in lst])

@pytest.mark.parametrize("input_, expected",
                         [('Pig latin is cool', 'igPay atinlay siay oolcay'),
                          ('Hello world !', 'elloHay orldway !')])
def test_solution(input_, expected):
    assert move_first_letter_of_each_word_to_the_end(input_) == expected

def move_first_letter_of_each_word_to_the_end(text: str)-> str:
    return ' '.join([f"{i[1:]}{i[0]}ay" if i not in ['!',',','?'] else i for i in text.split() ])
    # out_str= ''
    # for i in s.split():
    #     #print(i)
    #     out_str += i[1:]
    # return out_str

if __name__ == '__main__':
    print(move_first_letter_of_each_word_to_the_end('Pig latin is cool'))
    print(move_first_letter_of_each_word_to_the_end('Hello world !'))


# MIT License
#
# Copyright (c) 2023 Andrey Zhdanov (rivitna)
# https://github.com/rivitna
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

import sys
import io
import struct
import zlib

MARKER = b'\xFE\x09\x00\x00\x8D'

def decompress_data(data):
    """Decompress data"""
    decompress = zlib.decompressobj(-zlib.MAX_WBITS)
    inflated = decompress.decompress(data)
    inflated += decompress.flush()
    return inflated

#
# Main
#
if len(sys.argv) != 2:
    print('Usage: '+ sys.argv[0] + ' filename')
    sys.exit(0)

filename = sys.argv[1]

with io.open(filename, 'rb') as f:
    file_data = f.read()

pos = 0

# Find configuration data
while True:

    pos = file_data.find(MARKER, pos)
    if pos < 0:
        break
    pos += len(MARKER)

    # stsfld
    if file_data[pos + 4] != 0x80:
        continue
    cfg_data_token, = struct.unpack_from('<L', file_data, pos + 5)

    # ldsfld
    if file_data[pos + 9] != 0x7E:
        continue
    token, = struct.unpack_from('<L', file_data, pos + 10)
    if token == cfg_data_token:
        pos += 9
        break

if pos < 0:
    print('Error: Configuration data not found.')
    sys.exit(1)

print('cfg data position: %08X' % pos)
print('cfg data token: 0x%08X' % cfg_data_token)

cfg_data_dict = {}

# Parse IL code
while pos + 16 <= len(file_data):

    # ldsfld
    if file_data[pos] != 0x7E:
        break
    pos += 1
    token, = struct.unpack_from('<L', file_data, pos)
    if token != cfg_data_token:
        break
    pos += 4

    # ldc.i4
    if file_data[pos] != 0x20:
        break
    pos += 1
    idx, = struct.unpack_from('<L', file_data, pos)
    if cfg_data_dict.get(idx) is not None:
        break
    pos += 4

    # ldc.i4, stelem.i1
    if (file_data[pos] != 0x20) or (file_data[pos + 5] != 0x9C):
        break
    pos += 1
    val, = struct.unpack_from('<L', file_data, pos)
    if val > 255:
        break
    pos += 5

    cfg_data_dict[idx] = val

    # skip nop
    if file_data[pos] == 0:
        pos += 1

pack_cfg_data_size = max(cfg_data_dict.keys()) + 1
print('compressed cfg data size: %d' % pack_cfg_data_size)

pack_cfg_data = b''
for i in range(pack_cfg_data_size):
    val = cfg_data_dict.get(i)
    if val is None:
        break
    pack_cfg_data += bytes([val])

cfg_data = decompress_data(pack_cfg_data)
print('cfg data size: %d' % len(cfg_data))

cfg_filename = filename + '.cfg'
with io.open(cfg_filename, 'wb') as f:
    f.write(cfg_data)


class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        same_tasks = {}
        for c in tasks:
            if c in same_tasks:
                same_tasks[c] += 1
            else:
                same_tasks[c] = 1
        max_heap = []
        for i in same_tasks.values():
            heapq.heappush(max_heap, -i)
        cycles = 0
        while len(max_heap) != 0:
            temp = []
            for i in range(n+1):
                if len(max_heap) != 0:
                    temp.append(-heapq.heappop(max_heap))
            for i in temp:
                i -= 1
                if i > 0: heapq.heappush(max_heap, -i)
            if len(max_heap) == 0:
                cycles += len(temp)
            else:
                cycles += n + 1
        return cycles


# https://cloud.google.com/pubsub/docs/create-topic#create_a_topic
# https://cloud.google.com/python/docs/reference/pubsub/latest

# %%
from google.cloud import pubsub_v1

# TODO(developer)
project_id = "podact-topic-extractor"
topic_id = "your-topic-id"

publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_id)

topic = publisher.create_topic(request={"name": topic_path})

print(f"Created topic: {topic.name}")
# %%
# When you delete a topic, its subscriptions are not deleted.
publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_id)

publisher.delete_topic(request={"topic": topic_path})

print(f"Topic deleted: {topic_path}")
# %%
from google.cloud import pubsub_v1

publisher = pubsub_v1.PublisherClient()
project_path = f"projects/{project_id}"

for topic in publisher.list_topics(request={"project": project_path}):
    print(topic)
# %%
"""Publishes multiple messages to a Pub/Sub topic with an error handler."""
from concurrent import futures
from typing import Callable

from google.cloud import pubsub_v1

publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_id)
publish_futures = []

def get_callback(
    publish_future: pubsub_v1.publisher.futures.Future, data: str
) -> Callable[[pubsub_v1.publisher.futures.Future], None]:
    def callback(publish_future: pubsub_v1.publisher.futures.Future) -> None:
        try:
            # Wait 60 seconds for the publish call to succeed.
            print(publish_future.result(timeout=60))
        except futures.TimeoutError:
            print(f"Publishing {data} timed out.")

    return callback

for i in range(10):
    data = str(i)
    # When you publish a message, the client returns a future.
    publish_future = publisher.publish(topic_path, data.encode("utf-8"))
    # Non-blocking. Publish failures are handled in the callback function.
    publish_future.add_done_callback(get_callback(publish_future, data))
    publish_futures.append(publish_future)

# Wait for all the publish futures to resolve before exiting.
futures.wait(publish_futures, return_when=futures.ALL_COMPLETED)

print(f"Published messages with error handler to {topic_path}.")
# %%
from google.cloud import pubsub_v1

# TODO(developer)
# project_id = "your-project-id"
# topic_id = "your-topic-id"

publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_id)

for n in range(1, 10):
    data_str = f"Message number {n}"
    # Data must be a bytestring
    data = data_str.encode("utf-8")
    # Add two attributes, origin and username, to the message
    future = publisher.publish(
        topic_path, data, origin="python-sample", username="gcp"
    )
    print(future.result())

print(f"Published messages with custom attributes to {topic_path}.")
# %%
from google.cloud import pubsub_v1

# TODO(developer): Choose an existing topic.
# project_id = "your-project-id"
# topic_id = "your-topic-id"

publisher_options = pubsub_v1.types.PublisherOptions(enable_message_ordering=True)
# Sending messages to the same region ensures they are received in order
# even when multiple publishers are used.
client_options = {"api_endpoint": "us-east1-pubsub.googleapis.com:443"}
publisher = pubsub_v1.PublisherClient(
    publisher_options=publisher_options, client_options=client_options
)
# The `topic_path` method creates a fully qualified identifier
# in the form `projects/{project_id}/topics/{topic_id}`
topic_path = publisher.topic_path(project_id, topic_id)

for message in [
    ("message1", "key1"),
    ("message2", "key2"),
    ("message3", "key1"),
    ("message4", "key2"),
]:
    # Data must be a bytestring
    data = message[0].encode("utf-8")
    ordering_key = message[1]
    # When you publish a message, the client returns a future.
    future = publisher.publish(topic_path, data=data, ordering_key=ordering_key)
    print(future.result())

print(f"Published messages with ordering keys to {topic_path}.")
# %%
from concurrent import futures

from google.cloud import pubsub_v1

# TODO(developer)
# project_id = "your-project-id"
# topic_id = "your-topic-id"

# Configure the batch to publish as soon as there are 10 messages
# or 1 KiB of data, or 1 second has passed.
batch_settings = pubsub_v1.types.BatchSettings(
    max_messages=10,  # default 100
    max_bytes=1024,  # default 1 MB
    max_latency=1,  # default 10 ms
)
publisher = pubsub_v1.PublisherClient(batch_settings)
topic_path = publisher.topic_path(project_id, topic_id)
publish_futures = []

# Resolve the publish future in a separate thread.
def callback(future: pubsub_v1.publisher.futures.Future) -> None:
    message_id = future.result()
    print(message_id)

for n in range(1, 10):
    data_str = f"Message number {n}"
    # Data must be a bytestring
    data = data_str.encode("utf-8")
    publish_future = publisher.publish(topic_path, data)
    # Non-blocking. Allow the publisher client to batch multiple messages.
    publish_future.add_done_callback(callback)
    publish_futures.append(publish_future)

futures.wait(publish_futures, return_when=futures.ALL_COMPLETED)

print(f"Published messages with batch settings to {topic_path}.")
# %%
from google import api_core
from google.cloud import pubsub_v1

# %%
# TODO(developer)
# project_id = "your-project-id"
# topic_id = "your-topic-id"

# Configure the retry settings. Defaults shown in comments are values applied
# by the library by default, instead of default values in the Retry object.
custom_retry = api_core.retry.Retry(
    initial=0.250,  # seconds (default: 0.1)
    maximum=90.0,  # seconds (default: 60.0)
    multiplier=1.45,  # default: 1.3
    deadline=300.0,  # seconds (default: 60.0)
    predicate=api_core.retry.if_exception_type(
        api_core.exceptions.Aborted,
        api_core.exceptions.DeadlineExceeded,
        api_core.exceptions.InternalServerError,
        api_core.exceptions.ResourceExhausted,
        api_core.exceptions.ServiceUnavailable,
        api_core.exceptions.Unknown,
        api_core.exceptions.Cancelled,
    ),
)

publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_id)

for n in range(1, 10):
    data_str = f"Message number {n}"
    # Data must be a bytestring
    data = data_str.encode("utf-8")
    future = publisher.publish(topic=topic_path, data=data, retry=custom_retry)
    print(future.result())

print(f"Published messages with retry settings to {topic_path}.")


'''

概念：一种保存数据的格式
作用：可以保存本地的json文件，也可以将json串进行传输，通常将json称为轻量级的传输方式

json文件组成
{}  代表对象(字典)
[]  代表列表
:   代表键值对
,   分隔两个部分

'''
import json

jsonStr = '''{
		"rate": "8.0",
		"cover_x": 1400,
		"title": "我是余欢水",
		"url": "https:\/\/movie.douban.com\/subject\/33442331\/",
		"playable": true,
		"cover": "https://img3.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2574916002.jpg",
		"id": "33442331",
		"cover_y": 2139,
		"is_new": false
	}
'''

#将json格式的字符串转为python数据类型的对象
#将json格式数据转换为字典，方便取值
jsonData = json.loads(jsonStr)
print(jsonData)
print(type(jsonData))
print(type(jsonStr))

jsonData2 = {
		"rate": "8.0",
		"cover_x": 1400,
		"title": "我是余欢水",
		"url": "https:\/\/movie.douban.com\/subject\/33442331\/",
		"playable": True,
		"cover": "https://img3.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2574916002.jpg",
		"id": "33442331",
		"cover_y": 2139,
		"is_new": False
	}

#将python数据类型的对象转换为json格式的字符串
jsonStr2 = json.dumps(jsonData2)
print(jsonStr2)
print(type(jsonData2))
print(type(jsonStr2))

正则表达式与爬虫/3、爬虫/7、json数据解析.py