#!/usr/bin/python
import sys
import re

def checkScript():
    """ Outputs lines which contains comma and/or quote
    It is here to monitor (and check) if the preprocessed file is still
    a well-built csv file
    """
    with open(sys.argv[1]) as fread:
        while True:
            line = fread.readline()
            if not line:
                break
            count_comma = line.count(',')
            count_quote = line.count('"')
            # if count_quote != 0:
            #     print(count_quote, line)
            if count_comma != 0 and count_comma != 10 :
                print(count_comma, line)
            # if count_quote != 0  :
            #     if count_comma != 0 and count_comma != 10 :
            #         print(count_comma, line)
            # cntcm = line.count(',')
            # cntqt = line.count('"')
            # if cntqt != 0 and cntcm != 0 :
            #     if cntcm != 10 :
            #         if cntqt > 2 :
            #             groups = line.split('"')
            #             test = '"'.join(groups[:cntqt]), '"'.join(groups[cntqt:])
            #             if test[0].count(',') == 10 and count_comma != 10:
            #                 print(count_comma, line)
            #                 return

def main():
    """ Provides an argument : a path to the csv file (including the name of the csv) """
    if len(sys.argv) != 2:
        print("One argument is necessary : the path to the csv file")
        return -1
    checkScript()

if __name__ == '__main__':
    main()


'''
4) Faça um algoritmo para efetuar o cálculo do IMC. Sabendo que é feito dividindo o peso (em quilogramas) pela altura (em metros) ao quadrado.

imc=peso/altura ao quadrado
'''
peso=int(input("Informe seu peso: "))
altura=float(input("Informe sua altura: "))

# A linguagem tem definido que a utilização de 2 asteriscos seguidos ** significa que o número a esquerda do operador será elevado ao número a direita do operador, por exemplo:
imc= peso/ (altura ** 2)

print("O seu indíce de massa corporal é ", imc)


"""model acuracy meters"""

import numpy as np

class errors():

    def __init__(self, actual_data, predictions):

        self.a = actual_data
        self.p = predictions

    def RMSE(self):
        N=self.a.shape[1]
        summ= np.sum((self.a-self.p)**2,axis=1)
        return (summ/N)**0.5

    def TotalSum(self):
        return np.sum(self.a - self.p)

    def sMAPE(self,factor=2):
        N=self.a.shape[1]
        summ=abs(self.a-self.p)*factor/(self.a+self.p)
        return summ

    def MAPE(self):
        N=self.a.shape[1]
        summ=np.sum(abs((self.a-self.p)/self.a),axis=1)
        return summ/N

    def MAE(self):
        N=self.a.shape[1]
        summ=np.sum(abs(self.a-self.p),axis=1)
        return summ/N

    def MAPE2(self):
        summ=np.average(abs((self.a-self.p)/self.a),axis=0)
        return summ
    def experrors(self):
        return [self.RMSE(),self.TotalSum(),self.MAPE(),self.sMAPE(),self.MAPE2(),self.MAE()]

    def plotperquarter(self,month=3):

        if month ==3:
            summ=abs(self.a-self.p)/self.a
            return summ
        else:
            [u,v]= self.a.shape

            v3=int(v/3)

            actual  = np.zeros((u,v3))
            forcasts= np.zeros((u,v3))

            for i in range(v3):
                actual[:,i]= np.sum(self.a[:,3*i:3*i+3],axis=1)
                forcasts[:,i]= np.sum(self.p[:,3*i:3*i+3],axis=1)
            summ=abs(actual-forcasts)/actual

            total=np.sum(self.a-self.p,axis=0)
            return summ,total

def normalization(timeseries):

    x,y=timeseries.shape

    normalized=np.ones((x,y))*-1
    statistical=[]
    for i in range(x):
        average=np.average(timeseries[i,:])
        std=np.std(timeseries[i,:])
        normalized[i,:]=(timeseries[i,:]-average)/std
        statistical.append((average,std))

    return normalized , statistical

def denormalization(timeseries,stats):
    x,y=timeseries.shape
    denormalizes=np.ones((x,y))*-1

    for i in range(x):
        denormalizes[i,:]=timeseries[i,:]*stats[i][1]+stats[i][0]

    return denormalizes


import argparse
import json
import logging
import os
import sys
from pathlib import Path
from typing import Dict

import mlflow
import pandas as pd
from omegaconf import DictConfig
from sklearn.model_selection import train_test_split

sys.path.append("..")
from common.custom_logger import CustomLogger  # noqa: E402
from common.omegaconf_manager import OmegaconfManager  # noqa: E402
from common.utils import get_mlflow_tag_from_input_parameters, split_input_parameters_str  # noqa: E402
from preprocess.src.extract_data import get_test_data_files, get_train_data_files  # noqa: E402
from preprocess.src.extract_dummy_data import get_dummy_data_files, get_meta_test_info  # noqa: E402

logging.basicConfig(
    level=logging.INFO,
)
logger = CustomLogger("Preprocess_Logger")

def main(cfg: DictConfig):
    """The main process of `preprocess` step.

    In `preprocess` step, split all datasets into 3 parts.
        1. Train (`meta_train.json`): The training dataset files with `train_dataset.csv`.
        2. Validation (`meta_valid.json`): The validation dataset files with `train_dataset.csv`.
        3. Test (`meta_test.json`): The test dataset files with `test_dataset.json`.

    Then, the 3 meta files are saved and logged as mlflow artifacts.
    """
    input_parameters = split_input_parameters_str(cfg.input_parameters)
    mlflow.set_tag("mlflow.runName", get_mlflow_tag_from_input_parameters(input_parameters) + "_preprcess")

    downstream_dir_path: str = cfg.preprocess.downstream_dir_path
    os.makedirs(downstream_dir_path, exist_ok=True)

    time_step_minutes = cfg.preprocess.time_step_minutes
    time_slides_delta = cfg.preprocess.time_slides_delta

    if cfg.use_dummy_data is True:
        logger.warning("... Using dummy data ...")
        data_files = get_dummy_data_files(
            input_parameters=input_parameters,
            time_step_minutes=time_step_minutes,
            downstream_dir_path=downstream_dir_path,
            dataset_length=100,
            input_seq_length=cfg.input_seq_length,
            label_seq_length=cfg.label_seq_length,
        )

        data_file_length = len(data_files)
        train_data_size = data_file_length // 2
        valid_test_data_size = data_file_length // 4
        train_data_files, valid_data_files, test_data_files = (
            data_files[:train_data_size],
            data_files[train_data_size : train_data_size + valid_test_data_size],  # noqa: E203
            data_files[
                train_data_size + valid_test_data_size : train_data_size + valid_test_data_size + 5
            ],  # noqa: E203
        )

    else:
        # train_dataset.csv comes from https://github.com/tsugumi-sys/poteka_data_analysis/blob/main/EDA/rain/rain_durations.ipynb
        current_dir = os.getcwd()
        train_list_df = pd.read_csv(os.path.join(current_dir, "src/train_dataset.csv"))
        train_data_files = get_train_data_files(
            project_root_dir_path=Path(cfg.pipeline_root_dir_path).parent,
            train_list_df=train_list_df,
            input_parameters=input_parameters,
            time_step_minutes=time_step_minutes,
            time_slides_delta=time_slides_delta,
            input_seq_length=cfg.input_seq_length,
            label_seq_length=cfg.label_seq_length,
        )
        train_data_files, valid_data_files = train_test_split(train_data_files, test_size=0.2, random_state=11)

        # test_dataset.json comes from https://github.com/tsugumi-sys/poteka_data_analysis/blob/main/EDA/rain/select_test_dataset.ipynb
        with open(os.path.join(current_dir, "src/test_dataset.json")) as f:
            test_data_list = json.load(f)
        test_data_files = get_test_data_files(
            project_root_dir_path=Path(cfg.pipeline_root_dir_path).parent,
            test_data_list=test_data_list,
            input_parameters=input_parameters,
            time_step_minutes=time_step_minutes,
            input_seq_length=cfg.input_seq_length,
            label_seq_length=cfg.label_seq_length,
        )

    meta_train = {"file_paths": train_data_files}
    meta_valid = {"file_paths": valid_data_files}
    meta_test = {
        "file_paths": test_data_files
        if isinstance(test_data_files, Dict)
        else get_meta_test_info(test_data_files, cfg.label_seq_length)
    }

    meta_train_filepath = os.path.join(
        downstream_dir_path,
        "meta_train.json",
    )
    meta_valid_filepath = os.path.join(
        downstream_dir_path,
        "meta_valid.json",
    )
    meta_test_filepath = os.path.join(
        downstream_dir_path,
        "meta_test.json",
    )

    with open(meta_train_filepath, "w") as f:
        json.dump(meta_train, f)
    with open(meta_valid_filepath, "w") as f:
        json.dump(meta_valid, f)
    with open(meta_test_filepath, "w") as f:
        json.dump(meta_test, f)

    for path in [meta_train_filepath, meta_valid_filepath, meta_test_filepath]:
        mlflow.log_artifact(path)
    logger.info("meta info files have saved")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Preprocess arguments")
    parser.add_argument("--hydra_file_path", type=str, help="Hydra configuration file saved in main.py.")
    args = parser.parse_args()
    omegaconf_manager = OmegaconfManager()
    hydra_conf = omegaconf_manager.load(args.hydra_file_path)
    main(hydra_conf)


import pandas as pd
import dash
from dash import Dash, dcc, html, Input, Output, State
import plotly.express as px
import os

# 01: Create a Dash web application
app = dash.Dash(__name__)
server = app.server

# 02: Define Download Path
from pathlib import Path
download_path = str(Path.home() / "Downloads")
download_path.replace("\\", "/")

# 03: Speciry Columns to include
#columns_to_include = ['Date','WA_B1','ESB_B1','WA_S1','ESB_S1','WA_B2','ESB_B2','WA_S2','ESB_S2','WA_B3','ESB_B3','WA_S3','ESB_S3','WA_B4','ESB_B4','WA_S4','ESB_S4']

# 04A: Read .csv file
data = pd.read_csv(download_path+'\Test_Origina_Data_ORIGINAL.csv',parse_dates=['Date'])
#data = pd.read_csv(download_path+'\Test_Origina_Data_ORIGINAL.csv',usecols=columns_to_include,parse_dates=['Date'])
# Create a Pandas DataFrame
#data = pd.DataFrame(data0)

# 04B: Crate Pandas DataFrame and Include only those columns required as Output file as per click button
data1 = (data[['Date','ESB_S1', 'ESB_B1', 'WA_S1', 'WA_B1']])       # 'Extract Average Price Porker 45-60 Kg'
data2 = (data[['Date','ESB_S2', 'ESB_B2', 'WA_S2', 'WA_B2']])       # 'Extract Prime Price Porker 45-60 Kg'
data3 = (data[['Date','ESB_S3', 'ESB_B3', 'WA_S3', 'WA_B3']])       # 'Extract Average Price Baconer 60.1 - 75 Kg'
data4 = (data[['Date','ESB_S4', 'ESB_B4', 'WA_S4', 'WA_B4']])       # 'Extract Prime Price Baconer 60.1 - 75 Kg'

