import time
import os
import re
import pyautogui
import telegram_send
import pyperclip

from platform import platform
from modules.constants import p, c, locate_and_click, Entry, open_network, \
    ImageNotFoundError, enter_maintenance
from modules.filereader import read_excel

if "Windows" in platform():
    from pywinauto.keyboard import send_keys
else:
    def send_keys(t): return pyautogui.write(t, interval=0.05)

# So here's how the bot is going to need to work
# Step 1: Read the Excel sheet
# Do this by opening every page in the spreadsheet using pandas
# and then read row by row to assemble a list of entries

xls = os.path.exists(c("stock.xls"))
xlsx = os.path.exists(c("stock.xlsx"))

if not xls and not xlsx:
    pyautogui.alert(
        "Please save the stock spreadsheet in the folder bot_data "
        "with the name stock.xls or stock.xlsx, and try again."
    )
    exit()

name = "stock.xls" if xls else "stock.xlsx"
try:
    entries = read_excel(c(name))
except ValueError as e:
    pyautogui.alert(str(e))
    exit()

if not entries:
    pyautogui.alert("No entries found in the spreadsheet.")
    exit()

print(entries)

# Step 2: Read the Word document containing the sign data to extract notes.
# Do this by opening the Word document and then reading the text
# using textract, then read the notes by splitting on double newlines
# and add each note to the entry with the corresponding index number
# Also, check that the prices match up

def locate_price(sentence: str) -> str:
    for word in sentence.split():
        if "$" in word:
            return word[word.index("$") + 1:]

    return ""

if not os.path.exists(c("signs.txt")):
    pyautogui.alert(
        "Please copy the signs information "
        "and save it in the folder bot_data/ as signs.txt."
    )
    exit()

try:
    with open(c("signs.txt"), 'r') as f:
        signs = f.read().strip().split("\n\n")
except UnicodeDecodeError as e:
    if "codec can't decode byte 0x93" in str(e):
        with open(c("signs.txt"), 'r', encoding="latin-1") as f:
            signs = f.read().strip()

        signs = signs.replace("0x93", '"').replace('?', '" ')
        signs = signs.split("\n\n")
    else:
        pyautogui.alert(
            "The signs file looks weird... I can't read it! :("
        )
        raise e

signs_map = {}
for s in signs:
    lines = s.splitlines()
    i = re.search(r"\d+", lines[0]).group(0)
    t = '\n'.join(lines[1:])
    signs_map[i] = t.split("   ")[0].strip()

requested = set()
for entry in entries:
    if entry.index in signs_map:
        # Check that the price is contained in the text
        if entry.price not in signs_map[entry.index].replace(',', ''):
            print(f"Price mismatch for {entry.index}")
            guess = locate_price(signs_map[entry.index])
            if guess != "":
                res = pyautogui.confirm(
                    f"Price mismatch for {entry.index} with "
                    f'sign text "{signs_map[entry.index]}". '
                    f"Is the price {guess}? "
                    "Click OK to accept, "
                    "or Cancel to keep the existing price. "
                    "If you would like to enter in a new price, "
                    'click "New Price".',
                    buttons=["OK", "Cancel", "New Price"],
                )

                if res == "New Price":
                    new_price = pyautogui.prompt("Please enter the new price:")
                    if new_price is None:
                        pass

                    entry.price = new_price.replace(',', '')

                elif res == "OK":
                    entry.price = guess.replace(',', '')

                else:
                    exit()

            else:
                # This probably shouldn't be happening, but we can check it
                res = pyautogui.confirm(
                    f"The sign for {entry.index} does not contain a price. "
                    "Please check the sign and enter in the price manually."
                    "Click OK to continue, or Cancel to stop the program.",
                )

                if res == "Cancel":
                    exit()

        entry.notes = signs_map[entry.index]

    elif entry.index not in requested:
        print(f"No sign data for {entry.index}")
        res = pyautogui.confirm(
            text=f"No sign data for {entry.index}. Continue?",
            title="CougarBot"
        )

        if res == "Cancel":
            exit()

        requested.add(entry.index)
        continue

print(entries[0])

# Step 3: Loop through the entries and insert the data into the system,
# checking during insertion that the index number does not already exist
# either by looking for greyed out text or checking the find menu

def enter_stock(entry: Entry, first=False) -> bool:
    """
    Enter the data from an entry into the Cougar Mountain system.

    Returns True if the entry was successfully entered, False otherwise.
    """

    # Enter the stock number
    if first:
        for _ in range(5):
            try:
                locate_and_click(p("number.png"))
                break
            except pyautogui.ImageNotFoundException as e:
                time.sleep(0.5)

        else:
            raise e

    time.sleep(0.5)
    send_keys(entry.code + "{TAB}", pause=0.1)
    tabs = 5
    on_hand = pyautogui.locateOnScreen(p("on_hand.png"))
    if on_hand is None:
        first = False
        tabs -= 1

    # Enter the location and description
    if first:
        tabs -= 2
        send_keys(entry.location + "{TAB 2}I1{TAB %d}" % tabs)
    else:
        send_keys(entry.location + "{TAB %d}" % tabs)

    # Now try to find the notes box. If it is not on screen,
    # then there must be a duplicate
    time.sleep(1)
    dup = pyautogui.locateOnScreen(p("notes.png"))
    if dup is None:
        # Duplicate detected.
        locate_and_click(p("cancel.png"))
        enter_maintenance()
        return False

    pyperclip.copy(entry.description)
    send_keys("^v")
    consign_grey = pyautogui.locateOnScreen(p("consignment_grey.png"))
    if entry.cnor.strip() not in ["nan", ''] and consign_grey is not None:
        send_keys("{TAB}" + entry.cnor, pause=0.1)
        try:
            locate_and_click(p("serialized.png"))
        except ImageNotFoundError:
            # Looks like this consignment item is already in the system
            locate_and_click(p("cancel.png"))
            locate_and_click(p("no.png"))
            locate_and_click(p("file.png"))
            locate_and_click(p("main_menu.png"))
            enter_maintenance()
            return False

        locate_and_click(p("consignment.png"), pos='r')
        send_keys("{TAB 2}" + entry.cost)
    elif entry.cnor in ["nan", '']:
        send_keys("{TAB}{BACK}")

    locate_and_click(p("breaklist.png"), pos="br", stretch=3)
    send_keys(entry.price + "{TAB}")

    # lc = pyautogui.locateOnScreen(p("last_cost.png"))
    # pyautogui.click(lc.left + lc.width + 2, lc.top + 0.5 * lc.height)
    locate_and_click(p("last_cost.png"), pos='r', stretch=2)
    send_keys(entry.cost + "{TAB}")

    locate_and_click(p("sales.png"))
    locate_and_click(p("storing.png"), pos='r', stretch=2)
    send_keys(entry.price + "{TAB}")

    locate_and_click(p("notes.png"))
    pyperclip.copy(entry.notes)
    send_keys("^v")

    locate_and_click(p("save.png"))
    time.sleep(2)

    with open("finished.txt", 'a') as f:
        f.write(entry.code + "\n")

    return True

if not os.path.exists("finished.txt"):
    with open("finished.txt", "w") as f:
        f.write("")

try:
    open_network()
    enter_maintenance()
    with open("finished.txt", "r") as f:
        finished = f.readlines()

    already_entered = []
    first = True
    for i, entry in enumerate(entries):
        if entry.code not in finished:
            if not enter_stock(entry, first=first):
                already_entered.append(entry)
                first = True
            else:
                first = False

except ImageNotFoundError as e:
    pyautogui.alert(f"Could not find {e}!")
    exit()

# Step 4: Send a Telegram message to Mom when the program is done
codes = '\n'.join([e.code for e in already_entered])
message = (
    f"Finished entering {len(entries) - len(already_entered)} entries"
    f" into Cougar Mountain. {len(already_entered)} duplicates found:"
    f"\n{codes}"
)
telegram_send.send(messages=[message])

# Step 5: Profit


from django.conf.urls import url
from inkscape.url_utils import url_tree

from .views import DownloadRedirect, ReleaseView, PlatformList,\
                   ReleasePlatformStage, ReleasePlatformView, PlatformView

version_urls = url_tree( # pylint: disable=invalid-name
    r'^(?P<version>[\w\+\.-]+)/',
    url('^$', ReleaseView.as_view(), name="release"),
    url(r'^platforms/$', PlatformList.as_view(), name="platforms"),

    # We don't use url_tree here because .+ competes with /dl/
    url('^(?P<platform>.+)/dl/$', ReleasePlatformView.as_view(), name="download"),
    url('^(?P<platform>.+)/$', ReleasePlatformStage.as_view(), name="platform"),
)

urlpatterns = [ # pylint: disable=invalid-name
    url(r'^$', DownloadRedirect.as_view(), name="download"),
    url('^all/(?P<platform>.+)/$', PlatformView.as_view(), name="platform"),
    url_tree(
        r'^(?P<project>[\w\-\.]+)-',
        version_urls,
    ),
    version_urls,
]


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import datetime
import random
import seaborn as sns; sns.set()
from dateutil.relativedelta import relativedelta

from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error

from especial_functions import augmentation_reduction

def exploiting_words(data):
    items_vector = list(data.item_name.unique())
    items_vector = pd.DataFrame({'item_name': items_vector})
    items_vector['lenstr'] = items_vector.item_name.str.len() ## this can be a feature
    items_vector['NWords'] = items_vector.item_name.str.split().str.len()
    items_vector['ItemWord0'] = items_vector.item_name.str.split().str.get(0)
    items_vector['ItemWord_1'] = items_vector.item_name.str.split().str.get(-1)

    def my_tokengen(data, columnx):
        unique_words = list()
        for string in  list(data[columnx].values):
            for word in string.split():
                unique_words.append(word)
        unique_words = set(unique_words)
        unique_words = list(unique_words)
        unique_words.sort()
        number = [idx[0] +1 for idx in enumerate(unique_words)]
        map_word = {x:y for x,y in zip(unique_words, number)}

        data[columnx] = data[columnx].map(map_word)
        return data

    items_vector = my_tokengen(items_vector, 'ItemWord0')
    items_vector = my_tokengen(items_vector, 'ItemWord_1')
    items_vector = items_vector.merge(data[['item_name','item_id']], on = 'item_name', how = 'left').drop(columns = 'item_name')

    return {'item_name_explo':items_vector}