# 04C:
y1 = ['ESB_S1', 'ESB_B1', 'WA_S1', 'WA_B1']
y2 = ['ESB_S2', 'ESB_B2', 'WA_S2', 'WA_B2']
y3 = ['ESB_S3', 'ESB_B3', 'WA_S3', 'WA_B3']
y4 = ['ESB_S4', 'ESB_B4', 'WA_S4', 'WA_B4']

# 04D: store min/max value for y-axis --> y1,y2,y3,y4
d1_min = int((data1[y1].min()).min())
d1_max = int((data1[y1].max()).max())
##
d2_min = int((data2[y2].min()).min())
d2_max = int((data2[y2].max()).max())
##
d3_min = int((data3[y3].min()).min())
d3_max = int((data3[y3].max()).max())
##
d4_min = int((data4[y4].min()).min())
d4_max = int((data4[y4].max()).max())

# 04D: call function to Downgrade for min and Upgrade for Max to accomodate y axis tick
def round_down_to_nearest_100_for_min_or_max(number,min_max):
    remainder = number % 100
    if remainder == 50 or remainder == 0 :      #regardless of min or max
                return number
    elif min_max == 'min':                      #get lowest value
        if remainder < 50:
            return number - remainder
        else:
            return number - remainder+50
    elif min_max == 'max':                      #get highest value
        if remainder < 50:
            return number - remainder +50
        else:
            return number - remainder + 100

# 04E: store min/max tick point for y-axis for corresponding data sets such as data1,data2,data3 and data4
y1_min_for_data1 = round_down_to_nearest_100_for_min_or_max(d1_min,'min')
y1_max_for_data1 = round_down_to_nearest_100_for_min_or_max(d1_max,'max')
y2_min_for_data2 = round_down_to_nearest_100_for_min_or_max(d2_min,'min')
y2_max_for_data2 = round_down_to_nearest_100_for_min_or_max(d2_max,'max')
y3_min_for_data3 = round_down_to_nearest_100_for_min_or_max(d3_min,'min')
y3_max_for_data3 = round_down_to_nearest_100_for_min_or_max(d3_max,'max')
y4_min_for_data4 = round_down_to_nearest_100_for_min_or_max(d4_min,'min')
y4_max_for_data5 = round_down_to_nearest_100_for_min_or_max(d4_max,'max')

# 04F: Rename data frame columns for display purpose
data1 = data1.rename(columns={'ESB_S1': 'ESB Seller', 'ESB_B1': 'ESB Buyer', 'WA_S1': 'WA Seller', 'WA_B1': 'WA Buyer'})
data2 = data2.rename(columns={'ESB_S2': 'ESB Seller', 'ESB_B2': 'ESB Buyer', 'WA_S2': 'WA Seller', 'WA_B2': 'WA Buyer'})
data3 = data3.rename(columns={'ESB_S3': 'ESB Seller', 'ESB_B3': 'ESB Buyer', 'WA_S3': 'WA Seller', 'WA_B3': 'WA Buyer'})
data4 = data4.rename(columns={'ESB_S4': 'ESB Seller', 'ESB_B4': 'ESB Buyer', 'WA_S4': 'WA Seller', 'WA_B4': 'WA Buyer'})

# 04G: Downloaded File to --> C:\Users\xyz_Name\Downloads folde
#data1.to_csv(download_path.replace("\\", "/")+"/ESB and WA Average Price Porker (45kg - 60Kg).csv",index=False,encoding='utf-8')
data1.to_csv(download_path.replace("\\", "/")+"/ESB and WA Average Price Porker (45kg - 60Kg).csv",index=False)
data2.to_csv(download_path.replace("\\", "/")+"/ESB and WA Prime Price Porker (45kg - 60Kg).csv",index=False)
data3.to_csv(download_path.replace("\\", "/")+"/ESB and WA Average Price Baconer (60.1kg - 75Kg).csv",index=False)
data4.to_csv(download_path.replace("\\", "/")+"/ESB and WA Prime Price Baconer (60.1kg - 75Kg).csv",index=False)

# 04H: Define the titles and coordinates for each graph
graph_titles = [
    ('ESB and WA Average Price Porker (45kg - 60Kg)', 'top left', '100px', '100px'),
    ('ESB and WA Prime Price Porker (45kg - 60Kg)', 'top left', '100px', '100px'),
    ('ESB and WA Average Price Baconer (60.1kg - 75Kg)', 'top left', '100px', '100px'),
    ('ESB and WA Prime Price Baconer (60.1kg - 75Kg)', 'top left', '100px', '100px'),
]

# 04I: Create a function to add annotations to a figure
# Create a function to add annotations to a figure with a centered title
#  If you want to position the title at the top center of the figure and make only the title text bold, you can modify the add_annotations function as follows:
def add_annotations(fig, title, x_center, y_top):
    fig.add_annotation(
        text=title,  # Set the title text
        xref="paper", yref="paper",
        x=x_center, y=y_top,
        xanchor="center", yanchor="top",  # Position at the top center
        showarrow=False,
        font=dict(size=14, family="Arial, sans-serif", color="black"), # plotly does not support weight="bold"),  # Set font properties
    )

# 05: Create the layout of the web application
app.layout = html.Div([
    html.Div([
        #html.Label('Select Date Range:',style={'position': 'absolute', 'top': '0px', 'left': '1000px', 'font-size': '12px'}),
        html.Label('.'),
        dcc.RangeSlider(
            id='date-slider',
            min=pd.to_datetime(data['Date'].min()).timestamp(),
            max=pd.to_datetime(data['Date'].max()).timestamp(),
            step=7 * 24 * 3600,  # 1 week
            marks=None,
            ##marks={date.timestamp(): date.strftime('%Y-%m-%d') for date in data['Date']},
            value=[
                pd.to_datetime(data['Date'].min()).timestamp(),
                pd.to_datetime(data['Date'].max()).timestamp()
            ]
        ),
        ]),
    html.Div([
        #html.Label('Your Dashboard Title', style={'position': 'absolute', 'left': '100px', 'top': '100px', 'font-size': '24px'}),
        dcc.Graph(id='graph-01'),
        html.Button('View/Extract Data', id='extract-button-01',
                    style={'position': 'absolute', 'top': '90px', 'right': '168px'}),
        ]),
    html.Div([
        dcc.Graph(id='graph-02'),
        html.Button('View/Extract Data', id='extract-button-02',
        ##html.Button('View/Extract ESB and WA Prime Price Porker (45-60 Kg) Data', id='extract-button-02',
                    style={'position': 'absolute', 'top': '320px', 'right': '168px'}),
        ]),
    html.Div([
        dcc.Graph(id='graph-03'),
        html.Button('View/Extract Data', id='extract-button-03',
                    style={'position': 'absolute', 'top': '550px', 'right': '168px'}),
        ]),
   html.Div([
        dcc.Graph(id='graph-04'),
        html.Button('View/Extract Data', id='extract-button-04',
                    style={'position': 'absolute', 'top': '780px', 'right': '168px'}),
        ]),
    dcc.Store(id='data-store')  # Store component to hold the data for download
])

# 06: Define callback functions
@app.callback(
    [Output('graph-01', 'figure'),
     Output('graph-02', 'figure'),
     Output('graph-03', 'figure'),
     Output('graph-04', 'figure')],
    [Input('date-slider', 'value')]
)

# 07: Define update_graph
def update_graph(selected_date_range):
    filtered_data1 = data1[(data1['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data1['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]
    filtered_data2 = data2[(data['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]
    filtered_data3 = data3[(data['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]
    filtered_data4 = data4[(data['Date'] >= pd.to_datetime(selected_date_range[0], unit='s')) &
                         (data['Date'] <= pd.to_datetime(selected_date_range[1], unit='s'))]

    fig1 = px.line(filtered_data1, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer'])

    fig1.update_layout(
        title_text='ESB and WA Average Price Porker (45kg - 60Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig1.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig1.update_yaxes(range=[y1_min_for_data1, y1_max_for_data1], dtick=100)
    fig1.update_layout(height=230)#, width=400)
    fig1.update_layout(margin=dict(l=0, r=0, t=50, b=0))        #left/right/top/bottom margin
    #fig1.update_layout(padding=dict(l=10, r=10, t=10, b=10))   #left/right/top/bottom margin

    fig2 = px.line(filtered_data2, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer'])
    fig2.update_layout(
        title_text='ESB and WA Prime Price Porker (45kg - 60Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig2.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig2.update_yaxes(range=[y2_min_for_data2, y2_max_for_data2], dtick=100)
    fig2.update_layout(height=230)#, width=400)
    fig2.update_layout(margin=dict(l=0, r=0, t=50, b=0))   #left/right/top/bottom margin

    fig3 = px.line(filtered_data3, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer']) #, title='ESB and WA Average Price Baconer (60.1kg - 75Kg)')
    fig3.update_layout(
        title_text='ESB and WA Average Price Baconer (60.1kg - 75Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig3.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig3.update_yaxes(range=[y3_min_for_data3, y3_max_for_data3], dtick=100)
    fig3.update_layout(height=230)#, width=400)
    fig3.update_layout(margin=dict(l=0, r=0, t=50, b=0))   #left/right/top/bottom margin

    fig4 = px.line(filtered_data4, x='Date', y=['ESB Seller', 'ESB Buyer', 'WA Seller', 'WA Buyer'], title='ESB and WA Prime Price Baconer (60.1kg - 75Kg)')
    fig4.update_layout(
        title_text='ESB and WA Prime Price Baconer (60.1kg - 75Kg)',  # Title with  tags for bold
        title_x=0.5,            # Set the x-position to center
        title_y=.9, #1.2,       # Set the y-position to place it above the graph  value should be between int/float 0 and 1
        title_xanchor='center', # Center the x position
        title_font=dict(size=20, family="Arial, sans-serif", color="#808080") #, weight="bold")  # Set font properties
    )
    fig4.update_layout(xaxis_title='', yaxis_title='',legend_title_text='')
    fig4.update_yaxes(range=[y4_min_for_data4, y4_max_for_data5], dtick=100)
    fig4.update_layout(height=230)#, width=400)
    fig4.update_layout(margin=dict(l=0, r=0, t=50, b=0))   #left/right/top/bottom margin

    return fig1, fig2, fig3, fig4

# 08: Define callback functions for output
@app.callback(
    Output('data-store', 'data'),
    [Input('extract-button-01', 'n_clicks'),
     Input('extract-button-02', 'n_clicks'),
     Input('extract-button-03', 'n_clicks'),
     Input('extract-button-04', 'n_clicks')],
    [State('date-slider', 'value')]
)

# 09: Define extract data function with click extract button
def extract_data(n_clicks_01, n_clicks_02, n_clicks_03, n_clicks_04, selected_date_range):
    ctx = dash.callback_context
    if not ctx.triggered:
        return None

    triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]

    if triggered_id == 'extract-button-01':
##        csv_string = data1.to_csv(index=False,encoding='utf-8')
##        return {
##            'content': csv_string,
##            'filename': 'ESB and WA Average Price Porker (45kg - 60Kg).csv'  # Specify the desired filename
##        }
        filtered_data1 = os.startfile(download_path +'\ESB and WA Average Price Porker (45kg - 60Kg).csv')
        return filtered_data1
    elif triggered_id == 'extract-button-02':
        filtered_data2 = os.startfile(download_path +'\ESB and WA Prime Price Porker (45kg - 60Kg).csv')
        return filtered_data2
    elif triggered_id == 'extract-button-03':
        filtered_data3 = os.startfile(download_path +'\ESB and WA Average Price Baconer (60.1kg - 75Kg).csv')
        return filtered_data3
    elif triggered_id == 'extract-button-04':
        filtered_data4 = os.startfile(download_path +'\ESB and WA Prime Price Baconer (60.1kg - 75Kg).csv')
        return filtered_data4

# 00: main calling program
if __name__ == '__main__':
    app.run_server(host="0.0.0.0",debug=True)
    #app.run_server(host="127.0.0.1",debug=True)


import functools

from flask import g

class Middleware:
    def __call__(self, func):
        @functools.wraps(func)
        def _wrapper(*args, **kwargs):
            if "subject" not in g:
                return {
                    "code": "authentication_failure",
                    "message": "Authentication required to access endpoint.",
                }, 401
            return func(*args, **kwargs)

        return _wrapper


from abc import ABC, abstractmethod
from sqlalchemy import select
from sqlalchemy.orm import aliased

from src.settings.db import async_session_maker
from src.apps.models import *

class AbstractRepository(ABC):
    @abstractmethod
    async def get_all():
        raise NotImplementedError

    @abstractmethod
    async def get_deffirence(self, city):
        raise NotImplementedError

class SQLAlchemyRepository(AbstractRepository):
    model = None

    async def get_all(self):
        async with async_session_maker() as session:
            query = select(self.model)
            result = await session.execute(query)
            return result.all()

    async def get_deffirence(self, city):
        raise NotImplementedError

class SQLAlcemyRepositoryWithTwoModels(AbstractRepository):
    model_1 = None
    model_2 = None

    async def get_all():
        raise NotImplementedError

    async def get_deffirence(self, city):
        async with async_session_maker() as session:
                cities = aliased(self.model_1)
                cities_2 = aliased(self.model_1)
                Roads = aliased(self.model_2)

                neighboring_cities = (select(
                    cities.name.label("city"),
                    cities_2.name.label("target_city"),
                    Roads.distance.label("distance")
                )
                .join(Roads, Roads.previous_city == cities.id)
                .join(cities_2, cities_2.id == Roads.next_city)
                .filter(cities.name == city)).cte("neighboring_cities")

                query = (
                    select(
                            neighboring_cities,
                            sa.func.lead(neighboring_cities.c.distance)
                            .over(
                                    partition_by=neighboring_cities.c.city,
                                    order_by=neighboring_cities.c.target_city
                                )
                            - neighboring_cities.c.distance
                        )

                    )

                result = await session.execute(query)
                return result.all()


from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.views.generic.base import RedirectView

urlpatterns = patterns('',
	url(r'^$', RedirectView.as_view(url='/principal/login/')),
    url(r'^principal/', include('principal.urls', namespace="principal")),
    url(r'^empresas/', include('empresas.urls', namespace="empresas")),
    url(r'^cotizaciones/', include('cotizaciones.urls', namespace="cotizaciones")),
    url(r'^contactos/', include('contactos.urls', namespace="contactos")),
    url(r'^oauth2callback', 'contactos.views.auth_return'),
    url(r'^grappelli/', include('grappelli.urls')),
    url(r'^admin/', include(admin.site.urls)),
)


import argparse
import contextlib
import glob
import json
import logging
import os
import shutil
import sys

from blinkpy.common import exit_codes
from blinkpy.common import path_finder
from blinkpy.common.host import Host
from blinkpy.common.path_finder import PathFinder
from blinkpy.web_tests.port.android import (
    ANDROID_WEBVIEW,
    CHROME_ANDROID,
)

path_finder.add_testing_dir_to_sys_path()
path_finder.add_build_android_to_sys_path()

from scripts import common

logger = logging.getLogger(__name__)

UPSTREAM_GIT_URL = 'https://github.com/web-platform-tests/wpt.git'

try:
    # This import adds `devil` to `sys.path`.
    import devil_chromium
    from devil import devil_env
    from devil.utils.parallelizer import SyncParallelizer
    from devil.android import apk_helper
    from devil.android import device_utils
    from devil.android.device_errors import CommandFailedError
    from devil.android.tools import webview_app
    from pylib.local.emulator import avd
    _ANDROID_ENABLED = True
except ImportError:
    logger.warning('Android tools not found')
    _ANDROID_ENABLED = False

def _make_pass_through_action(dest, map_arg=lambda arg: arg):
    class PassThroughAction(argparse.Action):
        def __init__(self, option_strings, dest, nargs=None, **kwargs):
            if nargs is not None and not isinstance(nargs, int):
                raise ValueError('nargs {} not supported for {}'.format(
                    nargs, option_strings))
            super().__init__(option_strings, dest, nargs=nargs, **kwargs)

        def __call__(self, parser, namespace, values, option_string=None):
            if not option_string:
                return
            args = [option_string]
            if self.nargs is None:
                # Typically a single-arg option, but *not* wrapped in a list,
                # as is the case for `nargs=1`.
                args.append(str(values))
            else:
                args.extend(map(str, values))
            # Use the single-arg form of a long option. Easier to read with
            # option prefixing. Example:
            #   --binary-arg=--enable-blink-features=Feature
            # instead of
            #   --binary-arg=--enable-blink-features --binary-arg=Feature
            if len(args) == 2 and args[0].startswith('--'):
                args = ['%s=%s' % (args[0], args[1])]
            wpt_args = getattr(namespace, dest, [])
            wpt_args.extend(map(map_arg, args))
            setattr(namespace, dest, wpt_args)

    return PassThroughAction

WPTPassThroughAction = _make_pass_through_action('wpt_args')
BinaryPassThroughAction = _make_pass_through_action(
    'wpt_args', lambda arg: '--binary-arg=%s' % arg)

class WPTAdapter(common.BaseIsolatedScriptArgsAdapter):
    def __init__(self):
        self._tmp_dir = None
        self.host = Host()
        self.fs = self.host.filesystem
        self.path_finder = PathFinder(self.fs)
        self.port = self.host.port_factory.get()
        super(WPTAdapter, self).__init__()
        self.wptreport = None
        self._include_filename = None
        self.layout_test_results_subdir = 'layout-test-results'
        self._parser = self._override_options(self._parser)
        # Parent adapter adds extra arguments, so it is safe to parse the
        # arguments and set options here.
        try:
            self.parse_args()
            product_cls = _product_registry[self.options.product_name]
            self.product = product_cls(self.host, self.options,
                                       self.select_python_executable())
            self.add_default_child_process()
        except ValueError as exc:
            self._parser.error(str(exc))

    @property
    def _upstream_dir(self):
        return self.fs.join(self._tmp_dir, 'upstream_wpt')

    def add_default_child_process(self):
        if self._options.processes is None:
            if self.product.name == 'chrome' or self.product.name == 'content_shell':
                self._options.processes = self.port.default_child_processes()
            else:
                self._options.processes = 1

    def _override_options(self, base_parser):
        """Create a parser that overrides existing options.

        `argument.ArgumentParser` can extend other parsers and override their
        options, with the caveat that the child parser only inherits options
        that the parent had at the time of the child's initialization. There is
        not a clean way to add option overrides in `add_extra_arguments`, where
        the provided parser is only passed up the inheritance chain, so we add
        overridden options here at the very end.

        See Also:
            https://docs.python.org/3/library/argparse.html#parents
        """
        parser = argparse.ArgumentParser(
            parents=[base_parser],
            # Allow overriding existing options in the parent parser.
            conflict_handler='resolve',
            epilog=(
                'All unrecognized arguments are passed through '
                "to wptrunner. Use '--wpt-help' to see wptrunner's usage."),
        )
        parser.add_argument('--isolated-script-test-repeat',
                            '--repeat',
                            '--gtest_repeat',
                            metavar='REPEAT',
                            type=int,
                            default=1,
                            help='Number of times to run the tests')
        parser.add_argument(
            '--isolated-script-test-launcher-retry-limit',
            '--test-launcher-retry-limit',
            '--retry-unexpected',
            metavar='RETRIES',
            type=int,
            help=(
                'Maximum number of times to rerun unexpectedly failed tests. '
                'Defaults to 3 unless given an explicit list of tests to run.'
            ))
        # `--gtest_filter` and `--isolated-script-test-filter` have slightly
        # different formats and behavior, so keep them as separate options.
        # See: crbug/1316164#c4

        # TODO(crbug.com/1356318): This is a temporary hack to hide the
        # inherited '--xvfb' option and force Xvfb to run always.
        parser.add_argument('--xvfb',
                            action='store_true',
                            default=True,
                            help=argparse.SUPPRESS)
        return parser

    def parse_args(self, args=None):
        super().parse_args(args)
        if self.options.wpt_help:
            self._show_wpt_help()
        # Update the output directory and wptreport filename to defaults if not
        # set. We cannot provide CLI option defaults because they depend on
        # other options ('--target' and '--product').
        self.maybe_set_default_isolated_script_test_output()
        report = self.options.log_wptreport
        if report is not None:
            if not report:
                report = self._default_wpt_report()
            self.wptreport = self.fs.join(self.fs.dirname(self.wpt_output),
                                          report)
        if self.options.isolated_script_test_launcher_retry_limit is None:
            self.options.isolated_script_test_launcher_retry_limit = (
                self._default_retry_limit)
        if not hasattr(self.options, 'wpt_args'):
            self.options.wpt_args = []
        logging.basicConfig(
            level=self.log_level,
            # Align level name for easier reading.
            format='%(asctime)s [%(levelname)-8s] %(name)s: %(message)s',
            force=True)

    def _default_wpt_report(self):
        product = self.wpt_product_name()
        shard_index = os.environ.get('GTEST_SHARD_INDEX')
        if shard_index is not None:
            return 'wpt_reports_%s_%02d.json' % (product, int(shard_index))
        return 'wpt_reports_%s.json' % product

    @property
    def _default_retry_limit(self) -> int:
        if self.options.use_upstream_wpt:
            return 0
        return super()._default_retry_limit

    @property
    def wpt_binary(self):
        if self.options.use_upstream_wpt:
            return os.path.join(self._upstream_dir, "wpt")
        default_wpt_binary = os.path.join(common.SRC_DIR, "third_party",
                                          "wpt_tools", "wpt", "wpt")
        return os.environ.get("WPT_BINARY", default_wpt_binary)

    @property
    def wpt_root_dir(self):
        if self.options.use_upstream_wpt:
            return self._upstream_dir
        return self.path_finder.path_from_web_tests(
            self.path_finder.wpt_prefix())

    @property
    def output_directory(self):
        return self.path_finder.path_from_chromium_base(
            'out', self.options.target)

    @property
    def mojo_js_directory(self):
        return self.fs.join(self.output_directory, 'gen')

    def handle_unknown_args(self, rest_args):
        unknown_args = super().rest_args
        # We pass through unknown args as late as possible so that they can
        # override earlier options. It also allows users to pass test names as
        # positional args, which must not have option strings between them.
        for unknown_arg in unknown_args:
            # crbug/1274933#c14: Some developers had used the end-of-options
            # marker '--' to pass through arguments to wptrunner.
            # crrev.com/c/3573284 makes this no longer necessary.
            if unknown_arg == '--':
                logger.warning(
                    'Unrecognized options will automatically fall through '
                    'to wptrunner.')
                logger.warning(
                    "There is no need to use the end-of-options marker '--'.")
            else:
                rest_args.append(unknown_arg)
        return rest_args

    @property
    def rest_args(self):
        rest_args = list(self._wpt_run_args)

        if self.options.default_exclude:
            rest_args.extend(['--default-exclude'])

        if self.wptreport:
            rest_args.extend(['--log-wptreport', self.wptreport])

        if self.options.verbose >= 3:
            rest_args.extend([
                '--log-mach=-',
                '--log-mach-level=debug',
                '--log-mach-verbose',
            ])
        if self.options.verbose >= 4:
            rest_args.extend([
                '--webdriver-arg=--verbose',
                '--webdriver-arg="--log-path=-"',
            ])

        rest_args.append(self.wpt_product_name())
        rest_args = self.handle_unknown_args(rest_args)
        rest_args.extend([
            '--webdriver-arg=--enable-chrome-logs',
            # TODO(crbug/1316055): Enable tombstone with '--stackwalk-binary'
            # and '--symbols-path'.
            # Exclude webdriver tests for now. The CI runs them separately.
            '--exclude=webdriver',
            '--exclude=infrastructure/webdriver',
            '--binary-arg=--host-resolver-rules='
            'MAP nonexistent.*.test ~NOTFOUND, MAP *.test 127.0.0.1',
            '--binary-arg=--enable-experimental-web-platform-features',
            '--binary-arg=--enable-blink-features=MojoJS,MojoJSTest',
            '--binary-arg=--enable-blink-test-features',
            '--binary-arg=--disable-field-trial-config',
            '--binary-arg=--enable-features='
            'DownloadService<DownloadServiceStudy',
            '--binary-arg=--force-fieldtrials=DownloadServiceStudy/Enabled',
            '--binary-arg=--force-fieldtrial-params='
            'DownloadServiceStudy.Enabled:start_up_delay_ms/0',
            '--run-info=%s' % self._tmp_dir,
            '--no-pause-after-test',
            '--no-capture-stdio',
            '--no-manifest-download',
            '--tests=%s' % self.wpt_root_dir,
            '--metadata=%s' % self.wpt_root_dir,
            '--mojojs-path=%s' % self.mojo_js_directory,
        ])

        if self.options.enable_leak_detection:
            rest_args.append('--binary-arg=--enable-leak-detection')
        if self.options.use_upstream_wpt:
            # when running with upstream, the goal is to get wpt report that can
            # be uploaded to wpt.fyi. We do not really cares if tests failed or
            # not. Add '--no-fail-on-unexpected' so that the overall result is
            # success. Add '--no-restart-on-unexpected' to speed up the test. On
            # Android side, we are always running with one emulator or worker,
            # so do not add '--run-by-dir=0'
            rest_args.extend([
                '--no-fail-on-unexpected',
                '--no-restart-on-unexpected',
            ])
        else:
            # By default, wpt will treat unexpected passes as errors, so we
            # disable that to be consistent with Chromium CI. Add
            # '--run-by-dir=0' so that tests can be more evenly distributed
            # among workers.
            rest_args.extend([
                '--no-fail-on-unexpected-pass',
                '--no-restart-on-new-group',
                '--run-by-dir=0',
            ])

        rest_args.extend(self.product.wpt_args)

        if self.options.headless:
            rest_args.append('--headless')

        if self.options.run_wpt_internal:
            rest_args.extend([
                '--config',
                self.path_finder.path_from_web_tests("wptrunner.blink.ini")
            ])

        if self.options.flag_specific:
            configs = self.port.flag_specific_configs()
            rest_args.extend('--binary-arg=%s' % arg
                             for arg in configs[self.options.flag_specific][0])
            if configs[self.options.flag_specific][1] is not None:
                smoke_file_path = self.path_finder.path_from_web_tests(
                    configs[self.options.flag_specific][1])
                if not self._has_explicit_tests:
                    rest_args.append('--include-file=%s' % smoke_file_path)

        if self.options.test_filter:
            for pattern in self.options.test_filter.split(':'):
                rest_args.extend([
                    '--include',
                    self.path_finder.strip_wpt_path(pattern),
                ])

        rest_args.extend(self.options.wpt_args)
        return rest_args

    @property
    def log_level(self):
        if self.options.verbose >= 2:
            return logging.DEBUG
        if self.options.verbose >= 1:
            return logging.INFO
        return logging.WARNING

    def run_test(self):
        with contextlib.ExitStack() as stack:
            self._tmp_dir = stack.enter_context(self.fs.mkdtemp())
            # Manually remove the temporary directory's contents recursively
            # after the tests complete. Otherwise, `mkdtemp()` raise an error.
            stack.callback(self.fs.rmtree, self._tmp_dir)
            stack.enter_context(self.product.test_env())
            if self.options.use_upstream_wpt:
                logger.info("Using upstream wpt, cloning to %s ..." %
                            self._upstream_dir)
                # check if directory exists, if it does remove it
                if os.path.isdir(self._upstream_dir):
                    shutil.rmtree(self._upstream_dir, ignore_errors=True)
                # make a temp directory and git pull into it
                self.host.executive.run_command([
                    'git', 'clone', UPSTREAM_GIT_URL, self._upstream_dir,
                    '--depth=25'
                ])
                self._checkout_3h_epoch_commit()

            self._create_extra_run_info()
            return super().run_test(self.path_finder.web_tests_dir())

    def _checkout_3h_epoch_commit(self):
        output = self.host.executive.run_command(
            [self.wpt_binary, 'rev-list', '--epoch', '3h'])
        commit = output.splitlines()[0]
        self.host.executive.run_command(['git', 'checkout', commit],
                                        cwd=self._upstream_dir)

    def _create_extra_run_info(self):
        run_info = {
            # This property should always be a string so that the metadata
            # updater works, even when wptrunner is not running a flag-specific
            # suite.
            'flag_specific': self.options.flag_specific or '',
            'used_upstream': self.options.use_upstream_wpt,
            'sanitizer_enabled': self.options.enable_sanitizer,
        }
        if self.options.use_upstream_wpt:
            # `run_wpt_tests` does not run in the upstream checkout's git
            # context, so wptrunner cannot infer the latest revision. Manually
            # add the revision here.
            run_info['revision'] = self.host.git(
                path=self.wpt_root_dir).current_revision()
        # The filename must be `mozinfo.json` for wptrunner to read it.
        # The `--run-info` parameter passed to wptrunner is the directory
        # containing `mozinfo.json`.
        run_info_path = self.fs.join(self._tmp_dir, 'mozinfo.json')
        with self.fs.open_text_file_for_writing(run_info_path) as file_handle:
            json.dump(run_info, file_handle)

    @property
    def _wpt_run_args(self):
        """The start of a 'wpt run' command."""
        return [
            self.wpt_binary,
            # Use virtualenv packages installed by vpython, not wpt.
            '--venv=%s' % self.path_finder.chromium_base(),
            '--skip-venv-setup',
            'run',
        ]

    def maybe_set_default_isolated_script_test_output(self):
        if self.options.isolated_script_test_output:
            return
        default_value = self.path_finder.path_from_chromium_base(
            'out', self.options.target, "results.json")
        print("--isolated-script-test-output not set, defaulting to %s" %
              default_value)
        self.options.isolated_script_test_output = default_value

    def generate_test_output_args(self, output):
        return ['--log-chromium=%s' % output]

    def _resolve_tests_from_isolate_filter(self, test_filter):
        """Resolve an isolated script-style filter string into lists of tests.

        Arguments:
            test_filter (str): Glob patterns delimited by double colons ('::').
                The glob is prefixed with '-' to indicate that tests matching
                the pattern should not run. Assume a valid wpt name cannot start
                with '-'.

        Returns:
            tuple[list[str], list[str]]: Tests to include and exclude,
                respectively.
        """
        included_tests, excluded_tests = [], []
        for pattern in common.extract_filter_list(test_filter):
            test_group = included_tests
            if pattern.startswith('-'):
                test_group, pattern = excluded_tests, pattern[1:]
            if self.path_finder.is_wpt_internal_path(pattern):
                pattern_on_disk = self.path_finder.path_from_web_tests(pattern)
            else:
                pattern_on_disk = self.fs.join(self.wpt_root_dir, pattern)
            test_group.extend(glob.glob(pattern_on_disk))
        return included_tests, excluded_tests

    def generate_test_filter_args(self, test_filter_str):
        included_tests, excluded_tests = \
            self._resolve_tests_from_isolate_filter(test_filter_str)
        include_file, self._include_filename = self.fs.open_text_tempfile()
        with include_file:
            for test in included_tests:
                include_file.write(test)
                include_file.write('\n')
        wpt_args = ['--include-file=%s' % self._include_filename]
        for test in excluded_tests:
            wpt_args.append('--exclude=%s' % test)
        return wpt_args

    def generate_test_repeat_args(self, repeat_count):
        return ['--repeat=%d' % repeat_count]

    @property
    def _has_explicit_tests(self):
        # TODO(crbug.com/1356318): `run_wpt_tests` has multiple ways to
        # explicitly specify tests. Some are inherited from wptrunner, the rest
        # from Chromium infra. After we consolidate `run_wpt_tests` and
        # `wpt_common`, maybe we should build a single explicit test list to
        # simplify this check?
        for test_or_option in super().rest_args:
            if not test_or_option.startswith('-'):
                return True
        return (getattr(self.options, 'include', None)
                or getattr(self.options, 'include_file', None)
                or getattr(self.options, 'gtest_filter', None)
                or self._include_filename)

    def generate_test_launcher_retry_limit_args(self, retry_limit):
        return ['--retry-unexpected=%d' % retry_limit]

    def generate_sharding_args(self, total_shards, shard_index):
        return [
            '--total-chunks=%d' % total_shards,
            # shard_index is 0-based but WPT's this-chunk to be 1-based
            '--this-chunk=%d' % (shard_index + 1),
            # The default sharding strategy is to shard by directory. But
            # we want to hash each test to determine which shard runs it.
            # This allows running individual directories that have few
            # tests across many shards.
            '--chunk-type=hash'
        ]

    @property
    def _default_retry_limit(self) -> int:
        return 0 if self._has_explicit_tests else 3

    @property
    def wpt_output(self):
        return self.options.isolated_script_test_output

    def _show_wpt_help(self):
        command = [
            self.select_python_executable(),
        ]
        command.extend(self._wpt_run_args)
        command.extend(['--help'])
        exit_code = common.run_command(command)
        self.parser.exit(exit_code)

    def process_and_upload_results(self):
        command = [
            self.select_python_executable(),
            os.path.join(path_finder.get_blink_tools_dir(),
                         'wpt_process_results.py'),
            '--target',
            self.options.target,
            '--web-tests-dir',
            self.path_finder.web_tests_dir(),
            '--artifacts-dir',
            os.path.join(os.path.dirname(self.wpt_output),
                         self.layout_test_results_subdir),
            '--wpt-results',
            self.wpt_output,
        ]
        if self.options.verbose:
            command.append('--verbose')
        if self.wptreport:
            command.extend(['--wpt-report', self.wptreport])
        return common.run_command(command)

    def do_post_test_run_tasks(self):
        process_return = self.process_and_upload_results()

        if (process_return != exit_codes.INTERRUPTED_EXIT_STATUS
                and self.options.show_results and self.has_regressions()):
            self.show_results_in_browser()

    def show_results_in_browser(self):
        results_file = self.fs.join(self.fs.dirname(self.wpt_output),
                                    self.layout_test_results_subdir,
                                    'results.html')
        self.port.show_results_html_file(results_file)

    def has_regressions(self):
        full_results_file = self.fs.join(self.fs.dirname(self.wpt_output),
                                         self.layout_test_results_subdir,
                                         'full_results.json')
        with self.fs.open_text_file_for_reading(
                full_results_file) as full_results:
            results = json.load(full_results)
        return results["num_regressions"] > 0

    def clean_up_after_test_run(self):
        if self._include_filename:
            self.fs.remove(self._include_filename)
        # Avoid having a dangling reference to the temp directory
        # which was deleted
        self._tmp_dir = None

    def add_extra_arguments(self, parser):
        super().add_extra_arguments(parser)
        parser.description = __doc__
        self.add_mode_arguments(parser)
        self.add_output_arguments(parser)
        self.add_binary_arguments(parser)
        self.add_test_arguments(parser)
        if _ANDROID_ENABLED:
            self.add_android_arguments(parser)
        parser.add_argument(
            '-p',
            '--product',
            dest='product_name',
            default='content_shell',
            # The parser converts the value before checking if it is in choices,
            # so we avoid looking up the class right away.
            choices=sorted(_product_registry, key=len),
            help='Product (browser or browser component) to test.')
        parser.add_argument('--webdriver-binary',
                            type=os.path.abspath,
                            help=('Path of the webdriver binary.'
                                  'It needs to have the same major version '
                                  'as the browser binary or APK.'))
        parser.add_argument('--webdriver-arg',
                            action=WPTPassThroughAction,
                            help='WebDriver args.')
        parser.add_argument(
            '-j',
            '--processes',
            '--child-processes',
            type=lambda processes: max(0, int(processes)),
            help=('Number of drivers to start in parallel. (For Android, '
                  'this number is the number of emulators started.) '
                  'The actual number of devices tested may be higher '
                  'if physical devices are available.)'))
        parser.add_argument('--use-upstream-wpt',
                            action='store_true',
                            help=('Use the upstream wpt, this tag will clone '
                                  'the upstream github wpt to a temporary '
                                  'directory and will use the binary and '
                                  'tests from upstream'))
        parser.add_argument('--flag-specific',
                            choices=sorted(self.port.flag_specific_configs()),
                            metavar='FLAG_SPECIFIC',
                            help='The name of a flag-specific suite to run.')
        parser.add_argument('--no-headless',
                            action='store_false',
                            dest='headless',
                            default=True,
                            help=('Use this tag to not run wptrunner in'
                                  'headless mode'))
        parser.add_argument('--no-show-results',
                            dest="show_results",
                            action='store_false',
                            default=True,
                            help=("Don't launch a browser with results after"
                                  "the tests are done"))
        parser.add_argument(
            '--enable-sanitizer',
            action='store_true',
            help='Only report sanitizer-related errors and crashes.')
        parser.add_argument('-t',
                            '--target',
                            default='Release',
                            help='Target build subdirectory under //out')
        parser.add_argument(
            '--default-exclude',
            action='store_true',
            help=('Only run the tests explicitly given in arguments '
                  '(can run no tests, which will exit with code 0)'))
        parser.add_argument('--enable-leak-detection',
                            action="store_true",
                            help='Enable the leak detection of DOM objects.')

    def add_binary_arguments(self, parser):
        group = parser.add_argument_group(
            'Binary Configuration',
            'Options for configuring the binary under test.')
        group.add_argument('--enable-features',
                           metavar='FEATURES',
                           action=BinaryPassThroughAction,
                           help='Chromium features to enable during testing.')
        group.add_argument('--disable-features',
                           metavar='FEATURES',
                           action=BinaryPassThroughAction,
                           help='Chromium features to disable during testing.')
        group.add_argument('--force-fieldtrials',
                           metavar='TRIALS',
                           action=BinaryPassThroughAction,
                           help='Force trials for Chromium features.')
        group.add_argument('--force-fieldtrial-params',
                           metavar='TRIAL_PARAMS',
                           action=BinaryPassThroughAction,
                           help='Force trial params for Chromium features.')
        return group

    def add_test_arguments(self, parser):
        group = parser.add_argument_group(
            'Test Selection', 'Options for selecting tests to run.')
        group.add_argument('--include',
                           metavar='TEST_OR_DIR',
                           action=WPTPassThroughAction,
                           help=('Test(s) to run. Defaults to all tests, '
                                 "if '--default-exclude' not provided."))
        group.add_argument('--include-file',
                           action=WPTPassThroughAction,
                           type=os.path.abspath,
                           help='A file listing test(s) to run.')
        group.add_argument(
            '--test-filter',
            '--gtest_filter',
            metavar='TESTS_OR_DIRS',
            help='Colon-separated list of test names (URL prefixes).')
        parser.add_argument('--no-wpt-internal',
                            action='store_false',
                            dest='run_wpt_internal',
                            default=True,
                            help='Do not run internal WPTs.')
        return group

    def add_mode_arguments(self, parser):
        group = parser.add_argument_group(
            'Mode', 'Options for wptrunner modes other than running tests.')
        # We provide an option to show wptrunner's help here because the 'wpt'
        # executable may be inaccessible from the user's PATH. The top-level
        # 'wpt' command also needs to have virtualenv disabled.
        group.add_argument('--wpt-help',
                           action='store_true',
                           help='Show the wptrunner help message and exit')
        group.add_argument('--list-tests',
                           nargs=0,
                           action=WPTPassThroughAction,
                           help='List all tests that will run.')
        return group

    def add_output_arguments(self, parser):
        group = parser.add_argument_group(
            'Output Logging', 'Options for controlling logging behavior.')
        group.add_argument('-v',
                           '--verbose',
                           action='count',
                           default=0,
                           help='Increase verbosity')
        group.add_argument('--log-raw',
                           metavar='RAW_REPORT_FILE',
                           action=WPTPassThroughAction,
                           help='Log raw report.')
        group.add_argument('--log-html',
                           metavar='HTML_REPORT_FILE',
                           action=WPTPassThroughAction,
                           help='Log html report.')
        group.add_argument('--log-xunit',
                           metavar='XUNIT_REPORT_FILE',
                           action=WPTPassThroughAction,
                           help='Log xunit report.')
        group.add_argument(
            '--log-wptreport',
            nargs='?',
            # We cannot provide a default, since the default filename depends on
            # the product, so we use this placeholder instead.
            const='',
            help=('Log a wptreport in JSON to the output directory '
                  '(default filename: '
                  'wpt_reports_<product>_<shard-index>.json)'))
        return group

    def add_android_arguments(self, parser):
        group = parser.add_argument_group(
            'Android', 'Options for configuring Android devices and tooling.')
        add_emulator_args(group)
        group.add_argument(
            '--browser-apk',
            # Aliases for backwards compatibility.
            '--chrome-apk',
            '--system-webview-shell',
            type=os.path.abspath,
            help=('Path to the browser APK to install and run. '
                  '(For WebView, this value is the shell. '
                  'Defaults to an on-device APK if not provided.)'))
        group.add_argument('--webview-provider',
                           type=os.path.abspath,
                           help=('Path to a WebView provider APK to install. '
                                 '(WebView only.)'))
        group.add_argument(
            '--additional-apk',
            type=os.path.abspath,
            action='append',
            default=[],
            help='Paths to additional APKs to install.')
        group.add_argument(
            '--release-channel',
            help='Install WebView from release channel. (WebView only.)')
        group.add_argument(
            '--package-name',
            # Aliases for backwards compatibility.
            '--chrome-package-name',
            help='Package name to run tests against.')
        group.add_argument('--adb-binary',
                           type=os.path.abspath,
                           help='Path to adb binary to use.')
        return group

    def wpt_product_name(self):
        # `self.product` may not be set yet, so `self.product.name` is
        # unavailable. `self._options.product_name` may be an alias, so we need
        # to translate it into its wpt-accepted name.
        product_cls = _product_registry[self._options.product_name]
        return product_cls.name

class Product:
    """A product (browser or browser component) that can run web platform tests.

    Attributes:
        name (str): The official wpt-accepted name of this product.
        aliases (list[str]): Human-friendly aliases for the official name.
    """
    name = ''
    aliases = []

    def __init__(self, host, options, python_executable=None):
        self._host = host
        self._path_finder = PathFinder(self._host.filesystem)
        self._options = options
        self._python_executable = python_executable
        self._tasks = contextlib.ExitStack()
        self._validate_options()

    def _path_from_target(self, *components):
        return self._path_finder.path_from_chromium_base(
            'out', self._options.target, *components)

    def _validate_options(self):
        """Validate product-specific command-line options.

        The validity of some options may depend on the product. We check these
        options here instead of at parse time because the product itself is an
        option and the parser cannot handle that dependency.

        The test environment will not be set up at this point, so checks should
        not depend on external resources.

        Raises:
            ValueError: When the given options are invalid for this product.
                The user will see the error's message (formatted with
                `argparse`, not a traceback) and the program will exit early,
                which avoids wasted runtime.
        """

    @contextlib.contextmanager
    def test_env(self):
        """Set up and clean up the test environment."""
        with self._tasks:
            yield

    @property
    def wpt_args(self):
        """list[str]: Arguments to add to a 'wpt run' command."""
        args = []
        version = self.get_version()  # pylint: disable=assignment-from-none
        if version:
            args.append('--browser-version=%s' % version)
        webdriver = self.webdriver_binary
        if webdriver and self._host.filesystem.exists(webdriver):
            args.append('--webdriver-binary=%s' % webdriver)
        return args

    def get_version(self):
        """Get the product version, if available."""
        return None

    @property
    def webdriver_binary(self):
        """Optional[str]: Path to the webdriver binary, if available."""
        return self._options.webdriver_binary

class ChromeBase(Product):
    @property
    def binary(self):
        raise NotImplementedError

    @property
    def wpt_args(self):
        wpt_args = list(super().wpt_args)
        wpt_args.extend([
            '--binary=%s' % self.binary,
            '--processes=%d' % self._options.processes,
        ])
        return wpt_args

class Chrome(ChromeBase):
    name = 'chrome'

    @property
    def binary(self):
        binary_path = 'chrome'
        if self._host.platform.is_win():
            binary_path += '.exe'
        elif self._host.platform.is_mac():
            binary_path = self._host.filesystem.join('Chromium.app',
                                                     'Contents', 'MacOS',
                                                     'Chromium')
        return self._path_from_target(binary_path)

    @property
    def webdriver_binary(self):
        default_binary = 'chromedriver'
        if self._host.platform.is_win():
            default_binary += '.exe'
        return (super().webdriver_binary
                or self._path_from_target(default_binary))

class ContentShell(ChromeBase):
    name = 'content_shell'

    @property
    def binary(self):
        binary_path = 'content_shell'
        if self._host.platform.is_win():
            binary_path += '.exe'
        elif self._host.platform.is_mac():
            binary_path = self._host.filesystem.join('Content Shell.app',
                                                     'Contents', 'MacOS',
                                                     'Content Shell')
        return self._path_from_target(binary_path)

class ChromeiOS(Product):
    name = 'chrome_ios'

    @property
    def wpt_args(self):
        wpt_args = list(super().wpt_args)
        cwt_chromedriver_path = os.path.realpath(
            os.path.join(
                os.path.dirname(__file__),
                '../../../ios/chrome/test/wpt/tools/'
                'run_cwt_chromedriver_wrapper.py'))
        wpt_args.extend([
            '--processes=%d' % self._options.processes,
            '--webdriver-binary=%s' % cwt_chromedriver_path,
        ])
        return wpt_args

@contextlib.contextmanager
def _install_apk(device, path):
    """Helper context manager for ensuring a device uninstalls an APK."""
    device.Install(path)
    try:
        yield
    finally:
        device.Uninstall(path)

class ChromeAndroidBase(Product):
    def __init__(self, host, options, python_executable=None):
        super().__init__(host, options, python_executable)
        self.devices = {}

    @contextlib.contextmanager
    def test_env(self):
        with super().test_env():
            devil_chromium.Initialize(adb_path=self._options.adb_binary)
            if not self._options.adb_binary:
                self._options.adb_binary = devil_env.config.FetchPath('adb')
            devices = self._tasks.enter_context(get_devices(self._options))
            if not devices:
                raise Exception('No devices attached to this host. '
                                "Make sure to provide '--avd-config' "
                                'if using only emulators.')

            self.provision_devices(devices)
            yield

    @property
    def wpt_args(self):
        wpt_args = list(super().wpt_args)
        for serial in self.devices:
            wpt_args.append('--device-serial=%s' % serial)
        package_name = self.get_browser_package_name()
        if package_name:
            wpt_args.append('--package-name=%s' % package_name)
        if self._options.adb_binary:
            wpt_args.append('--adb-binary=%s' % self._options.adb_binary)
        return wpt_args

    def get_version(self):
        version_provider = self.get_version_provider_package_name()
        if self.devices and version_provider:
            # Assume devices are identically provisioned, so select any.
            device = list(self.devices.values())[0]
            try:
                version = device.GetApplicationVersion(version_provider)
                logger.info('Product version: %s %s (package: %r)', self.name,
                            version, version_provider)
                return version
            except CommandFailedError:
                logger.warning(
                    'Failed to retrieve version of %s (package: %r)',
                    self.name, version_provider)
        return None

    @property
    def webdriver_binary(self):
        default_binary = self._path_from_target('clang_x64', 'chromedriver')
        return super().webdriver_binary or default_binary

    def get_browser_package_name(self):
        """Get the name of the package to run tests against.

        For WebView, this package is the shell.

        Returns:
            Optional[str]: The name of a package installed on the devices or
                `None` to use wpt's best guess of the runnable package.

        See Also:
            https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/browser.py#L867-L924
        """
        if self._options.package_name:
            return self._options.package_name
        if self._options.browser_apk:
            with contextlib.suppress(apk_helper.ApkHelperError):
                return apk_helper.GetPackageName(self._options.browser_apk)
        return None

    def get_version_provider_package_name(self):
        """Get the name of the package containing the product version.

        Some Android products are made up of multiple packages with decoupled
        "versionName" fields. This method identifies the package whose
        "versionName" should be consider the product's version.

        Returns:
            Optional[str]: The name of a package installed on the devices or
                `None` to use wpt's best guess of the version.

        See Also:
            https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/run.py#L810-L816
            https://github.com/web-platform-tests/wpt/blob/merge_pr_33203/tools/wpt/browser.py#L850-L924
        """
        # Assume the product is a single APK.
        return self.get_browser_package_name()

    def provision_devices(self, devices):
        """Provisions a set of Android devices in parallel."""
        contexts = [self._provision_device(device) for device in devices]
        self._tasks.enter_context(SyncParallelizer(contexts))

        for device in devices:
            if device.serial in self.devices:
                raise Exception('duplicate device serial: %s' % device.serial)
            self.devices[device.serial] = device
            self._tasks.callback(self.devices.pop, device.serial, None)

    @contextlib.contextmanager
    def _provision_device(self, device):
        """Provision a single Android device for a test.

        This method will be executed in parallel on all devices, so
        it is crucial that it is thread safe.
        """
        with contextlib.ExitStack() as exit_stack:
            if self._options.browser_apk:
                exit_stack.enter_context(
                    _install_apk(device, self._options.browser_apk))
            for apk in self._options.additional_apk:
                exit_stack.enter_context(_install_apk(device, apk))
            logger.info('Provisioned device (serial: %s)', device.serial)
            yield

class WebView(ChromeAndroidBase):
    name = ANDROID_WEBVIEW
    aliases = ['webview']

    def _install_webview(self, device):
        # Prioritize local builds.
        if self._options.webview_provider:
            return webview_app.UseWebViewProvider(
                device, self._options.webview_provider)
        assert self._options.release_channel, 'no webview install method'
        return self._install_webview_from_release(device)

    def _validate_options(self):
        super()._validate_options()
        if not self._options.webview_provider \
                and not self._options.release_channel:
            raise ValueError("Must provide either '--webview-provider' or "
                             "'--release-channel' to install WebView.")

    def get_browser_package_name(self):
        return (super().get_browser_package_name()
                or 'org.chromium.webview_shell')

    def get_version_provider_package_name(self):
        # Prioritize using the webview provider, not the shell, since the
        # provider is distributed to end users. The shell is developer-facing,
        # so its version is usually not actively updated.
        if self._options.webview_provider:
            with contextlib.suppress(apk_helper.ApkHelperError):
                return apk_helper.GetPackageName(
                    self._options.webview_provider)
        return super().get_version_provider_package_name()

    @contextlib.contextmanager
    def _provision_device(self, device):
        with self._install_webview(device), super()._provision_device(device):
            yield

    @contextlib.contextmanager
    def _install_webview_from_release(self, device):
        script_path = self._path_finder.path_from_chromium_base(
            'clank', 'bin', 'install_webview.py')
        command = [
            self._python_executable, script_path, '-s', device.serial,
            '--channel', self._options.release_channel
        ]
        exit_code = common.run_command(command)
        if exit_code != 0:
            raise Exception(
                'failed to install webview from release '
                '(serial: %r, channel: %r, exit code: %d)' %
                (device.serial, self._options.release_channel, exit_code))
        yield

class ChromeAndroid(ChromeAndroidBase):
    name = CHROME_ANDROID
    aliases = ['clank']

    def _validate_options(self):
        super()._validate_options()
        if not self._options.package_name and not self._options.browser_apk:
            raise ValueError(
                "Must provide either '--package-name' or '--browser-apk' "
                'for %r.' % self.name)

def add_emulator_args(parser):
    parser.add_argument(
        '--avd-config',
        type=os.path.realpath,
        help=('Path to the avd config. Required for Android products. '
              '(See //tools/android/avd/proto for message definition '
              'and existing *.textpb files.)'))
    parser.add_argument(
        '--emulator-window',
        action='store_true',
        default=False,
        help='Enable graphical window display on the emulator.')

def _make_product_registry():
    """Create a mapping from all product names (including aliases) to their
    respective classes.
    """
    product_registry = {}
    product_classes = [Chrome, ContentShell, ChromeiOS]
    if _ANDROID_ENABLED:
        product_classes.extend([ChromeAndroid, WebView])
    for product_cls in product_classes:
        names = [product_cls.name] + product_cls.aliases
        product_registry.update((name, product_cls) for name in names)
    return product_registry

_product_registry = _make_product_registry()

@contextlib.contextmanager
def get_device(args):
    with get_devices(args) as devices:
        yield None if not devices else devices[0]

@contextlib.contextmanager
def get_devices(args):
    if not _ANDROID_ENABLED:
        raise Exception('Android is not available')
    instances = []
    try:
        if args.avd_config:
            avd_config = avd.AvdConfig(args.avd_config)
            logger.warning('Installing emulator from %s', args.avd_config)
            avd_config.Install()

            for _ in range(max(args.processes, 1)):
                instance = avd_config.CreateInstance()
                instances.append(instance)

            SyncParallelizer(instances).Start(writable_system=True,
                                              window=args.emulator_window,
                                              require_fast_start=True)

        #TODO(weizhong): when choose device, make sure abi matches with target
        yield device_utils.DeviceUtils.HealthyDevices()
    finally:
        SyncParallelizer(instances).Stop()

def main():
    # This environment fix is needed on windows as codec is trying
    # to encode in cp1252 rather than utf-8 and throwing errors.
    os.environ['PYTHONUTF8'] = '1'

    try:
        adapter = WPTAdapter()
        return adapter.run_test()
    except KeyboardInterrupt:
        return exit_codes.INTERRUPTED_EXIT_STATUS

# This is not really a "script test" so does not need to manually add
# any additional compile targets.
def main_compile_targets(args):
    json.dump([], args.output)

if __name__ == '__main__':
    # Conform minimally to the protocol defined by ScriptTest.
    if 'compile_targets' in sys.argv:
        funcs = {
            'run': None,
            'compile_targets': main_compile_targets,
        }
        sys.exit(common.run_script(sys.argv[1:], funcs))
    sys.exit(main())


from pathlib import Path
from typing import List

from models.custom_model import CustomModel, collection_name
from models.data_models.person_model import Person

@collection_name("theses")
class Thesis(CustomModel):
    title: str
    authors: List[str]
    advisors: List[str]
    keywords: List[str]
    version: int = 0
    balance: int = 2022

    @classmethod
    def from_advisors(cls, advisors: List[Person]):
        names = set([a.name for a in advisors])

        for thesis in cls.all():
            if set(thesis.advisors) & names:
                yield thesis

    def check(self):
        if not self.title:
            raise ValueError("El título no puede ser vacío.")

        if len(self.authors) == 0:
            raise ValueError("Debe tener al menos un autor.")

        if len(self.advisors) == 0:
            raise ValueError("Debe tener al menos un tutor.")

        if len(self.keywords) < 3:
            raise ValueError("Debe tener al menos 3 palabras clave.")

        return True

    def save_thesis_pdf(self, pdf):
        self.version += 1
        name_pdf = f"{self.uuid}_v{self.version}.pdf"
        path: Path = Path(f"/src/data/Thesis/files/{name_pdf}")
        path.parent.mkdir(exist_ok=True, parents=True)

        with path.open("wb") as f:
            f.write(pdf.getbuffer())


#!/usr/bin/env python

from astropy import log
from astropy.io import fits
from astroquery.mast import Observations
from astropy import units
import numpy as np
import os

desc="""
Download tess data from MAST for QLP and SPOC pipelines
"""

from . import tessutils

data_path = os.environ.get('QUICKTESS_DIR', None)

if data_path is None:
    print("Please set the QUICKTESS_DIR environment variable to path ot the downloaded data")

def download(TIC=None, ra=None, dec=None,
             pipeline='QLP',
             download_dir=data_path,
             verbose=False):

    full_path = None
    if TIC is None:
        assert(ra is not None)
        assert(dec is not None)

        obs_table = Observations.query_region(
                f'{ra} {dec}',
                radius=5.0*units.arcsecond, verbose=verbose)
        idx = np.where(obs_table['provenance_name'] == pipeline)[0]
        full_path = []
        if len(idx) != 0:
            TIC_arr = np.array(obs_table['target_name'][idx])
            if len(np.unique(TIC_arr)) != 1:
                idx = [i for i in idx
                       if obs_table['target_name'][i]==obs_table['target_name'][idx[0]] ]

            TIC = obs_table['target_name'][idx[0]]
            for i in range(len(idx)):
                print(obs_table['obs_id'][idx[i]])
                full_path.append(
                        os.path.join(
                                data_path,
                                'mastDownload/HLSP',
                                obs_table['obs_id'][idx[i]],
                                obs_table['obs_id'][idx[i]]+'.fits'))
            if pipeline == 'TESS-SPOC':
                full_path = [ x.replace('_tp.fits', '_lc.fits')
                              for x in full_path ]
        else:
            log.info(f'No TESS data on MAST for {ra} {dec}')
            return None

    obsTable = Observations.query_criteria(provenance_name=pipeline,
                                           target_name=TIC, verbose=verbose)

    if len(obsTable) == 0:
        return None

    data = Observations.get_product_list(obsTable, verbose=verbose)
    with tessutils.HiddenPrints():
        download_lc = Observations.download_products(
                data, download_dir=download_dir)

    if full_path is None:
        if pipeline == 'QLP':
            full_path = [ os.path.join(download_dir, 'mastDownload/HLSP',
                                       x, x+'.fits')
                          for x in obsTable['obs_id'] ]
        elif pipeline == 'TESS-SPOC':
            full_path = [ os.path.join(download_dir, 'mastDownload/HLSP',
                                       x, x.replace('_tp', '_lc.fits'))
                          for x in obsTable['obs_id'] ]
        else:
            raise ValueError(
                    f'pipeline name {pipeline} not recognized')

    return tessutils.sort_paths(full_path)


'''
Author: Ri Hong
Date AC'd: June 27, 2020
'''
#Explanation
'''
This problem has two quirks we need to look out for before we even start solving the problem.
1) The starting positions of the knight and the pawn are in real-world coordinates, not programming indexes.
Meaning, if we have a 3x3 grid, the first row would be index 0, second row wold be index 1 and third row would
be index 2. However, In the real world, if you wanted to reference the top left most cell, you would say the cell
on row 1 and column 1 not row 0 and column 0. So for example, if the test case was:
1
3
3
1
1
2
3
pr (starting row of the pawn) is 1 and pc (starting column of the pawn) is also 1. But in the 2d array, you would want the
starting cell to be array_name[0][0]

2) The row and column number system is also not the same as programming indexes. In programming, if we have a 2d array, we count
from left to right and top to bottom. Heres the grid coordinates as indexes:
(0,0) (0,1) (0,2)
(1,0) (1,1) (1,2)
(2,0) (2,1) (2,2)
However, the problem specifies that Row 1 is at the bottom of the board and Row r is at the top of the board. Column 1 is at the
left and column c is at the right. Their number system is as follows:
(3,1) (3,2) (3,3)
(2,1) (2,2) (2,3)
(1,1) (1,2) (1,3)
Converting to programming indexes gives us:
(2,0) (2,1) (2,2)
(1,0) (1,1) (1,2)
(0,0) (0,1) (0,2)

So basiclly, we have to invert the row numbers and subtract each coordinate the problem gives us by one. Doing so will
allow us to get the identical board and starting positions indexed in programming indexes.

Now let's move onto the problem
We construct a 2D array called "board" with the exact dimensions of the chessboard. All cells of board are initially
set to 10000. Each cell of board will store the number of moves it will take the knight to reach that cell. The knight's starting
cell will be set to 0. We can use BFS to fill the cells for the knight
In the case of:
1
3
3
1
1
2
3
Initially, the board will be
10000 10000 10000
10000 10000 0
10000 10000 10000
because the knight starts on row 2 col 3 (board[1][2]). Then the board will be
1 10000 10000
10000 10000 0
1 10000 10000
because the knight can move 1 space up/down and two spaces left. Next,
1 2 10000
10000 10000 0
1 2 10000
The knight at (0, 0) moves to (2, 1) and the knight at (2, 0) moves to (0, 1). Next,
1 2 3
10000 10000 0
1 2 3
The knight at (0, 1) moves to (2, 2) and the knight at (2, 1) moves to (0, 2). Finally,
1 2 3
4 10000 0
1 2 3
Either the knight at (2, 2) and the knight at (0, 2) moves to (1, 0).
Note that it is impossible to get to (1, 1) so the value at (1, 1) remains as 10000.

Now that we have a 2d array with the knight's movements, we can deal with the pawn's movements. We check
each cell above the pawn and along the way, we keep track of how many cells the pawn has moved. If the pawn
can reach a cell on the same number of moves as the knight, then the knight wins. If the knight can reach a cell
that in n moves and the pawn reaches the cell below it in n moves, then a stalemate is achieved. If there is a win,
then then we dont have to look at the stalemate moves or losses. If there is no win, then we check if there is a stalemate.
If there is no win or no stalemate, then we process a loss.

There is a problem we need to solve with this method. So far we only assume that the knight cannot travel back to visited cells.
Consider this testcase: let K represent the starting cell of the knight and P the starting cell of the pawn and . empty cells
. .
. .
. .
K .
. P
After we handle the knight travelling, board would look like:
10000 10000
10000 1
10000 10000
0 10000
10000 10000
or
. .
. 1
. .
0 .
. P
Using our current algorithm, it would determine that it would be impossible for the knight to win. However, the knight can win if it:
starts on (3, 0)
move to (1, 1)
move back to (3, 0)
move back to (1, 1) to capture the pawn

As you can see, we need handle the knight being able to travel back to visited cells. This is how we can account for this:
1) Check if it is even possible for the knight to reach our current cell before the pawn. Do this by checking if our current cell on
the board holds a value that is greater than the number of moves it takes the pawn to get there. If its not, it means that the pawn
arrives earlier than the knight and thus we do not need to check further. If it is, it means that the knight will arrive at that cell
before the pawn. Next we check if the knight can arrive at the cell on the same turn as the pawn. We can do this by using the mod function.
we mod the value of our current cell by 2 and do the same with the number of moves it takes the pawn to reach the same cell. if the two modded
values are the same, then it means the knight can reach that specific cell on the same turn as the pawn by cycling to already visited cells.
Consider the previous testcase.
. .
. .
. .
K .
. P
With the knight starting on cell (3, 0), it can reach cell (1, 1) on turns: 1, 3, 5, 7..... We can observe that the knight can reach that cell on every odd turn,
so as long as the pawn reaches that cell on any odd turn, the pawn will be captured. The pawn can arrive on turn 1, 3, 9, 213712893123121, it doesn't matter, as long
as it is odd. The same is true with even turns. If a knight can arrive on a cell on an even turn before the pawn, the pawn will be captured if it arrives on an even turn.

'''

#Code
#get input
numGames = int(input()) #get the number of games as an integer
for i in range(numGames): #loop through each game
  r = int(input()) #get the number of rows of the chessboard
  c = int(input()) #get the number of columns of the chessboard
  pr = r - int(input()) #get the starting row of the pawn but adjust it to be the programming grid
  pc = int(input()) - 1 #get the starting column of the pawn but adjust it to be the programming grid
  kr = r - int(input()) #get the starting row of the knight but adjust it to be the programming grid
  kc = int(input()) - 1 #get the starting column of the knight but adjust it to be the programming grid
  win = 0 #this stores how many moves it takes the knight to capture the pawn. If it's 0, it means the knight has not captured the pawn
  stalemate = 10000 #if this value is 10000 (the initial starting value), then that means there is no stalemate. Otherwise, this will store the number of knight moves it takes to stalemate
  board = [[10000 for x in range(c)] for x in range(r)] #create a 2d array with r rows and c columns and fill each cell with 10000
  board[kr][kc] = 0 #initialize the starting cell of the knight to be 0
  queue = [[kr,kc]] #this list stores the list of knight moves that need to be processed on a specific turn

  #fill board with knight positions using BFS
  while queue: #while there are still values in the queue
    row, col = queue.pop(0) #take out the first set out coordinates and assign them to row and col. row will be the index for rows and col will be the index for cols
    valueOfCurrSquare = board[row][col] #get the value of the cell and assign it to valueOfCurrSquare

    #now we process each possible movement of the knight starting from row, col
    #up 1 left 2
    if row > 0 and col > 1: #make sure there is enough room to go up 1 left 2
      if board[row - 1][col - 2] > valueOfCurrSquare + 1: #if the value of the cell of up 1 left 2 is greater than than the number of moves it will take us to get there from our current position (valueOfCurrSquare + 1)
        board[row - 1][col - 2] = valueOfCurrSquare + 1 #update the cell at up 1 left 2 to valueOfCurrSquare + 1
        queue.append([row - 1, col - 2]) #add the coordinates at up 1 left 2 to the queue
    #do the same checks for the other possible positions the knight can move to
    #up 2 left 1
    if row > 1 and col > 0:
      if board[row - 2][col - 1] > valueOfCurrSquare + 1:
        board[row - 2][col - 1] = valueOfCurrSquare + 1
        queue.append([row - 2, col - 1])
    #up 1 right 2
    if row > 0 and col < c - 2: #remember, c is the number of columns in the chessboard
      if board[row - 1][col + 2] > valueOfCurrSquare + 1:
        board[row - 1][col + 2] = valueOfCurrSquare + 1
        queue.append([row - 1, col + 2])
    #up 2 right 1
    if row > 1 and col < c - 1:
      if board[row - 2][col + 1] > valueOfCurrSquare + 1:
        board[row - 2][col + 1] = valueOfCurrSquare + 1
        queue.append([row - 2, col + 1])
    #down 1 left 2
    if row < r - 1 and col > 1: #remember, r is the number of rows in the chessboard
      if board[row + 1][col - 2] > valueOfCurrSquare + 1:
        board[row + 1][col - 2] = valueOfCurrSquare + 1
        queue.append([row + 1, col - 2])
    #down 2 left 1
    if row < r - 2 and col > 0:
      if board[row + 2][col - 1] > valueOfCurrSquare + 1:
        board[row + 2][col - 1] = valueOfCurrSquare + 1
        queue.append([row + 1, col - 1])
    #down 1 right 2
    if row < r - 1 and col < c - 2:
      if board[row + 1][col + 2] > valueOfCurrSquare + 1:
        board[row + 1][col + 2] = valueOfCurrSquare + 1
        queue.append([row + 1,col + 2])
    #down 2 right 1
    if row < r - 2 and col < c - 1:
      if board[row + 2][col + 1] > valueOfCurrSquare + 1:
        board[row + 2][col + 1] = valueOfCurrSquare + 1
        queue.append([row + 2,col + 1])

  #process the pawn movements pr is the pawn's starting row, pc is the pawn's starting column
  pawnMoveCounter = 0 #stores the number of moves the pawn has made
  for row in range(pr, 0, -1): #loop through all the rows above the pawn's starting row in reverse. E.g. if the pawn started on row 4, we want to loop through 4, 3, 2, 1, 0
    if board[row][pc] == pawnMoveCounter: #if the pawn and knight arrive to the same cell at on the same move
      win = pawnMoveCounter #knight wins. Assign the number of moves it takes to win to the variable win
      break #exit the loop
    elif board[row - 1][pc] == pawnMoveCounter: #if the knight lands on the cell ahead of the pawn on the same move (stalemate)
      if pawnMoveCounter < stalemate: #if the number of moves it takes to stalemate is less than the stored number of moves to stalemate
        stalemate = pawnMoveCounter #set the number of moves it will take to achieve a stalemate to the variable stalemate
    #deal with cycles
    elif pawnMoveCounter > 1: #only check for cycles if the pawn moves is greater than 1. This is because there is no way a knight can cycle when a pawn has only moved once or less
      if board[row][pc] < pawnMoveCounter: #check if the knight can arrive earlier than the pawn. Aka check if the value of the cell on board is less than the # of moves the pawn has taken
        if board[row][pc] % 2 == pawnMoveCounter % 2: #check if the knight and the pawn can arrive at the same cell in an odd or even number of moves. If they are both odd or both even:
          win = pawnMoveCounter #the knight wins. Store the number of moves it took to win in the variable "win"
          break #no need to check further. Exit the loop
        elif board[row - 1][pc] % 2 == pawnMoveCounter % 2: #check if the knight can arrive at the cell above the pawn in an odd or even number of moves. If both arrive in an odd or even number of moves:
          if pawnMoveCounter < stalemate: #if the number of moves it takes to stalemate is less than the stored number of moves to stalemate
            stalemate = pawnMoveCounter #set the number of moves it will take to achieve a stalemate to the variable stalemate

    #increment the number of pawn moves before going onto the next iteration of the loop
    pawnMoveCounter += 1

  #handle output
  if win: #if win has a value other than 0 (the initial value), that means a win has been achieved
    print ("Win in", win, "knight move(s).") #print how many moves it took to win (value that's stored in the variable "win")
  elif stalemate != 10000: #if stalemate has a value other than 10000, that means a stalemate has been achieved
    print ("Stalemate in", stalemate, "knight move(s).") #print the number of moves it took to stalemate (value that's stored in stalemate)
  else: #if there is no win or no stalemate, then it must be a loss
    print ("Loss in", r - (r - pr) - 1, "knight move(s).") #print the number of rows the pawn has to travel to get to the top row. (aka # moves it took to produce a loss)

CCC '99/ CCC '99 S4 - A Knightly Pursuit.py


import logging
from pathlib import Path
from typing import List

import numpy as np
import pandas as pd
import pytorch_lightning as pl
import torch
import torchvision
from torch.utils.data.sampler import SubsetRandomSampler

logger = logging.getLogger(__name__)

class FolderDataset(torchvision.datasets.ImageFolder):
    def __init__(self, root: str, transform=[], target_transform=[]):
        transform = torchvision.transforms.Compose(transform)
        target_transform = torchvision.transforms.Compose(target_transform)
        super().__init__(root=root, transform=transform, target_transform=target_transform)

    def to_csv(self, filename, indices: List[int] = None):
        df = pd.DataFrame(self.samples, columns=["filename", "label"])
        df["class"] = df["label"].apply(lambda x: self.classes[x])
        if indices:
            df = df.iloc[indices]
        df.to_csv(filename, index=False)

class FolderDataModule(pl.LightningDataModule):
    def __init__(self, root_dir, train_transforms=[], val_transforms=[], val_size=0.1, batch_size=16):
        super().__init__()
        self.root_dir = root_dir
        self.transforms = train_transforms
        if val_transforms:
            logger.info("val_transforms not supported for this dataset, using train_transforms for all datasets")

        self.val_size = val_size
        self.batch_size = batch_size

    def prepare_data(self):
        self.dataset = FolderDataset(self.root_dir, transform=self.transforms)
        train_idx, valid_idx = self.get_sampler_indices(self.val_size)
        self.train_sampler = SubsetRandomSampler(train_idx)
        self.val_sampler = SubsetRandomSampler(valid_idx)

    def get_sampler_indices(self, val_size=0.1, shuffle=True, random_seed=14):
        num_train = len(self.dataset)
        indices = list(range(num_train))
        split = int(np.floor(val_size * num_train))

        if shuffle:
            np.random.seed(random_seed)
            np.random.shuffle(indices)

        train_idx, valid_idx = indices[split:], indices[:split]
        return train_idx, valid_idx

    def train_dataloader(self):
        return torch.utils.data.DataLoader(
            self.dataset, batch_size=self.batch_size, sampler=self.train_sampler, num_workers=4
        )

    def val_dataloader(self):
        return torch.utils.data.DataLoader(
            self.dataset, batch_size=self.batch_size, sampler=self.val_sampler, num_workers=4
        )

    @property
    def len_train(self):
        return len(self.train_sampler)

    @property
    def len_valid(self):
        return len(self.val_sampler)

    def to_csv(self, output_dir):
        train_csv = Path(output_dir, "train.csv")
        self.dataset.to_csv(train_csv, indices=self.train_sampler.indices)

        val_csv = Path(output_dir, "val.csv")
        self.dataset.to_csv(val_csv, indices=self.val_sampler.indices)


import os
import numpy
import string
import cv2
import argparse
import captcha.image
from gtts import gTTS
import librosa
import librosa.display
from matplotlib import pylab
from pylab import *
import random

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--width', help='Width of captcha image', type=int)
    parser.add_argument('--height', help='Height of captcha image', type=int)
    parser.add_argument('--length', help='Length of captchas in characters', type=int)
    parser.add_argument('--count', help='How many captchas to generate', type=int)
    parser.add_argument('--output-dir', help='Where to store the generated captchas', type=str)
    parser.add_argument('--symbols', help='File with the symbols to use in captchas', type=str)
    args = parser.parse_args()

    if args.width is None:
        print("Please specify the captcha image width")
        exit(1)

    if args.height is None:
        print("Please specify the captcha image height")
        exit(1)

    if args.length is None:
        print("Please specify the captcha length")
        exit(1)

    if args.count is None:
        print("Please specify the captcha count to generate")
        exit(1)

    if args.output_dir is None:
        print("Please specify the captcha output directory")
        exit(1)

    if args.symbols is None:
        print("Please specify the captcha symbols file")
        exit(1)

    # captcha_generator = captcha.image.ImageCaptcha(width=args.width, height=args.height)

    symbols_file = open(args.symbols, 'r')
    captcha_symbols = symbols_file.readline().strip()
    symbols_file.close()

    print("Generating captchas with symbol set {" + captcha_symbols + "}")

    if not os.path.exists(args.output_dir):
        print("Creating output directory " + args.output_dir)
        os.makedirs(args.output_dir)

    for i in range(args.count):
        captcha_text = ''.join([random.choice(captcha_symbols) for j in range(args.length)])
        image_path = os.path.join(args.output_dir, captcha_text+'.png')
        audio_path = os.path.join(args.output_dir, captcha_text + '.mp3')
        if os.path.exists(image_path):
            version = 1
            while os.path.exists(os.path.join(args.output_dir, captcha_text + '_' + str(version) + '.png')):
                version += 1
            image_path = os.path.join(args.output_dir, captcha_text + '_' + str(version) + '.png')
            audio_path = os.path.join(args.output_dir, captcha_text + '_' + str(version) + '.mp3')

        # image = numpy.array(captcha_generator.generate_image(captcha_text))
        # cv2.imwrite(image_path, image)

        audio = gTTS(text=captcha_text, lang='en', slow=False)
        audio.save(audio_path)

        sig, fs = librosa.load(audio_path)
        pylab.figure(figsize=(1.28, 0.64), dpi=100)
        pylab.axis('off')
        pylab.axes([0., 0., 1., 1.], frameon=False, xticks=[], yticks=[])
        S = librosa.feature.melspectrogram(y=sig, sr=fs)
        librosa.display.specshow(librosa.power_to_db(S, ref=np.max))
        save_path = image_path
        pylab.savefig(save_path, bbox_inches=None, pad_inches=0)
        pylab.close()

        os.unlink(audio_path)

        print(i)

if __name__ == '__main__':
    main()


from playsound import playsound
import random

while(1):
    seq_len = input("请输入序列长度：")
    if len(seq_len) > 0:
        seq_len = int(seq_len)
        break

while(1):
    sequence = []
    for i in range(seq_len):
        num = random.randint(0, 9)
        sequence.append(num)

    for i in sequence:
        filename = "number_audios/audio_%d.mp3"%(i)
        playsound(filename)

    sequence_test= list(map(int, list(input("请输入听到的数字序列："))))

    print("实际值：" + "".join(list(map(str, sequence))))
    print("测量值：" + "".join(list(map(str, sequence_test))))

    print("测试结果：" + str(sequence_test == sequence))

    control = input("继续Y/退出n:")
    if control == "" or control == "Y" or control == "y":
        continue
    else:
        break