def cleaning_shop_categs(shops, categories):
    shops[['shop_comp1','shop_comp2']] = shops.shop_name.str.split(' "',expand=True,)
    shops['shop_comp1'] = shops.shop_comp1.replace({r'\([^)]*\)' : ''}, regex=True)
    shops['shop_comp1'] = shops['shop_comp1'].str.split(' ').str[0]

    shops['shop_comp2'] = shops.shop_comp2.replace({r'\([^)]*\)' : ''}, regex=True)
    shops['shop_comp2'] = shops.shop_comp2.replace({'"' :''}, regex=True)

    categories[['categ_comp1','categ_comp2']] = categories.item_category_name.str.split(" - ",expand=True,)
    categories['categ_comp1'] = categories.categ_comp1.replace({r'\([^)]*\)' : ''}, regex=True)
    categories['categ_comp2'] = categories.categ_comp2.replace({r'\([^)]*\)' : ''}, regex=True)

    return shops, categories

def indexing_shop_categs(data, column):
    my_map = dict()
    for index, value in enumerate(data[column].unique()):
        my_map[value] = index
    data[f'map_{column}'] = data[column].map(my_map)
    return data

def merge_basic(train_data, test_data, test_how = 'inner'):
    train_merge = train_data.merge(items, on = 'item_id', how = 'left')\
        .merge(categories, on = 'item_category_id', how = 'left')\
        .merge(shops, on = 'shop_id', how = 'left')\
        .merge(test_data, on = ['shop_id','item_id'], how = test_how)\

    train_merge['SaleDate'] = pd.to_datetime(train_merge['date'], format = '%d.%m.%Y')

    return train_merge

def convert_bronze(data, train = False, test_order = 34):
    if train:
        data['date'] = pd.to_datetime(data['date'], format = '%d.%m.%Y')

    data['SaleDateMY'] = data['date'].dt.to_period('M').dt.to_timestamp()
    data['item_cnt_day'] = data['item_cnt_day'].clip(0,20)

    data = data.groupby(['SaleDateMY', 'date_block_num', 'shop_id', 'item_id'],
                                ).agg( Sale = ('item_cnt_day', 'sum'),
                                   item_price = ('item_price', 'mean'))
    data = data.reset_index().rename(columns = {'SaleDateMY':'Date'})

    data['min_date'] = data.groupby(['shop_id', 'item_id'])['Date'].transform('min')

    ### completing with 0 items with 0 in no dates

    map_aggg = data[['Date','item_id','shop_id','min_date','Sale']]\
        .pivot_table(index = ['item_id','shop_id','min_date'], columns = ['Date'], values = 'Sale', aggfunc = 'count').reset_index()
    map_aggg = map_aggg.melt(id_vars=['item_id','shop_id','min_date'], value_vars = map_aggg.columns[2:],  var_name='Date', value_name='Nothing')
    map_aggg = map_aggg[map_aggg.Date >= map_aggg.min_date]
    map_aggg = map_aggg[['item_id','shop_id','Date']]

    group_data = map_aggg.merge(data, on = ['item_id','shop_id','Date'], how = 'left').fillna(0)
    group_data['Sale'] = group_data['Sale'].clip(0,20)

    del map_aggg

    if train:
        ### get a map of the dates
        date_list =[group_data.Date.min()]
        date = group_data.Date.min()
        max_date = group_data.Date.max()
        while date <= max_date:
            date = date_list[-1] + relativedelta(months=1)
            date_list.append(date)
        order_list = range(1,len(date_list)+1)
        date_map = pd.DataFrame({'Date':date_list, 'Order':order_list})
        group_data = group_data.merge(date_map, on = 'Date', how = 'left')
    group_data = group_data.drop(columns = ['date_block_num', 'min_date'])
    return group_data

def merge_basic(train_data, items, items_feature ,categories, shops):
    train_merge = train_data.merge(items, on = 'item_id', how = 'left')
    train_merge = train_merge.merge(items_feature, on = 'item_id', how = 'left')
    train_merge = train_merge.merge(categories, on = 'item_category_id', how = 'left')
    train_merge = train_merge.merge(shops, on = 'shop_id', how = 'left')

    return train_merge

def supagg(data):
    data['lit'] = data.groupby(['item_id'])['shop_id'].transform('nunique')
    agge = data.groupby(['item_id','shop_id']).agg(lit = ('lit','max')).reset_index()
    return agge

def  reduce_sample(data, frac, single = False ):
    if single:
        data = data.groupby(['item_id','shop_id']).agg(lit = ('lit','max')).reset_index().sample(frac = frac, random_state = 123)
    else:
        data = data.sample(frac = frac, random_state = 123)
        data = data[['item_id','shop_id','lit']]
    return data

def get_val_window(data, date, items_to_drop_all):
    val_window_1 = data[data.Date == date ]
    already_found = val_window_1.shopitem.unique()

    val_window_2 = data[ (data.Date != date) & (data.shopitem.isin(items_to_drop_all)) ]
    val_window_2['Order'] = val_window_2.groupby(['shop_id','item_id']).cumcount() + 1
    val_window_2 = val_window_2[val_window_2.Order == 1]
    val_window_2['Date'] = date
    val_window_2 = val_window_2[~val_window_2.shopitem.isin(already_found)]
    val_window_2['Sale'] = 0
    print(len(val_window_1),len(val_window_2))
    val_window = pd.concat([val_window_1,val_window_2])
    return val_window

def inversed_scale(scaler, data, target_name, y_pred = np.array([])):
    data_new = data.copy()
    if y_pred.any():
        data_new[target_name] = y_pred

    unscaled_data = scaler.inverse_transform(data_new)
    unscaled_data = pd.DataFrame(unscaled_data, columns = data_new.columns)
    return unscaled_data

def consolidation_prediction(data, prediction ):
    pdf_plot = data.copy()
    #pdf_plot['PredictedVar'] = prediction
    pdf_plot['PredSale'] = prediction
    #pdf_plot['RealValueVar'] = pdf_plot.L0M_L1M
    #pdf_plot = pdf_plot.assign(PredSale = pdf_plot.PredictedVar + pdf_plot.SaleL1M)
    pdf_plot = pdf_plot[['Date','shop_id','item_id','Sale','PredSale']]
    return pdf_plot

def plot_prediction(dfplot):
    fig, axs = plt.subplots(nrows=1, ncols=1, figsize=(10,8))

    ax = sns.scatterplot(ax = axs, x="Sale", y="PredSale", data=dfplot)

    x0, x1 = axs.get_xlim()
    y0, y1 = axs.get_ylim()
    lims = [max(x0, y0), min(x1, y1)]
    axs.plot(lims, lims, '-r')

    axs.set(xlabel='Real Sale', ylabel='Predicted Sale', title='Scatter plot of variation predictions')

def preparing_to_predict(val_data, train_data):
    val_data_go = val_data.copy()
    val_data_go['date_block_num'] = np.nan
    val_data_go['shopitem'] = val_data_go['shop_id'].astype('str') + '-' + val_data_go['item_id'].astype('str')
    val_data_go = val_data_go[train_data.columns]
    items = val_data_go.shopitem.unique()
    result_data = pd.concat([train_data[train_data.shopitem.isin(items)],val_data_go])
    return result_data

def plot_metrics(data):
    resultdict = {'Date':list(), 'RMSE':list()}
    for datex in data.Date.unique():
        df = data[data.Date == datex]
        rmse = (mean_squared_error(df.Sale, df.PredSale))**(1/2)
        resultdict['Date'].append(datex)
        resultdict['RMSE'].append(rmse)
    result = pd.DataFrame(resultdict)
    return result
#########################################################
### The following functions follow a fixed sstructure ###
#########################################################

"""test_dates = [datetime.datetime(2014, 11, 1), datetime.datetime(2015, 4, 1), datetime.datetime(2015, 5, 1), datetime.datetime(2015, 6, 1), datetime.datetime(2015, 7, 1), datetime.datetime(2015, 8, 1),
datetime.datetime(2015, 9, 1), datetime.datetime(2015, 10, 1),]

to_drop_columns = ['Date']
ids_columns = ['item_category_id',
       'ItemIdPart1','ItemIdPart2', 'CategIdItem3A', 'CategIdItem3B'] #'item_id'
numericals = [ 'Quarter', 'OrderGot', 'countItemPossitives',
       'countItemZero', 'L1maxLev1Item', 'L2maxLev1Item', 'L3maxLev1Item',
       'L4maxLev1Item', 'L5maxLev1Item', 'lenstr', 'NWords',
       'Word0', 'Word_1', 'L1maxLev1Shop', 'meanLaggedVars',
       'item_id_mean_LastMonth', 'item_id_max_LastMonth',
       'item_id_mean_LastOrder', 'item_id_max_LastOrder', 'meanL1Price',
       'SpaceOrder'] + ids_columns

categoricals = ['SpaceOrderActivator', 'CategoryL1Item']
my_features = to_drop_columns + numericals + categoricals
my_target = 'Sale'

my_columns_to_drops = ['Quarter','CategoryL1Item_C', 'CategoryL1Item_A', 'CategoryL1Item_D',
       'SpaceOrderActivator_A', 'CategIdItem3B', 'ItemIdPart1',
       'CategIdItem3A']

categoricals_features = ['SpaceOrderActivator_A', 'SpaceOrderActivator_B',
                         'CategoryL1Item_A','CategoryL1Item_B','CategoryL1Item_C','CategoryL1Item_D']

if len(categoricals) != 0:
    final_features = categoricals_features +  numericals
else:
    final_features = numericals
final_features = [x for x in final_features if x not in my_columns_to_drops]

"""

def My_ML_prediction_on_test( data_dict , dates_vector, model, features_dict, zero_vector, frac = 0.80):

    numericals = features_dict['numericals']
    categoricals = features_dict['categoricals']
    my_columns_to_drops = features_dict['features_to_drop']
    my_target = features_dict['my_target']

    data_result = list()

    for datex in dates_vector:
        datex_str = datex.strftime('%Y-%m-%d')
        #### reading files
        train_selection = data_dict['Train Data'][datex_str]
        val_selection = data_dict['Validation Data'][datex_str]
        train_augmented = augmentation_reduction(train_selection, fracs = zero_vector)

        train_scaled, my_scaler = scaler(train_augmented, numericals, my_target, scaler=None, drop_columns = my_columns_to_drops)
        #train_scaled = train_augmented[ numericals + [my_target] ]
        if len(categoricals) != 0:
            train_dummies = pd.get_dummies(train_augmented[categoricals])
            train_scaled = pd.concat([train_dummies,train_scaled],axis = 1)

        if len(categoricals) != 0:
            final_features = list(train_dummies.columns) +  numericals
        else:
            final_features = numericals
        final_features = [x for x in final_features if x not in my_columns_to_drops]

        train_scaled_sampled = train_scaled.sample(frac = frac, random_state = 12489)

        ### ML train
        X_train = train_scaled_sampled[final_features]
        Y_train = train_scaled_sampled[my_target]
        model.fit(X_train, Y_train)

        ### Test data prepa
        val_scaled = scaler(val_selection, numericals, my_target, scaler=my_scaler,drop_columns = my_columns_to_drops )

        if len(categoricals) != 0:
            val_dummies = pd.get_dummies(val_selection[categoricals])
            val_scaled_full = pd.concat([val_dummies,val_scaled],axis = 1)
        else:
            val_scaled_full = val_scaled

        X_val = val_scaled_full[final_features]

        ## Prediction
        Y_pred = model.predict(X_val)

        ## Saving Result
        predicted_val = inversed_scale(scaler = my_scaler, data = val_scaled, target_name = my_target, y_pred = Y_pred)
        my_lm_plot = consolidation_prediction(data = val_selection, prediction = predicted_val.Sale.values)
        data_result.append(my_lm_plot)
        #print(f'the prediction over the {datex} data is done')

    return pd.concat(data_result)

def scaler(dataset, features, target, scaler=None, drop_columns = []):
    if scaler:
        features = [x for x in features if x not in drop_columns]
        df = dataset[features + [target]]
        df_scaled = scaler.transform(df)
        dataset_scaled = pd.DataFrame(df_scaled, columns = df.columns, index = df.index)
        return dataset_scaled

    else:
        features = [x for x in features if x not in drop_columns]
        df  = dataset[features + [target]]
        scaler = MinMaxScaler()
        scaler.fit(df)

        dataset_scaled = scaler.transform(df)
        dataset_scaled = pd.DataFrame(dataset_scaled, columns = df.columns, index = df.index)
        return dataset_scaled, scaler

def consolidated_metrics_MSE(data):
    labels = list()
    RMSEs = list()
    for i in range(len(data['machine label'])):
        label = data['machine label'][i]
        result_detail = data['machine result'][i]
        pred = result_detail['Sale']
        real = result_detail['PredSale']
        rmse = mean_squared_error(real,pred)**(1/2)
        labels.append(label)
        RMSEs.append(rmse)

    return pd.DataFrame({'machine':labels,'RMSE': RMSEs}).sort_values('RMSE')

def consolidated_plot_metrics(data):
    list_dfplot = list()
    for i in range(len(data['machine label'])):
        label = data['machine label'][i]
        result_detail = data['machine result'][i]
        df_plot = plot_metrics(result_detail)
        df_plot['machine'] = label
        list_dfplot.append(df_plot)

    return pd.concat(list_dfplot)

def get_splited_data(dates_vector):
    train_data = dict()
    validation_data = dict()

    for datex in dates_vector:
        datex_str = datex.strftime('%Y-%m-%d')
        train_selection = pd.read_csv(f'generated_datasets/data_{datex_str}/train_selection.csv')
        val_selection = pd.read_csv(f'generated_datasets/data_{datex_str}/val_selection.csv')

        train_data[datex_str] = train_selection
        validation_data[datex_str] = val_selection
        print(datex_str)
    return { 'Train Data' : train_data,
            'Validation Data': validation_data}


#!/usr/bin/env python

#=======================================================================================================================
# PyClone
# Author : Andrew Roth
#=======================================================================================================================
import pyclone.run as run

import argparse

def main():
    parser = argparse.ArgumentParser(prog='PyClone')

    parser.add_argument('--version', action='version', version='PyClone-0.13.1')

    subparsers = parser.add_subparsers()

#----------------------------------------------------------------------------------------------------------------------
    setup_analysis_parser = subparsers.add_parser(
        'setup_analysis',
        help='''Setup a config file and mutations files for a PyClone analysis.'''
    )

    _setup_setup_analysis_parser(setup_analysis_parser)

#----------------------------------------------------------------------------------------------------------------------
    run_analysis_parser = subparsers.add_parser(
        'run_analysis',
        help='''Run an MCMC sampler to sample from the posterior of the PyClone model.'''
    )

    _setup_run_analysis_parser(run_analysis_parser)

#----------------------------------------------------------------------------------------------------------------------
    analysis_pipeline_parser = subparsers.add_parser(
        'run_analysis_pipeline',
        help='''Run a full PyClone analysis.'''
    )

    _setup_analysis_pipeline_parser(analysis_pipeline_parser)

#----------------------------------------------------------------------------------------------------------------------
    build_prior_parser = subparsers.add_parser(
        'build_mutations_file',
        help='''Build a YAML format file with mutation data and states prior to be used for PyClone analysis.'''
    )

    _setup_build_prior_parser(build_prior_parser)

#----------------------------------------------------------------------------------------------------------------------
    plot_clusters_parser = subparsers.add_parser(
        'plot_clusters',
        help='''Plot features of the clusters.'''
    )

    _setup_cluster_plot_parser(plot_clusters_parser)

#----------------------------------------------------------------------------------------------------------------------
    plot_loci_parser = subparsers.add_parser(
        'plot_loci',
        help='''Plot features of the loci.'''
    )

    _setup_loci_plot_parser(plot_loci_parser)

#----------------------------------------------------------------------------------------------------------------------
    build_table_parser = subparsers.add_parser(
        'build_table',
        help='''Build results table which contains cluster ids and (mean) cellular prevalence estimates.''')

    _setup_build_table_parser(build_table_parser)

#----------------------------------------------------------------------------------------------------------------------
    args = parser.parse_args()

    args.func(args)

def _setup_run_analysis_parser(parser):

    _add_config_file_args(parser)

    _add_seed_args(parser)

    parser.set_defaults(func=run.run_analysis)

def _setup_setup_analysis_parser(parser):

    parser.add_argument(
        '--in_files',
        nargs='+',
        required=True,
        help='''Space delimited list of tsv format files with copy number and allele count information. See
        build_mutations_file command for information.'''
    )

    parser.add_argument(
        '--working_dir',
        required=True,
        help='''Path of directory where analysis pipeline files will be placed.'''
    )

    parser.add_argument(
        '--tumour_contents',
        nargs='+',
        type=float,
        default=None,
        help='''Space delimited list of tumour contents. Should match the order of --in_files. If not given tumour
        content is assumed to 1.0 in all samples.'''
    )

    parser.add_argument(
        '--samples',
        nargs='+', default=None, help='''Space delimited list of sample names. Should be in the same order as
        --in_files. If not set sample name will be inferred from file names and ordering in plots will be arbitrary.''')

    parser.add_argument(
        '--config_extras_file',
        required=False,
        help='''Path to configuration file with extra parameters used for analysis.'''
    )

    parser.add_argument(
        '--density',
        choices=['pyclone_binomial', 'pyclone_beta_binomial'],
        default='pyclone_beta_binomial',
        help='''Emission density for the model. Default is pyclone_beta_binomial.'''
    )

    _add_init_method_args(parser)

    parser.add_argument(
        '--num_iters',
        default=10000,
        type=int,
        help='''Number of iterations of the MCMC sampler to perform. Default is 10,000.'''
    )

    _add_prior_args(parser)

    parser.set_defaults(func=run.setup_analysis)

def _setup_analysis_pipeline_parser(parser):

    _setup_setup_analysis_parser(parser)

    _add_post_process_args(parser)

    _add_seed_args(parser)

    parser.add_argument(
        '--plot_file_format',
        default='pdf',
        choices=['pdf', 'svg'],
        help='''File format for plots. Default is pdf.'''
    )

    _add_max_clusters_args(parser)

    _add_mesh_size_args(parser)

    _add_min_cluster_size_args(parser)

    parser.set_defaults(func=run.run_analysis_pipeline)

def _setup_build_prior_parser(parser):

    parser.add_argument(
        '--in_file',
        required=True,
        help='''Path to tab separated input file. The input file should have header and the following columns:
        mutation_id, ref_counts, var_counts, normal_cn, minor_cn, major_cn. Any additional columns will be ignored.
        See examples for format.'''
    )

    parser.add_argument(
        '--out_file',
        required=True,
        help='''Path where YAML formatted PyClone input file will be written.'''
    )

    _add_prior_args(parser)

    parser.set_defaults(func=run.build_mutations_file)

def _setup_build_table_parser(parser):

    _add_config_file_args(parser)

    parser.add_argument(
        '--out_file',
        required=True,
        help='''Path where table will be written in tsv format.'''
    )

    parser.add_argument(
        '--table_type',
        choices=['cluster', 'loci', 'old_style'],
        required=True,
        help='''Build a table of results. Choices are: `cluster` for cluster specific information; `loci` for loci
        specific information; `old_style` matches the 0.12.x PyClone output.'''
    )

    _add_max_clusters_args(parser)

    _add_mesh_size_args(parser)

    _add_post_process_args(parser)

    parser.set_defaults(func=run.build_table)

def _setup_cluster_plot_parser(parser):

    _add_config_file_args(parser)

    _add_plot_out_file_args(parser)

    parser.add_argument(
        '--plot_type',
        choices=['density', 'parallel_coordinates', 'scatter'],
        default='density',
        required=True,
        help='''Determines which style of plot will be done.'''
    )

    _add_post_process_args(parser)

    _add_max_clusters_args(parser)

    _add_mesh_size_args(parser)

    _add_min_cluster_size_args(parser)

    parser.add_argument(
        '--samples',
        default=None,
        nargs='+',
        help='''Samples to plot and order to plot them in.'''
    )

    parser.set_defaults(func=run.cluster_plot)

def _setup_loci_plot_parser(parser):

    _add_config_file_args(parser)

    _add_plot_out_file_args(parser)

    parser.add_argument(
        '--plot_type',
        choices=[
            'density',
            'parallel_coordinates',
            'scatter',
            'similarity_matrix',
            'vaf_parallel_coordinates',
            'vaf_scatter'
        ],
        default='density',
        required=True,
        help='''Determines which style of plot will be done.'''
    )

    _add_post_process_args(parser)

    _add_min_cluster_size_args(parser)

    _add_max_clusters_args(parser)

    parser.add_argument(
        '--samples',
        default=None,
        nargs='+',
        help='''Samples to plot and order to plot them in.'''
    )

    parser.set_defaults(func=run.loci_plot)

#=======================================================================================================================
# Common args
#=======================================================================================================================

def _add_config_file_args(parser):

    parser.add_argument(
        '--config_file',
        required=True,
        help='''Path to configuration file used for analysis.'''
    )

def _add_plot_out_file_args(parser):

    parser.add_argument(
        '--plot_file',
        required=True,
        help='Path to file where plot will be saved. Format can be controlled by changing file extension.'
    )

def _add_seed_args(parser):

    parser.add_argument(
        '--seed',
        default=None,
        type=int,
        help='''Set random seed so results can be reproduced. By default a random seed is chosen.'''
    )

def _add_prior_args(parser):

    parser.add_argument(
        '--prior',
        choices=['major_copy_number', 'parental_copy_number', 'total_copy_number'],
        default='major_copy_number',
        help='''Method used to set the possible genotypes. See online help for description. Default is major_copy_number.'''
    )

def _add_post_process_args(parser):

    parser.add_argument(
        '--burnin',
        default=0,
        type=int,
        help='''Number of samples to discard as burning for the MCMC chain. Default is 0.'''
    )

    parser.add_argument(
        '--thin',
        default=1,
        type=int,
        help='''Number of samples to thin MCMC trace. For example if thin=10 every tenth sample after burning will be
        used for inference. Default is 1.'''
    )

def _add_min_cluster_size_args(parser):
    parser.add_argument(
        '--min_cluster_size',
        default=0,
        type=int,
        help='''Clusters with fewer mutations than this value will not be plotted.'''
    )

def _add_mesh_size_args(parser):
    parser.add_argument(
        '--mesh_size',
        default=101,
        type=int,
        help='''Number of points to use for approximating the cluster posteriors. Default is 101.'''
    )

def _add_init_method_args(parser):
    parser.add_argument(
        '--init_method', choices=['connected', 'disconnected'], default='disconnected',
        help='''How to initialise the DP clustering algorithm. `connected` places all data points in one cluster,
        preferred for large datasets. `disconnected places each data point in a separate cluster. Default
        `disconnected`.'''
    )

def _add_max_clusters_args(parser):
    parser.add_argument(
        '--max_clusters', default=None, type=int,
        help='''Maximum number of clusters to consider for post-processing. Note this does not affect the DP sampling
        only the final post-processing steps to get hard cluster assignments.'''
    )

if __name__ == '__main__':
    main()


import sys
import inspect
import os
import datetime
import traceback

this_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
if this_dir not in sys.path:
    sys.path.append(this_dir)

import menu_tools as tools

# constants
_error_print_periodicity = 2.0  # As seconds
_error_signatures_stack_size = 32
# variables
_prev_error_signatures = [""] * _error_signatures_stack_size
_prev_error_index = 0
_prev_time = datetime.datetime.now()
_accumulated_dt = 0.0

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# ////////   P R I V A T E   F U N C T I O N S   ////////
# - - - - - - - - - - - - - - - - - - - - - - - - - - -
# region [ COLLAPSE / EXPAND ]
def _try_print(signature, title, func_name, line_num, filename, comment, suggestion, call_stack):
    if _signatures_stack_has_signature(signature) is False:
        # New error ==> PRINT
        _insert_error_signature(signature)
        print(_create_print_message(title, func_name, line_num, filename, comment, suggestion, call_stack))
        # Reset time-variables here to avoid inconsistent printing
        globals()['_prev_time'] = datetime.datetime.now()
        globals()['_accumulated_dt'] = 0
    else:
        # Error already reported ==> adjust time-variables
        curr_time = datetime.datetime.now()
        dt = curr_time - globals()['_prev_time']
        globals()['_accumulated_dt'] += dt.total_seconds()
        globals()['_prev_time'] = curr_time
        # Timer has passed 'print periodicity' ==> PRINT
        if globals()['_accumulated_dt'] > globals()['_error_print_periodicity']:
            _insert_error_signature(signature)
            print(_create_print_message(title, func_name, line_num, filename, comment, suggestion, call_stack))
            # Reset accumulated time
            globals()['_accumulated_dt'] = 0

def _create_print_message(title, func_name, line_num, filename, comment, suggestion, call_stack):
    result = ""
    result += ("\n" + title[:7] + "\t|FUNCTION NAME: " + func_name)
    result += (" (line " + line_num + " in '" + filename + "')")
    if comment != "":
        result += "\n"
        result += "-" * len(title[:7])
        result += ("\t|COMMENT:" + "\t" + comment)
    if suggestion != "":
        result += ("\n\t|SUGGESTION: " + "\t" + suggestion)
    if call_stack != "":
        result += ("\n" + call_stack)
    return result

def _signatures_stack_has_signature(signature):
    if signature in globals()['_prev_error_signatures']:
        return True
    return False

def _insert_error_signature(signature):
    curr_error_index = globals()['_prev_error_index'] + 1
    if curr_error_index == globals()['_error_signatures_stack_size']:
        curr_error_index = 0
    globals()['_prev_error_signatures'][curr_error_index] = signature
    globals()['_prev_error_index'] = curr_error_index

def _traceback_list_to_array(traceback_list):
    return_array = [[0]*4 for i in range(len(traceback_list))]
    for i in range(len(traceback_list)):
        temp_string = traceback_list[i]
        # Filepath
        temp_string = temp_string.split("\"", 2)
        return_array[i][1] = temp_string[1]
        # Line number
        temp_string = temp_string[2].split("line ", 1)[1].split(",", 1)
        return_array[i][2] = temp_string[0]
        # Function name
        temp_string = temp_string[1].split("in ", 1)[1].split("\n", 1)
        return_array[i][3] = temp_string[0]
        # The actual line of code
        return_array[i][0] = temp_string[1].lstrip()
    return return_array

def _get_call_stack_as_str(call_stack_2d_array, prune_depth, reverse_order=False):
    prune_depth = tools.clamp(prune_depth, 0, len(call_stack_2d_array))
    return_str = "\tCALL STACK:"
    if (prune_depth == len(call_stack_2d_array)):
        return ""  # E A R L Y   E X I T
    else:
        total_depth = len(call_stack_2d_array)
        curr_depth = 0
        # Remove 'prune_depth' number of entries, bot -> top
        for i in range(total_depth - prune_depth):
            if reverse_order:
                index = total_depth-i-1
            else:
                index = i
            return_str += ("\n\t\t")
            if (i != 0):
                return_str += (" "*(curr_depth-1) + "\\" + " ")
            return_str += call_stack_2d_array[index][3]
            return_str += " (line "
            return_str += str(call_stack_2d_array[index][2])
            return_str += " in "
            return_str += os.path.basename(call_stack_2d_array[index][1])
            return_str += ")"
            curr_depth += 1
    return return_str

def _get_call_stack_exception_last_layer():
    return_array = [0]*4
    traceback_as_2d_array = _traceback_list_to_array(traceback.extract_tb(sys.exc_info()[2]).format())
    last_index = len(traceback_as_2d_array) - 1
    for i in range(4):
        return_array[i] = traceback_as_2d_array[last_index][i]
    return return_array

def _get_call_stack_exception(prune_depth=0):
    traceback_as_2d_array = _traceback_list_to_array(traceback.extract_tb(sys.exc_info()[2]).format())
    return _get_call_stack_as_str(traceback_as_2d_array, prune_depth)
# endregion

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# ////////   E X P O R T E D   F U N C T I O N S   ////////
# - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# region [ COLLAPSE / EXPAND ]
def get_this_method_name():
    return inspect.stack()[1][3]

def get_parent_method_name():
    try:
        inspect.stack()[2][3]
    except IndexError as e:
        try_print_except(str(e), "The parent method wasn't found. Perhaps the function calling this passed as a lambda?")
        return "Parent method not found!"
    return inspect.stack()[2][3]

def get_this_method_line_num():
    return inspect.stack()[1][2]

def get_parent_method_line_num():
    try:
        inspect.stack()[2][2]
    except IndexError as e:
        try_print_except(str(e), "The parent method wasn't found. Perhaps the function calling this was passed as a lambda?")
        return "Parent method not found!"
    return inspect.stack()[2][2]

def get_this_method_filename():
    return inspect.stack()[1][1]

def get_parent_method_filename():
    try:
        inspect.stack()[2][2]
    except IndexError as e:
        try_print_except(str(e), "The parent method wasn't found. Perhaps the function calling this was passed as a lambda?")
        return "Parent method not found!"
    return inspect.stack()[2][1]

def try_print_except(comment="", suggestion="", only_filename=False):
    # Append the exception type to the comment
    comment = str(sys.exc_info()[0]).split("'", 2)[1] + " - " + comment
    call_stack_bot_layer_info = _get_call_stack_exception_last_layer()
    func_name = call_stack_bot_layer_info[3]
    line_num = call_stack_bot_layer_info[2]
    if only_filename:
        filename = call_stack_bot_layer_info[1].rsplit("\\", 1)[1]
    else:
        filename = call_stack_bot_layer_info[1]
    call_stack = _get_call_stack_exception()
    signature = func_name + line_num
    _try_print(signature, "EXCEPT", func_name, line_num, filename, comment, suggestion, call_stack)

def force_print_except(comment="", suggestion="", only_filename=False):
    # Append the exception type to the comment
    comment = str(sys.exc_info()[0]).split("'", 2)[1] + " - " + comment
    call_stack_bot_layer_info = _get_call_stack_exception_last_layer()
    func_name = call_stack_bot_layer_info[3]
    line_num = call_stack_bot_layer_info[2]
    if only_filename:
        filename = call_stack_bot_layer_info[1].rsplit("\\", 1)[1]
    else:
        filename = call_stack_bot_layer_info[1]
    call_stack = _get_call_stack_exception()
    print(_create_print_message("EXCEPT", func_name, line_num, filename, comment, suggestion, call_stack))

def try_print(title_short="MSG", comment="", suggestion="", only_filename=False):
    func_name = get_parent_method_name()
    line_num = str(get_parent_method_line_num())
    if only_filename:
        filename = get_parent_method_filename().rsplit("\\", 1)[1]
    else:
        filename = get_parent_method_filename()
    signature = func_name + line_num
    _try_print(signature, title_short, func_name, line_num, filename, comment, suggestion, "")

def force_print(title_short, comment="", suggestion="", only_filename=False):
    func_name = get_parent_method_name()
    line_num = str(get_parent_method_line_num())
    if only_filename:
        filename = get_parent_method_filename().rsplit("\\", 1)[1]
    else:
        filename = get_parent_method_filename()
    print(_create_print_message(title_short, func_name, line_num, filename, comment, suggestion, ""))
# endregion


from llm.openai.model import OpenAIModel
import time
import json
import os
from typing import Optional
from parse import parse_json, flatten

def start_history(system_seed: str, user_seed: str, path: str = "./llm/openai/chat_history.json") -> Optional[int]:
    if os.path.exists(path):
        return None

    print(f"\n\033[33mCreating a new chat history at {path}\033[0m")
    with open(path, 'w') as file:
        json.dump([{"role": "system", "content": system_seed},{"role": "user", "content": user_seed}], file)

    print(f"\n\033[32mCreated a new chat history at {path}\033[0m")

    return 1

def destroy_history(path: str = "./llm/openai/chat_history.json") -> None:
    if os.path.exists(path):
        os.remove(path)
        print(f"\n\033[32mSuccessfully removed chat history at {path}\033[0m")
        return None

    print(f"\n\033[33mNo chat history exists at {path}\033[0m")
    return None

def read_history(path: str = "./llm/openai/chat_history.json") -> Optional[list]:
    if not os.path.exists(path):
        print(f"\n\033[31mNo chat history at {path}\033[0m")
        return None

    with open(path, 'r') as file:
        return json.load(file)

def append_history(messages: list, path: str = "./llm/openai/chat_history.json") -> None:
    chat_history: list = read_history()
    if not chat_history:
        print(f"\n\033[31mFailed to read the chat history at {path}\033[0m")
        return None

    chat_history.extend(messages)

    with open(path, 'w') as file:
        json.dump(chat_history, file)
        return None

def chat(new_message: str, role: str = "user") -> Optional[str]:

    llm = OpenAIModel()

    if not start_history(system_seed="You are an assistant and will answer any questions asked of you", user_seed=new_message):
        append_history([{"role": role, "content": new_message}])

    history = read_history()

    response = llm.get_chat_response(history)

    assistant_response = flatten(parse_json(response, "content"))[0]

    append_history([{"role": "assistant", "content": assistant_response}])

    return assistant_response


"""add avatar in user

Revision ID: 515c73f0bce4
Revises: 4b6909ef6e08
Create Date: 2017-10-13 19:29:35.145905

"""

# revision identifiers, used by Alembic.
revision = '515c73f0bce4'
down_revision = '4b6909ef6e08'

from alembic import op
import sqlalchemy as sa

def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('users', schema=None) as batch_op:
        batch_op.add_column(sa.Column('avatar', sa.Text(), nullable=True))

    ### end Alembic commands ###

def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('users', schema=None) as batch_op:
        batch_op.drop_column('avatar')

    ### end Alembic commands ###


import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np

## Scroll down for the part where your code belongs

class Dataset():
    def __init__(self, n_items, config={}):
        'Initialization'
        default = {
            "seed":0,
            "size":(14,14),
            "channels":1,
            "square_size":(2,2)
        }
        config = {**default,**config}
        self.config = config
        self.n_items = n_items
        self.list_IDs, self.labels, self.seeds = self.__gen_init__()

    def __len__(self):
        'Denotes the total number of samples'
        return len(self.list_IDs)

    def __getitem__(self, index):
        'Generates one sample of data'
        # Select sample
        ID = self.list_IDs[index]

        # Load data and get label
        X, y = self.__gen_sample__(ID)
        return X, y

    def __gen_init__(self):
        'Generates the init data'
        ids = list(range(self.n_items))
        ids = {i:i for i in ids}
        lbs = {i:i for i in ids}
        seeds = np.random.uniform(size=self.n_items)
        seeds = {i:seeds[i] for i in ids}
        return ids, lbs, seeds

    def __gen_sample__(self, ID):
        'Generates a single sample'
        # generate size

        # generate y
        dh = np.random.randint(
            self.config["square_size"][0],
            self.config["square_size"][1]+1
        )
        dw = np.random.randint(
            self.config["square_size"][0],
            self.config["square_size"][1]+1
        )
        h, w = self.config["size"]
        px = int(dw + (w - 2*dw) * int(str(self.seeds[ID])[2:4])/100)
        py = int(dh + (h - 2*dh) * int(str(self.seeds[ID])[4:6])/100)
        # generate X
        X = np.random.rand(*[h, w])*0.9
        X[py:py+dh, px:px+dw] = 1.0
        y = [1, (px+dw/2)/w, (py+dh/2)/h, dw/w, dh/h]
        return X, y

def plot_image(X, y, ax=None):
    if ax is None:
        fig, ax = plt.subplots()
    # plot image
    ax.imshow(X, cmap="gray")
    ax.set_aspect(1)
    ax.set_yticks([i-0.5 for i in range(X.shape[0])], minor='True')
    ax.set_xticks([i-0.5 for i in range(X.shape[1])], minor='True')
    ax.yaxis.grid(True, which='minor')
    ax.xaxis.grid(True, which='minor')
    # plot y
    ax.axvline(x=y[1]*X.shape[1]-0.5, c="r")
    ax.axhline(y=y[2]*X.shape[0]-0.5, c="r")
    return ax

######################################
# YOUR CODE GOES HERE
######################################


from django.urls import path
from .views import PostListView, PostDetailView, PostCreateView, PostUpdateView, PostDeleteView
from . import views

urlpatterns = [
    path('', PostListView.as_view(),name = 'home'),
    path('post/<int:pk>/', PostDetailView.as_view(),name = 'post-detail'),
    path('post/new/', PostCreateView.as_view(),name = 'post-create'),
    path('post/<int:pk>/update/', PostUpdateView.as_view(),name = 'post-update'),
    path('post/<int:pk>/delete/', PostDeleteView.as_view(),name = 'post-delete'),
    path('postjob', views.postjob,name = 'postjob'),
    path('findjob', views.findjob,name = 'findjob'),
    path('about', views.about,name = 'about'),
    path('contact', views.contact,name = 'contact'),
]


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

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('application', '0052_grouplevels_sort_num'),
    ]

    operations = [
        migrations.AlterUniqueTogether(
            name='lessons',
            unique_together=set([('date', 'student', 'group')]),
        ),
    ]


import argparse
from pathlib import Path
from moviepy.editor import VideoFileClip

def extract_audio(video_path, out_path=None):

    # If user does not provide path for audio file, use video path with changed extension
    if out_path is None:
        out_path = Path(video_path).with_suffix('.mp3')

    # If desired audio file already exists, raise exception
    if Path(out_path).is_file():
        raise ValueError(f'File {out_path} already exists')

    video_obj = VideoFileClip(video_path)
    video_obj.audio.write_audiofile(out_path)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('video_path')
    parser.add_argument('--out', required=False)
    args = parser.parse_args()
    extract_audio(args.video_path, args.out)


import traceback
import json
import subprocess

def gen_msg(logger, cmd, fnc=None):
    msg = {}
    if 'kubectl get' in cmd:
        cmd += f' -o json'
    try:
        st, res = subprocess.getstatusoutput(cmd)
        if st != 0:
            logger.error(res)
            msg['error'] = {'code': 400, 'message': res}
        elif fnc:
            obj = json.loads(res)
            msg['success'] = fnc(obj)

    except Exception as e:
        logger.error(traceback.format_exc())
        msg['error'] = {'code': 500, 'message': str(e)}
    return msg

def gen_msg_result_query(logger, result):
    msg = {}
    if result[0] > 0:
        msg['error'] = {
            'code': 400,
            'message': result[1]
        }
    return msg


"""Methods for parsing a build request document.

The primary input for this is a List[List[str]] representing the excel
or csv sheet. This can be obtained by parsing an excel sheet, using the
google sheet API, etc.
"""
import re
from itertools import tee
from typing import Callable
from typing import Dict
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple
from typing import TypeVar
from typing import Union

import pandas as pd

from .cell_value import CellValue
from .exceptions import BuildRequestParsingException
from .exceptions import parsing_location
from .validate import validate_part_list

T = TypeVar("T")

# TODO: parse these keys
class Keys:
    # keys used in the build request document

    Basic_DNA_Parts = "Basic DNA Parts"
    Composite_DNA_Parts = "Composite DNA Parts"
    Collection_Name = "Collection Name"
    Part_Name = "Part Name"
    Description = "Description (Optional)"
    Sequence = "Sequence"
    Length = "length (bp)"
    Source = "Source (Optional)"
    Role = "Role"

##########################
# Utilities
##########################

def _is_nan(x: Union[str, CellValue]) -> bool:
    if x is None:
        return True
    elif x.strip().lower() == "nan":
        return True
    elif x.strip().lower() == "None":
        return True
    elif x.strip() == "":
        return True
    return False

def _dispatch_match_fxn(
    match: Union[Callable, str, re.Pattern]
) -> Callable[[str], bool]:
    """Generate a match function from a string, Callable, or regex pattern."""
    if callable(match):
        _match_fxn = match
    else:

        def _match_fxn(x):
            if isinstance(match, re.Pattern):
                return match.match(x)
            else:
                return match == x

    return _match_fxn

def match_value(match: Union[Callable, str, re.Pattern], value: str) -> bool:
    """Match a string, Callable, or regex pattern with a value."""
    fxn = _dispatch_match_fxn(match)
    return fxn(value)

def find_value(
    values,
    match: Union[Callable, str, re.Pattern],
    only_rows: Optional[List[int]] = None,
    only_cols: Optional[List[int]] = None,
) -> List[Tuple[int, int]]:
    """Return the indices [(int, int), ...] of the cells that match the
    provided matching string, function, regex pattern."""
    found = []

    for r, row in enumerate(values):
        if not only_rows or r in only_rows:
            for c, val in enumerate(row):
                if not only_cols or c in only_cols:
                    if match_value(match, str(val)):
                        found.append((r, c))
    return found

def pairwise(iterable: Iterable[T]) -> Iterable[Tuple[T, T]]:
    """s -> (s0,s1), (s1,s2), (s2, s3), ..."""
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

def _partition(x: List, indices: Union[None, int]) -> List:
    """Partition a list using a list of indices."""
    for a, b in pairwise(indices):
        yield x[a:b]

def cell_is_empty(cell: str):
    return _is_nan(cell)

def row_is_empty(row: List[str]) -> bool:
    """check that a row of string values is empty."""
    return all(cell_is_empty(c) for c in row)

def remove_empties_from_row(values: List[List[str]]) -> List[List[str]]:
    """Remove empty rows."""
    return [row for row in values if not row_is_empty(row)]

def is_square(values: List[List[T]]) -> bool:
    """Check that 2D list is square."""
    row_sizes = [len(r) for r in values]
    return len(set(row_sizes)) == 1

def transpose(values: List[List[T]]) -> List[List[T]]:
    """Try to 'transpose' a list of lists."""
    num_rows = max([len(r) for r in values])
    transposed = [[] for _ in range(num_rows)]
    for r in range(num_rows):
        for row in values:
            if r < len(row):
                transposed[r].append(row[r])
    return transposed

##########################
# Extract sections of
# the build request values
##########################

def extract_meta(values: List[List[CellValue]]) -> List[List[CellValue]]:
    r1 = find_value(values, Keys.Basic_DNA_Parts)[0][0]
    return remove_empties_from_row(values[:r1])

def extract_basic_parts(values: List[List[Union[str, int]]]) -> List[List[CellValue]]:
    r1 = find_value(values, Keys.Basic_DNA_Parts)[0][0]
    r2 = find_value(values, Keys.Composite_DNA_Parts)[0][0]
    return remove_empties_from_row(values[r1:r2])

def extract_composite_parts(
    values: List[List[Union[str, int]]]
) -> List[List[CellValue]]:
    r1 = find_value(values, Keys.Composite_DNA_Parts)[0][0]
    return remove_empties_from_row(values[r1:])

########################
# Parse extracted values
########################

def values_to_json(
    values: List[List[Union[str, int]]],
    key_column: int,
    fill_empty_key_from_above: bool = False,
):
    data = {}
    for row in values:
        key = row[key_column].strip()
        if fill_empty_key_from_above and key == "":
            key = prev_key
        data.setdefault(key, list())
        data[key].append(row[key_column + 1 :])
        prev_key = key
    return data

# TODO: rename this method
def parse_composite_parts(values: List[List[Union[str, int]]]) -> List[Dict]:
    composite = extract_composite_parts(values)

    # locate indices of new "Collections"
    indices = find_value(composite, re.compile(Keys.Collection_Name))
    rows = [None] + [i[0] for i in indices] + [None]

    # partition the values on the indices
    partitioned = list(_partition(composite, rows))[1:]
    parsed_json_arr = []
    for p in partitioned:
        with parsing_location(p[0][0]):
            parsed_json_arr.append(values_to_json(p, 0, True))

    # TODO: Validate the data here. Check for incorrect keys under each heading

    def _make_part_list(parsed_json):
        with parsing_location(parsed_json["Name:"][0][0]):
            names = transpose(parsed_json["Name:"])

        if "Parts:" in parsed_json:
            # TODO: should this be covered by the jsonschema?
            # if 'Part Sequence:' in parsed_json:
            #     with parsing_location(parsed_json['Parts:'][0][0]):
            #         raise BuildRequestParsingException("Both 'Parts:' and 'Part Sequence:' cannot be "
            #                                            "defined for a composite part")
            with parsing_location(parsed_json["Parts:"][0][0]):
                parts = transpose(parsed_json["Parts:"])
            part_type = "composite part"
        # if 'Part Sequence:' in parsed_json:
        #     with parsing_location(parsed_json['Part Sequence:'][0][0]):
        #         parts = transpose(parsed_json['Part Sequence:'])
        #     part_type = 'composite part'
        with parsing_location(parsed_json["Description:"][0][0]):
            descriptions = transpose(parsed_json["Description:"])
        collection_name = parsed_json["Collection Name:"][0][0]

        parts = [[p for p in row if not _is_nan(p)] for row in parts]

        part_list = []
        for _name, _description, _parts in list(zip(names, descriptions, parts)):
            part_list.append(
                {
                    "name": _name[0],
                    "collection": collection_name,
                    "partType": part_type,
                    "description": _description[0],
                    "parts": _parts,
                }
            )
        return part_list

    part_list = []
    for p in parsed_json_arr:
        part_list += _make_part_list(p)

    # remove empty parts (again)
    part_list_old = part_list[:]
    part_list = []
    for part in part_list_old:
        if (
            _is_nan(part["name"])
            and _is_nan(part["description"])
            and part["parts"] in [[], ["nan"], ["none"], ["None"], [""]]
        ):
            continue
        else:
            part_list.append(part)

    return part_list

def _get_and_strip(d: Dict[str, str], k: str, default: T) -> Union[T, str]:
    """Get value from dictionary and strip.

    Else, return default
    """
    if k in d:
        if not isinstance(d[k], str):
            return default
        else:
            cv = CellValue(d[k].strip())
            cv.add_rc(d[k].row, d[k].col)
            return cv

def parse_basic_parts(values: List[List[Union[str, int]]]) -> List[Dict]:
    basic = extract_basic_parts(values)

    # remove empty rows
    basic = remove_empties_from_row(basic)

    part_json = pd.DataFrame(basic[2:], columns=basic[1]).T.to_dict()

    part_json_list = []
    for data in part_json.values():
        new_data = {
            "name": _get_and_strip(data, Keys.Part_Name, None),
            "collection": "basic DNA parts",
            "partType": "basic part",
            "description": _get_and_strip(data, Keys.Description, None),
            "sequence": _get_and_strip(data, Keys.Sequence, None),
        }

        length = data[Keys.Length]
        if length and not _is_nan(length):
            try:
                new_data["length"] = int(length)
            except ValueError:
                pass

        source = data[Keys.Source]
        if source and not _is_nan(source):
            new_data["source"] = source.strip()

        role = data[Keys.Role]
        if role and not _is_nan(role):
            new_data["roles"] = [role.strip()]

        part_json_list.append(new_data)
    return part_json_list

def parse_parts(values: List[List[Union[str, int]]]) -> List[Dict]:
    basic_parts = parse_basic_parts(values)
    composite_parts = parse_composite_parts(values)
    all_parts = basic_parts + composite_parts
    validate_part_list(all_parts)
    return all_parts


from PIL import Image
import numpy as np
import cv2
import os

# 定义文件夹路径和输出路径
folder_path = r'D:\zsh\biaozhu\4.20_jd_basetball\img_test'
output_path = r'D:\zsh\biaozhu\4.20_jd_basetball\img'

# 遍历文件夹中的所有图片
for filename in os.listdir(folder_path):
    if filename.endswith('.jpg') or filename.endswith('.png'):
        # 读取图片
        print(filename)
        img_path = os.path.join(folder_path, filename)
        img_o = Image.open(img_path)
        img = np.array(img_o)
        cv2.resize(img, (400, 400), interpolation=cv2.INTER_AREA)
        # 进行背景分割
        mask = np.zeros(img.shape[:2], np.uint8)
        bgdModel = np.zeros((1,65),np.float64)
        fgdModel = np.zeros((1,65),np.float64)
        rect = (50,50,img.shape[1]-50,img.shape[0]-50)
        cv2.grabCut(img,mask,rect,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)

        # 创建前景掩码
        mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')

        # 将前景与背景融合
        res = img*mask2[:,:,np.newaxis]

        # 将抠图结果填充为纯白色
        res[np.where((res == [0,0,0]).all(axis = 2))] = [255, 255, 255]

        # 保存结果
        output_name = os.path.join(output_path, filename)
        Image.fromarray(res).save(output_name)


from ckeditor.widgets import CKEditorWidget
from django import forms
from .models import EmailSend, Feedback, UniqueIP
from django.core.exceptions import ValidationError
from validate_email import validate_email
from django.contrib.sessions.backends.db import SessionStore
import re
from user_app.user_session import UserSessionToDo

reg = r'^[a-zA-Z0-9]([A-Za-z0-9]+[._-])*[A-Za-z0-9_]+@[A-Za-z0-9-_]+(\.[A-Z|a-z]{2,})+$'

class EmailSendForm(forms.ModelForm):
    class Meta:
        model = EmailSend
        fields = ('name', 'email')

    def clean_email(self):
        email = self.cleaned_data['email']
        if not re.match(reg, email):
            raise ValidationError('Проверьте правильность ввода email')
        return email

    def clean_name(self):
        name = self.cleaned_data['name']
        if len(name) < 2 or any(map(str.isdigit, name)):
            raise ValidationError('Где вы видели такое имя')
        return name.title()

class AddTodo(forms.Form):
    todo = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Новая задача'}), label='', required=False,
                           max_length=20)
    day_slug = forms.CharField(widget=forms.HiddenInput)
    sess = forms.CharField(widget=forms.HiddenInput)

    def clean(self):

        ust = UserSessionToDo(SessionStore(self.cleaned_data['sess']), sess=True)
        day = ust.todo_days[self.cleaned_data['day_slug']]
        todo = self.cleaned_data['todo']
        if not todo:
            raise ValidationError(
                f"Вы нечего не ввели")
        elif len(todo) > 20:
            raise ValidationError(
                "Не больше 20 символов")
        elif todo in day['actual']:
            raise ValidationError(
                f"Уже есть в Задачах на сегодня")
        elif todo in day['close']:
            raise ValidationError(
                f"Уже есть в Завершенных задачах")
        return todo

class FeedbackForm(forms.ModelForm):
    class Meta:
        model = Feedback
        fields = ('text',)

    text = forms.CharField(widget=CKEditorWidget(attrs={'placeholder': 'Текст'}), label='', required=False,
                           max_length=2000)

    def clean_text(self):
        text = self.cleaned_data['text']
        if not text:
            raise ValidationError('Напишите заголовок')
        if len(text) < 10:
            raise ValidationError('Вы очень скромный , хотя бы 10 символов')
        return text


from collections import deque
class Solution(object):
    def canVisitAllRooms(self, rooms):
        """
        :type rooms: List[List[int]]
        :rtype: bool
        """
        visited = [False] * len(rooms)

        def bfs(v):
            queue = deque()
            queue.append(v)
            visited[v] = True
            while queue:
                cur_v = queue.popleft()
                for next_v in rooms[cur_v]:
                    if visited[next_v] == False:
                        queue.append(next_v)
                        visited[next_v] = True

        bfs(0)

        return all(visited)


# to run script: `sudo python3 led.py`
#
# written by Boris Yakubchik
#
# Edit these:

NUM_OF_LED = 144
BRIGHTNESS = 0.2 # maximum 1 -- best when 1/n is an integer
PIANO_STARTS_WITH = 'B2'

# =========================================================
# DO NOT EDIT BELOW

from time import sleep
from time import time
import math
import threading

import board
import neopixel

import mido

import keyboard

pixels = neopixel.NeoPixel(board.D18, NUM_OF_LED, brightness=BRIGHTNESS, auto_write=False)

devices = mido.get_input_names()
piano = []

for device in devices:
  if device.startswith(PIANO_STARTS_WITH):
    piano.append(device)

if len(piano) == 0:
  print('\nIs the piano connected / turned on?\n')
  quit()
else:
  print('Listening for MIDI from', piano[0])

# =======================================================================
# Variables

# array for all BLOBS
all_blobs = {}

min_bright = math.ceil(1 / BRIGHTNESS)

# array for pixel color computation - to be written to the LED strip later
rgb = [1] * NUM_OF_LED # fill all of them with 1

# to throttle keyboard events
throttle_left = 0
throttle_right = 0
throttle_f12 = 0

# each Blob is a key pressed that evolves over time via its `update` method
class Blob:
  def __init__(self, x, r, v, s):
    self.x = x # x - location on x axis
    self.r = r # radius
    self.v = v # value == brightness
    self.s = s # status: `down`, `legato`, or `decay`

  def update(self):
    if self.s == 'down' or self.s == 'legato':
      self.r = min(self.r + 1, 5)
      self.v = math.floor(self.v * 0.98)
    else:
      self.r = self.r + 1
      self.v = math.floor(self.v * 0.75) # dim it

def add_note_to_workspace(key, velocity):
  all_blobs[key] = Blob(map_key_to_x(key), 1, velocity, 'down')

def thread_function(name):
  while True:

    global rgb

    # print(len(all_blobs)) # to check that blobs disappear after some time

    rgb = [min_bright] * NUM_OF_LED # reset array to min

    for blob_key in all_blobs:

      blob = all_blobs[blob_key]

      blob.update()

      for x in range(math.floor(blob.x - blob.r), math.ceil(blob.x + blob.r)):

        if (x >= 0 and x < NUM_OF_LED):
          scaler = (1 - abs(blob.x - x) / blob.r) ** 2
          rgb[x] = min(rgb[x] + scaler * blob.v, 255) # never let it go above 255
                                           # this can happen because we add many blobs together

    for idx, val in enumerate(rgb):
      # make sure these are integers
      pixels[idx] = (math.floor(val), math.floor(val), math.floor(val))

    pixels.show()

    sleep(0.05)

    to_delete = []

    # only keep blobs that are above brightness of 1
    for blob_key in all_blobs:
      if all_blobs[blob_key].v < 2:
        to_delete.append(blob_key)

    for key in to_delete:
      all_blobs.pop(key)

thread = threading.Thread(target=thread_function, args=(1,))
thread.start()

legato_pedal = 0 # 0 - not pressed, 1 - pressed

def unlegato_all_keys():
  for key in all_blobs:
    if all_blobs[key].s == 'legato':
      all_blobs[key].s = 'decay'

def handle_pedal(pedal, value):
  global legato_pedal
  if pedal == 67:
    throttle_key('left')
  elif pedal == 66:
    throttle_key('right')
  elif pedal == 64:
    if legato_pedal == 0 and value != 0:
      legato_pedal = 1
      print('legato ON')
    if value == 0:
      legato_pedal = 0
      print('legato OFF')
      unlegato_all_keys()

  else:
    print('unknown!')
    print(msg)

def throttle_key(key):
  global throttle_left
  global throttle_right
  global throttle_f12
  now = time()

  if key == 'left':

    if now < throttle_right + 1 and now > throttle_f12 + 0.5:
      throttle_f12 = now
      keyboard.press_and_release('f12')
    elif now > throttle_left + 0.5:
      throttle_left = now
      keyboard.press_and_release(key)

  elif key == 'right':

    if now < throttle_left + 1 and now > throttle_f12 + 0.5:
      throttle_f12 = now
      keyboard.press_and_release('f12')
    elif now > throttle_right + 0.5:
      throttle_right = now
      keyboard.press_and_release(key)

# takes raw key (21 - 108) and returns x coordinate
def map_key_to_x(key):
  # 65 = center between 21 and 108
  # 72 = NUM_OF_LEDS / 2 + fudge factor
  return (key - 65) * 2 + 72

with mido.open_input(piano[0]) as inport:
  for msg in inport:
    # print(msg)
    if hasattr(msg, 'velocity'):
      # print(msg.velocity)
      if msg.velocity == 64: # represents key-up velocity
        if msg.note in all_blobs: # key may have decayed and been removed already
          if legato_pedal == 1:
            all_blobs[msg.note].s = 'legato'
          else:
            all_blobs[msg.note].s = 'decay'
      else:
        add_note_to_workspace(msg.note, msg.velocity)

    elif hasattr(msg, 'control'):
      # pedal numbers 64, 65, 66
      handle_pedal(msg.control, msg.value)

    else:
      pring('something new and unknown!')
      print(msg)


import numpy as np
from numpy import nan
from numpy.linalg import norm as norm
import matplotlib.pyplot as plt
import time
import pinocchio as pin
import tsid
import gepetto.corbaserver
import subprocess
import os
from example_robot_data.robots_loader import getModelPath
from pinocchio.robot_wrapper import RobotWrapper
from mat4py import loadmat
import math
from IPython import embed
import sys

import plotly.graph_objects as go

#Fichiers perso
import load #fichier load.py
import marches
import read_data_kinematics
import read_data_moments

###### DEFINITIONS DE FONCTIONS ######

def degrees2radians(degrees):
    return degrees*math.pi/180

def radians2degrees(radians):
    return radians*180/math.pi

####### FIN DE DEFINITION DE FONCTION ######

######################################################################### READ DATA (kinematics, moments, events) ##########################################################################################

studied_walk=1

# PATH & URD
path = os.path.dirname(os.path.realpath(__file__))
urdf = path + '/urdf_augmented.urdf'

## Read data
data_kinematics= read_data_kinematics.read_data(path+"/Données_marche/fichiers txt/marche sans attelle "+str(studied_walk)+".txt", path + "/Données_marche/fichiers txt/marche sans attelle "+str(studied_walk)+".csv")
right=[data_kinematics.hip_flexion_r, data_kinematics.knee_angle_r, data_kinematics.ankle_angle_r]
left=[data_kinematics.hip_flexion_l, data_kinematics.knee_angle_l, data_kinematics.ankle_angle_l]

#Read Moments
data_moment=read_data_moments.read_data_moments(path + "/Données_marche/InverseDynamics/résultats sto & txt/marche sans attelle "+str(studied_walk)+".txt",path + "/Données_marche/InverseDynamics/résultats sto & txt/marche sans attelle "+str(studied_walk)+".csv")
right_moments=[data_moment.hip_flexion_r_moment, data_moment.knee_angle_r_moment, data_moment.ankle_angle_r_moment]
left_moments=[data_moment.hip_flexion_l_moment, data_moment.knee_angle_l_moment, data_moment.ankle_angle_l_moment]

#Walk construction
marche=marches.Marche('marche'+str(studied_walk),data_kinematics.time,left,right,data_moment.time,left_moments,right_moments)

#Read events
path_events= path + "/Données_marche/dataEvents/"
events= loadmat(path_events+'marche sans attelle '+str(studied_walk)+'Events.mat')
marche.toesoff(data_kinematics.time,events["Left_Foot_Off"],events["Right_Foot_Off"])
marche.heelstrike(data_kinematics.time,events["Left_Foot_Strike"],events["Right_Foot_Strike"])
#marche.printMarche() #print l'ensemble des données de la marche
#print('Heel strike', marche.heelstrike_right)
#Read FirstFoot
firstFoot=marche.firstFoot()

#delay = 255 * 0.005
delay = 0

t_hs_left = np.array(marche.heelstrike_left) - delay
t_hs_left = np.append(t_hs_left, np.zeros(1))
#print(t_hs_left)
i_hs_left = 0

t_hs_right = np.array(marche.heelstrike_right) - delay
t_hs_right = np.append(t_hs_right, np.zeros(1))
#print(t_hs_right)
i_hs_right = 0

t_to_left = np.array(marche.toesoff_left) - delay
t_to_left = np.append(t_to_left, np.zeros(1))
#print(t_to_left)
i_to_left = 0

t_to_right = np.array(marche.toesoff_right) - delay
t_to_right = np.concatenate((np.array([0.18]), t_to_right, np.zeros(1)))
#print(t_to_right)
i_to_right = 0

to_left_list = []
to_right_list = []
hs_left_list = []
hs_right_list = []
for i, t_event in enumerate(data_kinematics.time):
    print(t_event)
    if (t_hs_left[i_hs_left]<=t_event<t_hs_left[i_hs_left]+0.005):
        hs_left_list.append(i)
        i_hs_left += 1
    if (t_hs_right[i_hs_right]<=t_event<t_hs_right[i_hs_right]+0.005):
        hs_right_list.append(i)
        i_hs_right += 1
    if (t_to_left[i_to_left]<=t_event<t_to_left[i_to_left]+0.005):
        to_left_list.append(i)
        i_to_left += 1
    if (t_to_right[i_to_right]<=t_event<t_to_right[i_to_right]+0.005):
        to_right_list.append(i)
        i_to_right += 1

#Read markers
data_markers=loadmat(path + "/Données_marche/markers_data_patho.mat")
data_markers=data_markers['output']['marker_data']['Markers']
#print(data_markers.keys())

RLM = 1e-3 * np.array(data_markers['RLM']) # /!\ l'ordre du repère est -y, x, z
LLM = 1e-3 * np.array(data_markers['LLM']) # /!\ l'ordre du repère est -y, x, z

RGT = 1e-3 * np.array(data_markers['RGT']) # /!\ l'ordre du repère est -y, x, z
LGT = 1e-3 * np.array(data_markers['LGT']) # /!\ l'ordre du repère est -y, x, z

RASIS = 1e-3 * np.array(data_markers['RASIS']) # /!\ l'ordre du repère est -y, x, z
LASIS = 1e-3 * np.array(data_markers['LASIS']) # /!\ l'ordre du repère est -y, x, z

RPSIS = 1e-3 * np.array(data_markers['RPSIS']) # /!\ l'ordre du repère est -y, x, z
LPSIS = 1e-3 * np.array(data_markers['LPSIS']) # /!\ l'ordre du repère est -y, x, z

x_com = (1/4) * (RASIS[:,1] + LASIS[:,1] + RPSIS[:,1] + LPSIS[:,1])
y_com = -(1/4) * (RASIS[:,0] + LASIS[:,0] + RPSIS[:,0] + LPSIS[:,0])
z_com = (1/4) * (RASIS[:,2] + LASIS[:,2] + RPSIS[:,2] + LPSIS[:,2])

trace_right_foot = go.Scatter3d(x=RLM[:,1], y=-RLM[:,0], z=RLM[:,2],
                                   mode='lines', name= 'Right Foot')
trace_left_foot = go.Scatter3d(x=LLM[:,1], y=-LLM[:,0], z=LLM[:,2],
                                   mode='lines', name= 'Left Foot')
trace_com = go.Scatter3d(x=x_com, y=y_com, z=z_com,
                                   mode='lines', name= 'CoM')

x_start = [RLM[0,1], LLM[0,1], x_com[0]]
y_start = [-RLM[0,0], -LLM[0,0], y_com[0]]
z_start = [RLM[0,2], LLM[0,2], z_com[0]]

#x_start = [RLM[0,1], LLM[0,1]]
#y_start = [-RLM[0,0], -LLM[0,0]]
#z_start = [RLM[0,2], LLM[0,2]]

x_hs = []
y_hs = []
z_hs = []
for iter in (hs_right_list):
    x_hs.append(RLM[iter, 1])
    y_hs.append(-RLM[iter, 0])
    z_hs.append(RLM[iter, 2])
for iter in (hs_left_list):
    x_hs.append(LLM[iter, 1])
    y_hs.append(-LLM[iter, 0])
    z_hs.append(LLM[iter, 2])

x_to = []
y_to = []
z_to = []
for iter in (to_right_list):
    x_to.append(RLM[iter, 1])
    y_to.append(-RLM[iter, 0])
    z_to.append(RLM[iter, 2])
for iter in (to_left_list):
    x_to.append(LLM[iter, 1])
    y_to.append(-LLM[iter, 0])
    z_to.append(LLM[iter, 2])

trace_start = go.Scatter3d(x=x_start, y=y_start, z=z_start,
                                   mode='markers', name= 'Start')

trace_heel_strike = go.Scatter3d(x=x_hs, y=y_hs, z=z_hs,
                                   mode='markers', name= 'Heel Strike')

trace_toes_off = go.Scatter3d(x=x_to, y=y_to, z=z_to,
                                   mode='markers', name= 'Toes Off')

name = 'eye = (x:0., y:2.5, z:0.)'
camera = dict(
    up=dict(x=0, y=0., z=1.),
    eye=dict(x=0, y=-3, z=0.3),
    center=dict(x=0, y=0., z=-0.5)
)

#fig = go.Figure(data=[trace_right_foot, trace_left_foot, trace_start, trace_heel_strike, trace_toes_off])
fig = go.Figure(data=[trace_right_foot, trace_left_foot, trace_com, trace_start, trace_heel_strike, trace_toes_off])
fig.update_layout(scene_aspectmode='manual',
                  scene_aspectratio=dict(x=2, y=1, z=1), scene_camera=camera, legend=dict(font=dict(size=20), yanchor="top",
    y=0.8,
    xanchor="right",
    x=0.75)) #, xaxis = dict(tickfont = dict(size=20)), yaxis = dict(tickfont = dict(size=20)))
fig.update_yaxes(tickfont=dict(size=50))
fig.update_traces(marker=dict(size=3))
fig.show()


import os
import sys

from flask import Flask, redirect, render_template, request

from google.cloud import firestore
from google.cloud import storage
from google.cloud import vision

from helpers import is_valid, get_all_images

app = Flask(__name__)

@app.route('/')
def homepage():
    # Return the homepage
    return render_template('intropage.html')

@app.route('/home')
def intropage():
    # Return the intropage
    return render_template('intropage.html')

@app.route('/image_upload')
def image_upload():
    # Return the homepage
    return render_template('image_upload.html')

@app.route('/tag_photo', methods=['GET', 'POST'])
def tag_photo():
    # If the user inputs an image's link
    if request.form["image-link"]:
        image_public_url = request.form["image-link"]
        client = vision.ImageAnnotatorClient()
        image = vision.types.Image()
        image.source.image_uri = image_public_url

        response = client.label_detection(image=image)

        labels = response.label_annotations

        # Redirect to the scanned image page.
        return render_template('image_scan.html', labels=labels, image_public_url=image_public_url)

    # If a user uploads an image
    else:
        # Create a Cloud Storage client.
        storage_client = storage.Client()

        # Get the Cloud Storage bucket that the file will be uploaded to.
        bucket = storage_client.get_bucket(os.environ.get('CLOUD_STORAGE_BUCKET'))

        # Create a new blob and upload the file's content to Cloud Storage.
        photo = request.files['file']
        blob = bucket.blob(photo.filename)
        blob.upload_from_string(
                photo.read(), content_type=photo.content_type)

        # Make the blob publicly viewable.
        blob.make_public()
        image_public_url = blob.public_url

        # Create a Cloud Vision client.
        vision_client = vision.ImageAnnotatorClient()

        # Retrieve a Vision API response for the photo stored in Cloud Storage
        image = vision.types.Image()
        image.source.image_uri = 'gs://{}/{}'.format(os.environ.get('CLOUD_STORAGE_BUCKET'), blob.name)

        response = vision_client.annotate_image({'image': image})
        labels = response.label_annotations
        faces = response.face_annotations
        web_entities = response.web_detection.web_entities

        # Create a Cloud Firestore client
        firestore_client = firestore.Client()

        # Get a reference to the document we will upload to
        doc_ref = firestore_client.collection(u'photos').document(blob.name)

        # Note: If we are using Python version 2, we need to convert
        # our image URL to unicode to save it to Cloud Firestore properly.
        if sys.version_info < (3, 0):
            image_public_url = unicode(image_public_url, "utf-8")

        # Construct key/value pairs with data
        data = {
            u'image_public_url': image_public_url,
            u'top_label': labels[0].description
        }

        # Set the document with the data
        doc_ref.set(data)

        # Redirect to the home page.
        return render_template('image_scan.html', labels=labels, faces=faces, web_entities=web_entities, image_public_url=image_public_url)

@app.route('/page_scan')
def page_scan():
    # Redirect to the home page.
    return render_template('upload_page.html')

@app.route('/scan_url', methods=['GET', 'POST'])
def scan_url():
    # If the user inputs a site's link
    if request.form["site-link"]:
        page_link = request.form["site-link"]
        image_links = get_all_images(page_link)

        all_images = {} # hold all images information
        all_images["no_alt"] = [] # hold all images with no_alt
        all_images["possible_decorative"] = []
        all_images["warnings"] = []

        no_alt_images = image_links["no_alt"] # get array of images with no alt text attribute
        for single_image in no_alt_images:
            image_public_url = single_image["url"] # get the image's src
            client = vision.ImageAnnotatorClient()
            image = vision.types.Image()
            image.source.image_uri = image_public_url

            response = client.label_detection(image=image)

            labels = response.label_annotations
            if labels:
                image_details = {}
                image_details["url"] = image_public_url
                image_details["alt"] = single_image["alt"]
                image_details["warning"] = single_image["warning"]
                image_details["labels"] = labels
                all_images["no_alt"].append(image_details) # save details to no_alt

        possible_decorative_images = image_links["possible_decorative"] # get array of images that have empty alt text (could be decorative image)
        for single_image in possible_decorative_images:
            image_public_url = single_image["url"] # get the image's src
            client = vision.ImageAnnotatorClient()
            image = vision.types.Image()
            image.source.image_uri = image_public_url

            response = client.label_detection(image=image)

            labels = response.label_annotations
            if labels:
                image_details = {}
                image_details["url"] = image_public_url
                image_details["alt"] = single_image["alt"]
                image_details["warning"] = single_image["warning"]
                image_details["labels"] = labels
                all_images["possible_decorative"].append(image_details) # save details to possible_decorative

        index_count = 0 # start index count at 0
        warning_images = image_links["warnings"] # get array of images that might have bad alt text
        for single_image in warning_images:
            image_public_url = single_image["url"] # get the image's src
            client = vision.ImageAnnotatorClient()
            image = vision.types.Image()
            image.source.image_uri = image_public_url

            response = client.label_detection(image=image)

            labels = response.label_annotations
            if labels:
                image_details = {}
                image_details["url"] = image_public_url
                image_details["alt"] = single_image["alt"]
                image_details["warning"] = single_image["warning"]
                image_details["labels"] = labels
                all_images["warnings"].append(image_details) # save details to warnings

        # Organize data for passing
        no_alt_images = all_images["no_alt"] # get array of images with no alt text attribute
        possible_decorative_images = all_images["possible_decorative"] # get array of images that have empty alt text (could be decorative image)
        warning_images = all_images["warnings"] # get array of images that might have bad alt text

        # Redirect to the scan page.
        return render_template('scan_page.html', no_alt_images=no_alt_images, possible_decorative_images=possible_decorative_images, warning_images=warning_images, page_link=page_link, image_links=image_links)

@app.errorhandler(500)
def server_error(e):
    return """
    An internal error occurred: <pre>{}</pre>
    See logs for full stacktrace.
    """.format(e), 500

if __name__ == '__main__':
    # This is used when running locally. Gunicorn is used to run the
    # application on Google App Engine. See entrypoint in app.yaml.
    app.run(host='127.0.0.1', port=8080, debug=True)