import numpy as np
#import ExoMinerArch as ExoMiner

import torch.nn as nn
import FeatureEngineering as FE
import GatherData as gd
import torch.optim as optim
import pandas as pd
import matplotlib.pyplot as plt
import shutil
import glob

#import MyDataSet as D
#import RawDataSet as R
from torch.utils.data import DataLoader
import torch

from torchmetrics.classification import BinaryPrecision, BinaryRecall,BinaryAccuracy,BinaryStatScores,Precision , BinaryAUROC,BinaryF1Score
import ExoMinerRawVariation as ExoMinerRaw
def GenRandomTensor(size):
    return torch.rand(size)
def RandTestModel(seed=None):
    if (seed == None):
        pass
    else:
        torch.manual_seed(seed)
    f1 = GenRandomTensor((1, 1, 301))  # feature 1 input data
    f2 = GenRandomTensor((1, 1, 31))  # feature 2 input data
    f2_2 = GenRandomTensor((1, 1))  # feature 2 transit depth
    f3 = GenRandomTensor((1, 1, 301))  # feature 3 input data
    f4 = GenRandomTensor((1, 1, 31))  # feature 4 input data
    f4_2 = GenRandomTensor((1, 5))  # feature 4 centroid scalars
    f5_odd = GenRandomTensor((1, 1, 31))  # feature 5 odd input data
    f5_even = GenRandomTensor((1, 1, 31))  # feature 5 even input data
    f6 = GenRandomTensor((1, 1, 31))  # feature 6 input data
    f6_2 = GenRandomTensor((1, 4))  # feature 6 secondary scalars
    f7 = GenRandomTensor((1, 6))  # feature 7 stellar parameters
    f8 = GenRandomTensor((1, 6))  # feature 7 DV Diagnostic tests
    model = ExoMiner.ExoMiner()
    out = model.forward(f1, f2, f2_2, f3, f4, f4_2, f5_odd, f5_even, f6, f6_2, f7, f8)
    return out
def GenerateFeatures(ID, mish, Plot):
    Data = FE.Features(mish=mish, max_number_of_obs=3, Chosen_ID=ID, Plot=Plot)
    return Data
def Test():
    out = RandTestModel(1)
    print(out)
    return None
def GetRandomID(DataObject, label='CAND'):
    ValidCriteria = DataObject.IdData['WrapperDispo'][DataObject.IdData['WrapperDispo'] == label]
    idx = np.random.choice(ValidCriteria.index.values)
    return idx

def ReshapeEnsureNotNan(Feature, shape,nan=0):
    Feature[Feature==0] = nan
    Feature[Feature < 0]= nan
    return torch.tensor(np.nan_to_num(Feature,nan = nan)).reshape(shape).float()
def ReshapeEnsureNotNan2D(Feature,shape,nan=0):
    f1 = ReshapeEnsureNotNan(Feature[0], (shape[0],1,shape[2]),nan=nan)
    f2 = ReshapeEnsureNotNan(Feature[1], (shape[0], 1,shape[2]), nan=nan)
    return torch.cat((f1, f2), 1)
def UnPackData(mission,fp='FeatureData/'):
    files = glob.glob('./'+fp+mission+'/'+mission+r'Features_*.npy')
    idxs = ['load' not in file for file in files]
    f = []
    for i in range (0,len(idxs)):
        if(idxs[i]):
            f.append(files[i])
        else:
            pass
    files = f
    files.sort()
    data_return = {}
    for file in files:
        shutil.copyfile(file , file[:-4]+'_load.npy')
        data = np.load(file[:-4]+'_load.npy', allow_pickle=True).item()
        for id in data.keys():
            data_return[id] = data[id]
    return data_return

def DispoFunc(dispo):
    if(dispo=='CAND' or dispo=='UNK'):
        return .5
    elif(dispo =='CONF'):
        return 1
    else:
        return 0

def ReduceSetForBatchSize(data,batch_size,set_type):
    #r = len(data) % batch_size
    #data = data.sample(n=int(len(data)-r),random_state = 1)
    #print('Removing {} ids from {} to use batch size of {}'.format(r,set_type,batch_size))
    #data = data.sample(n=int(len(data) - 0), random_state=1)
    return data

def OverSample(CPs ,FPs):
    # oversample
    len_cp =CPs.__len__()
    len_fp = FPs.__len__()
    if(len_cp <=len_fp):
        df_small=CPs
        df_large=FPs
        len_small = df_small.__len__()
        len_large = df_large.__len__()
        ret =1
    else:
        df_small  = FPs
        df_large  = CPs
        len_small = df_small.__len__()
        len_large = df_large.__len__()
        ret = 2
    len_delta = int(len_large - len_small)
    df_small_temp = df_small.sample(n=len_delta , random_state=1 , replace=True)
    df_small_new = pd.concat([df_small,df_small_temp]).sample(frac=1, random_state=1, replace=False)
    assert df_small_new.index.nunique() ==df_small.index.nunique()
    assert df_small_new.__len__() == df_large.__len__()
    if(ret==1):
        # small = CP
        return df_small_new ,df_large
    else:
        # large = CP
        return df_large,df_small_new

def UnderSample(CPs,FPs):
    num_conf = np.floor(CPs.__len__())
    num_false = np.floor(FPs.__len__())
    num_conf = int(num_conf)
    num_false = int(num_false)
    n = min(num_conf, num_false)
    CPs = CPs.sample(n=n, random_state=1)
    FPs = FPs.sample(n=n, random_state=1)
    return CPs,FPs

def ClassImbalance(CPs,FPs,type_sample='Oversample'):

    if(type_sample!='None'):
        if(type_sample=='Oversample'):
            CPs,FPs = OverSample(FPs, CPs)
        elif(type_sample=='Undersample'):
            CPs, FPs = UnderSample(FPs, CPs)
    else:
        pass
    return  CPs,FPs

def GetTrainingAndValidationSets(CPs,FPs,training_size):
    CPs_for_training = CPs.sample(frac=training_size, random_state=1, replace=False)
    FPs_for_training = FPs.sample(frac=training_size, random_state=1, replace=False)
    #

    indexs_in_CP_train = CPs_for_training.index
    indexs_in_FP_train = FPs_for_training.index
    #
    CP_bool_array = ~CPs.index.isin(indexs_in_CP_train)
    FP_bool_array = ~FPs.index.isin(indexs_in_FP_train)
    #
    CPs_for_validation = CPs[CP_bool_array]
    FPs_for_validation = FPs[FP_bool_array]
    return CPs_for_training ,FPs_for_training ,CPs_for_validation ,FPs_for_validation

def GrabDatasets(data,DataObject,training_size,batch_size,Combo = True,type_sample='Oversample',Raw=False):
    df = pd.DataFrame(data).T
    Ids = DataObject.IdData['WrapperId']
    Dispo = DataObject.IdData['WrapperDispo']
    Dispo = pd.DataFrame(Dispo.values, columns=['Disposition'])
    Ids = pd.DataFrame(Ids.values, columns=['ID'])
    df2 = pd.concat([Dispo, Ids], axis=1)
    df2 = df2.set_index('ID')
    df = pd.concat([df, df2], axis=1)
    targets = pd.DataFrame([DispoFunc(x) for x in df.Disposition.values], columns=['Target'], index=df.index)
    df = pd.concat([df, targets], axis=1)
    known_set = df[df['Target'] != .5]
    unk_set = df[df['Target']==.5]
    CPs = known_set[known_set['Target']==1]
    FPs = known_set[known_set['Target']==0]
    CPs_for_training ,FPs_for_training ,CPs_for_validation ,FPs_for_validation =GetTrainingAndValidationSets(CPs, FPs, training_size)
    CPs_for_training, FPs_for_training = ClassImbalance(CPs_for_training, FPs_for_training,type_sample=type_sample)
    train_data = pd.concat([CPs_for_training,FPs_for_training]).sample(frac=1,random_state=1)
    #train_data = ReduceSetForBatchSize(train_data,batch_size,set_type='Training data')
    valid_data = pd.concat([CPs_for_validation, FPs_for_validation]).sample(frac=1, random_state=1)
    #valid_data = ReduceSetForBatchSize(valid_data, batch_size,set_type='Validation data')
    #unk_set = ReduceSetForBatchSize(unk_set, batch_size, set_type='Candidate data')
    if(Raw==False):
        TrainDataset = D.MyDataset(train_data)
        ValidDataset = D.MyDataset(valid_data)
        CandDataset = D.MyDataset(unk_set)
    elif(Raw==True):
        TrainDataset = R.RawDataset(train_data)
        ValidDataset = R.RawDataset(valid_data)
        CandDataset = R.RawDataset(unk_set)
    if(type_sample!='None'):
        weight=torch.tensor([1])
    else:

        weight =  torch.tensor(
            (len(known_set['Target']) - known_set['Target'].sum())
            /len(known_set['Target'])
        )
    if(Combo):
        return train_data ,valid_data ,unk_set ,weight
    else:
        return TrainDataset ,ValidDataset ,CandDataset ,weight

def train_one_epoch(epoch_index, training_loader,optimizer,model,criterion,mission):
    running_loss = 0.
    last_loss = 0.
    batch_size = training_loader.batch_size

    # Here, we use enumerate(training_loader) instead of
    # iter(training_loader) so that we can track the batch
    # index and do some intra-epoch reporting
    mission_flag = 'Raw' in mission  # if true then running new model
    for i, data in enumerate(training_loader):
        # Every data instance is an input + label pair
        inputs, labels,_ = data

        # Zero your gradients for every batch!
        optimizer.zero_grad()

        # Make predictions for this batch

        if(mission_flag):
            p = np.random.permutation(len(_))
            inputs = [inputs[0][p], inputs[1][p], inputs[2][p], inputs[3][p], inputs[4][p], inputs[5][p],
                            inputs[6][p],inputs[7][p]]
            labels = labels[p]
            outputs = model(
                flux     = ReshapeEnsureNotNan(inputs[0], shape=(batch_size, 1, 6000)),              #flux
                centroid = ReshapeEnsureNotNan2D([inputs[1],inputs[2]], shape=(batch_size, 2, 6001)) , #centroid data
                f2_2 = ReshapeEnsureNotNan(inputs[3], shape=(batch_size, 1)),                        #transit depth
                f4_2 = ReshapeEnsureNotNan(inputs[4], shape=(batch_size, 5)),                        #centroid scalars
                f6_2 = ReshapeEnsureNotNan(inputs[5], shape=(batch_size, 4)),                        #secondary Scalars
                f7   = ReshapeEnsureNotNan(inputs[6], shape=(batch_size, 6)),                        #Stellar parameters
                f8   = ReshapeEnsureNotNan(inputs[7], shape=(batch_size, 6))                        #DV Diagnosti                                                            #model Vector
            )
        else:
            p = np.random.permutation(len(_))
            inputs = [inputs[0][p], inputs[1][p], inputs[2][p], inputs[3][p], inputs[4][p], inputs[5][p],
                      inputs[6][p], inputs[7][p],inputs[8][p],inputs[9][p],inputs[10][p],inputs[11][p]]
            labels = labels[p]
            #run ExoMiner
            outputs=model(f1=ReshapeEnsureNotNan(inputs[0], shape=(batch_size, 1, 301)),
                  f2=ReshapeEnsureNotNan(inputs[1], shape=(batch_size, 1, 31)),
                  f2_2=ReshapeEnsureNotNan(inputs[2], shape=(batch_size, 1)),
                  f3=ReshapeEnsureNotNan(inputs[3], shape=(batch_size, 1, 301)),
                  f4=ReshapeEnsureNotNan(inputs[4], shape=(batch_size, 1, 31)),
                  f4_2=ReshapeEnsureNotNan(inputs[5], shape=(batch_size, 5)),
                  f5_odd=ReshapeEnsureNotNan(inputs[6], shape=(batch_size, 1, 31)),
                  f5_even=ReshapeEnsureNotNan(inputs[7], shape=(batch_size, 1, 31)),
                  f6=ReshapeEnsureNotNan(inputs[8], shape=(batch_size, 1, 31)),
                  f6_2=ReshapeEnsureNotNan(inputs[9], shape=(batch_size, 4)),
                  f7=ReshapeEnsureNotNan(inputs[10], shape=(batch_size, 6)),
                  f8=ReshapeEnsureNotNan(inputs[11], shape=(batch_size, 6)))

        # Compute the loss and its gradients
        labels = [torch.tensor([l]) for l in labels]
        loss = criterion(outputs, torch.tensor([labels]).reshape(outputs.shape).float())
        loss.backward()

        # Adjust learning weights
        optimizer.step()

        # Gather data and report
        running_loss += loss.item()

    return running_loss/len(training_loader.sampler)

def TrainAndValidate(NumEpochs,training_loader,validation_loader,optimizer,model,criterion,mission):
    epoch_number = 0
    batch_size = training_loader.batch_size
    TrainingLoss, ValidationLoss = [],[]
    writer = SummaryWriter()
    mission_flag = 'Raw' in mission # if true then running new model
    if(mission_flag):
        best_vloss =.61
    else:
        best_vloss =.47
    for epoch in range(NumEpochs):
        #print('EPOCH {}:'.format(epoch_number + 1))

        # Make sure gradient tracking is on, and do a pass over the data
        model.train(True)
        training_loss = train_one_epoch(epoch_number,training_loader,optimizer,model,criterion,mission)

        # We don't need gradients on to do reporting
        model.train(False)

        running_vloss = 0.0
        vdata_arr = []
        vlabels_arr = []
        model.eval()
        with torch.no_grad():
            for i, vdata in enumerate(validation_loader):
                vinputs, vlabels,_ = vdata
                if(mission_flag):
                    #run our model

                    voutputs = model(
                        flux=ReshapeEnsureNotNan(vinputs[0], shape=(batch_size, 1, 6000)),  # flux
                        centroid=ReshapeEnsureNotNan2D([vinputs[1], vinputs[2]], shape=(batch_size, 2, 6001)),
                        # centroid data
                        f2_2=ReshapeEnsureNotNan(vinputs[3], shape=(batch_size, 1)),  # transit depth
                        f4_2=ReshapeEnsureNotNan(vinputs[4], shape=(batch_size, 5)),  # centroid scalars
                        f6_2=ReshapeEnsureNotNan(vinputs[5], shape=(batch_size, 4)),  # secondary Scalars
                        f7=ReshapeEnsureNotNan(vinputs[6], shape=(batch_size, 6)),  # Stellar parameters
                        f8=ReshapeEnsureNotNan(vinputs[7], shape=(batch_size, 6))  # DV Diagnostics
                          # model Vector
                    )
                else:
                    #run exominer
                    voutputs = model(f1=ReshapeEnsureNotNan(vinputs[0], shape=(batch_size, 1, 301)),
                          f2=ReshapeEnsureNotNan(vinputs[1], shape=(batch_size, 1, 31)),
                          f2_2=ReshapeEnsureNotNan(vinputs[2], shape=(batch_size, 1)),
                          f3=ReshapeEnsureNotNan(vinputs[3], shape=(batch_size, 1, 301)),
                          f4=ReshapeEnsureNotNan(vinputs[4], shape=(batch_size, 1, 31)),
                          f4_2=ReshapeEnsureNotNan(vinputs[5], shape=(batch_size, 5)),
                          f5_odd=ReshapeEnsureNotNan(vinputs[6], shape=(batch_size, 1, 31)),
                          f5_even=ReshapeEnsureNotNan(vinputs[7], shape=(batch_size, 1, 31)),
                          f6=ReshapeEnsureNotNan(vinputs[8], shape=(batch_size, 1, 31)),
                          f6_2=ReshapeEnsureNotNan(vinputs[9], shape=(batch_size, 4)),
                          f7=ReshapeEnsureNotNan(vinputs[10], shape=(batch_size, 6)),
                          f8=ReshapeEnsureNotNan(vinputs[11], shape=(batch_size, 6)))

                vlabels = [torch.tensor([l]) for l in vlabels]
                vloss = criterion(voutputs, torch.tensor([vlabels]).reshape(voutputs.shape).float())
                running_vloss += vloss
                vdata_arr.append([voutputs.detach().numpy()])
                vlabels_arr.append([vlabels])
            vdata_arr= torch.as_tensor(vdata_arr).reshape(torch.as_tensor(vdata_arr).shape[0:3])
            vlabels_arr = torch.as_tensor(vlabels_arr).reshape(vdata_arr.shape)
            # plot epochs used for testing
            plot_epochs = [10,20,50,75,100]
            validation_loss = running_vloss / len(validation_loader.sampler)
            print('Epoch {}: LOSS train {} valid {}'.format(epoch_number + 1, training_loss, validation_loss))
            TrainingLoss.append(training_loss)
            ValidationLoss.append(validation_loss.detach().item())
            writer.add_scalars(f'Loss/LossPlots_{batch_size}_{optimizer.param_groups[0]["lr"]}_{NumEpochs}', {
                'train': training_loss,
                'validation': validation_loss }
                               ,epoch_number)

                # p_val_dict = {}
                # a_val_dict = {}
                # r_val_dict = {}
                # fpr_val_dict ={}
                # for t in np.arange(0,1,.01):
                #     percision = BinaryPrecision(threshold=t)
                #     recall =BinaryRecall(threshold=t)
                #     accuracy =BinaryAccuracy(threshold=t)
                #     stats=  BinaryStatScores(threshold=t)
                #     TP, FP, TN, FN, _ = stats(vdata_arr, vlabels_arr)
                #     fpr_val_dict[t] = FP / (FP + TN)
                #     p_val_dict[t]  = percision(vdata_arr, vlabels_arr)
                #     r_val_dict[t]  = recall(vdata_arr, vlabels_arr)
                #     a_val_dict[t]  = accuracy(vdata_arr, vlabels_arr)

                # PlotAccuracyThreshold(mission, a_val_dict, epoch+1, optimizer.param_groups[0]['lr'], batch_size)
                # PlotFprRecal(mission, fpr_val_dict, r_val_dict,epoch+1,optimizer.param_groups[0]['lr'],batch_size)
                # PlotPercisionRecal(mission,p_val_dict,r_val_dict,epoch+1,optimizer.param_groups[0]['lr'],batch_size)
                # SaveStats(mission, a_val_dict, p_val_dict, r_val_dict, fpr_val_dict,epoch+1,optimizer.param_groups[0]['lr'],batch_size)
                # LossCurve(mission, TrainingLoss, ValidationLoss, epoch+1, optimizer.param_groups[0]['lr'], batch_size)

            # Track best performance, and save the model's state
            if validation_loss < best_vloss:
                best_vloss = validation_loss
                model_path = 'ModelCheckpoints/'+mission+'/_bf_model_{}_batch_size_{}_lr_{}'.format(epoch_number,batch_size,optimizer.param_groups[0]['lr'])
                torch.save(model.state_dict(), model_path)
                plot_data_set = validation_loader.dataset
                Plot_loader = DataLoader(plot_data_set, batch_size=1)
                GenPercisionPlots(model, Plot_loader, mission, optimizer, epoch_number + 1, fp='Predictions/')
                LossCurve(mission, TrainingLoss, ValidationLoss, epoch_number + 1, optimizer.param_groups[0]['lr'], batch_size)
            epoch_number += 1
    LossCurve(mission , TrainingLoss, ValidationLoss,NumEpochs,optimizer.param_groups[0]['lr'],batch_size)
    writer.flush()

    return model

def PlotPercisionRecal(mission,Percision,Recall,EpochNum,lr,batch_size,fp = 'Figures/' ):
    p_temp = []
    r_temp = []
    for ci in Percision.keys():
        p_temp.append(Percision[ci].item())
        r_temp.append(Recall[ci].item())
    plt.plot(r_temp,p_temp)
    v = Recall[.5]
    v2 = Percision[.5]
    plt.scatter(Recall[.5],Percision[.5],label=f'R(t=.5)={v:.2f},P(t=.5)={v2:.2f}',c='r')
    plt.xlabel('Recall')
    plt.ylabel('Percision')
    plt.title('Precision vs Recall Plot')
    plt.legend()
    plt.savefig(fp +mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_RecallVsPercision.png')
    plt.close()
    return None

def PlotFprRecal(mission,Fpr,Recall,EpochNum,lr,batch_size,fp = 'Figures/'):
    fpr_temp = []
    r_temp = []
    for ci in Fpr.keys():
        fpr_temp.append(Fpr[ci].item() )
        r_temp.append(Recall[ci].item())
    plt.plot(fpr_temp,r_temp,label='Recall')

    x = [0,1]
    y= [0,1]
    plt.plot(x,y, c='g', ls='--', label='No Skill')
    v = Fpr[.5]
    v2 = Recall[.5]
    plt.scatter( Fpr[.5],Recall[.5], label=f'FPR(t=.5)={v:.2f},R(t=.5)={v2:.2f}', c='r')
    plt.ylabel('Recall')
    plt.xlabel('False Positive Rate')
    plt.legend()
    plt.savefig(fp +mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_RecallVsFPR.png')
    plt.close()
    return None

def PlotAccuracyThreshold(mission,Acc,EpochNum,lr,batch_size,fp = 'Figures/'):
    acc_temp = []
    for ci in Acc.keys():
        acc_temp.append(Acc[ci].item() )
    plt.plot(Acc.keys(),acc_temp,label = 'Accuracy')
    v = Acc[.5]
    plt.scatter(.5,Acc[.5],label=f't=.5, Accuracy={v:.2f}',c='r')
    plt.xlabel('Threshold')
    plt.ylabel('Accuracy')
    plt.title(mission + f' Accuracy vs Threshold lr={lr},batch_size={batch_size}')
    plt.legend()
    plt.savefig(fp +mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_AccVsThreshold.png')
    plt.close()
    return None

def PlotAccuracyEpoch(mission,Acc,Epochs,t,lr,batch_size,fp = 'Figures/'):
    plt.plot(Epochs,Acc,label='Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy at {}'.format(t))
    plt.legend()
    plt.savefig(fp +mission + f'/All_Epochs_Accuracy_Threshold_{t}_lr_{lr}_batchsize_{batch_size}.png')
    plt.close()
    return None

def LossCurve(mission,TrainingLoss,ValidationLoss,EpochNum,lr,batch_size,fp = 'Figures/'):
    plt.plot(TrainingLoss, label='Training Loss')
    plt.plot(ValidationLoss, label='Validation Loss')
    plt.legend()
    plt.savefig(fp +mission+ f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_ExoMinerTrainAndValidLossPlot.png')
    plt.close()
    return None

def SaveStats(mission ,Accuracy,Percision,Recall,FPR,F1,AUROC, EpochNum,lr,batch_size,fp='Stats/'):
    file_str = fp + mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_ExoMinerResults.txt'
    File = open(file_str, 'w')
    for t in Accuracy.keys():
        ExoMinerdata = {'Threshold': t,'Accuracy': Accuracy[t].item(), 'Percision': Percision[t].item(), 'Recall': Recall[t].item(),
                        'False Positive Rate':FPR[t].item() , 'F1 Score': F1[t].item()}

        File.write(str(ExoMinerdata))
        File.write('\n')
    File.write(f'AUROC Val:{AUROC}')
    File.close()
    return None

def SavePatK(mission,PatK ,EpochNum,lr,batch_size,fp='Stats/PatK/'):
    file_str = fp + mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_PatKResults.txt'
    File = open(file_str, 'w')
    for key in PatK .keys():
        PatKdata = {'k= '+str(key) :  'val='+str(PatK[key])}
        File.write(str(PatKdata))
        File.write('\n')
    File.close()
    return None

def GenPercisionPlots(model,Plot_loader,mission,optimizer, EpochNum ,fp='Predictions/'):
    batch_size = Plot_loader.batch_size
    outputs_arr = []
    targets = []
    mission_flag = 'Raw' in mission
    with torch.no_grad():
        for i, cdata in enumerate(Plot_loader):
            vinputs, clabels,ids = cdata
            if (mission_flag):
                # run our model

                coutputs = model(
                    flux=ReshapeEnsureNotNan(vinputs[0], shape=(batch_size, 1, 6000)),  # flux
                    centroid=ReshapeEnsureNotNan2D([vinputs[1], vinputs[2]], shape=(batch_size, 2, 6001)),
                    # centroid data
                    f2_2=ReshapeEnsureNotNan(vinputs[3], shape=(batch_size, 1)),  # transit depth
                    f4_2=ReshapeEnsureNotNan(vinputs[4], shape=(batch_size, 5)),  # centroid scalars
                    f6_2=ReshapeEnsureNotNan(vinputs[5], shape=(batch_size, 4)),  # secondary Scalars
                    f7=ReshapeEnsureNotNan(vinputs[6], shape=(batch_size, 6)),  # Stellar parameters
                    f8=ReshapeEnsureNotNan(vinputs[7], shape=(batch_size, 6))  # DV Diagnostics
                    # model Vector
                )
            else:
                # run exominer
                coutputs = model(f1=ReshapeEnsureNotNan(vinputs[0], shape=(batch_size, 1, 301)),
                                 f2=ReshapeEnsureNotNan(vinputs[1], shape=(batch_size, 1, 31)),
                                 f2_2=ReshapeEnsureNotNan(vinputs[2], shape=(batch_size, 1)),
                                 f3=ReshapeEnsureNotNan(vinputs[3], shape=(batch_size, 1, 301)),
                                 f4=ReshapeEnsureNotNan(vinputs[4], shape=(batch_size, 1, 31)),
                                 f4_2=ReshapeEnsureNotNan(vinputs[5], shape=(batch_size, 5)),
                                 f5_odd=ReshapeEnsureNotNan(vinputs[6], shape=(batch_size, 1, 31)),
                                 f5_even=ReshapeEnsureNotNan(vinputs[7], shape=(batch_size, 1, 31)),
                                 f6=ReshapeEnsureNotNan(vinputs[8], shape=(batch_size, 1, 31)),
                                 f6_2=ReshapeEnsureNotNan(vinputs[9], shape=(batch_size, 4)),
                                 f7=ReshapeEnsureNotNan(vinputs[10], shape=(batch_size, 6)),
                                 f8=ReshapeEnsureNotNan(vinputs[11], shape=(batch_size, 6)))
            out = coutputs.detach().numpy()
            target = clabels.detach().numpy()
            out = np.reshape(out, batch_size)
            target = np.reshape(target , batch_size)
            outputs_arr.append(out[0])
            targets.append(target[0])
    p_val_dict = {}
    a_val_dict = {}
    r_val_dict = {}
    fpr_val_dict = {}
    f1_score_dict ={}
    Patk = {50:0,100:0,200:0,1000:0,2200:0}

    for t in np.arange(0, 1, .01):
        percision = BinaryPrecision(threshold=t)
        recall = BinaryRecall(threshold=t)
        accuracy = BinaryAccuracy(threshold=t)
        stats = BinaryStatScores(threshold=t)
        f1 = BinaryF1Score(threshold=t)
        TP, FP, TN, FN, _ = stats(torch.tensor(outputs_arr), torch.tensor(targets))

        fpr_val_dict[t] = FP / (FP + TN)
        f1_score_dict[t] = f1(torch.tensor(outputs_arr), torch.tensor(targets))
        p_val_dict[t] = percision(torch.tensor(outputs_arr), torch.tensor(targets))
        r_val_dict[t] = recall(torch.tensor(outputs_arr), torch.tensor(targets))
        a_val_dict[t] = accuracy(torch.tensor(outputs_arr), torch.tensor(targets))

        if (t == .5):
            for k in Patk.keys():
                PatK = Precision(task ='binary' , threshold=.5, top_k=k)
                Patk[k] = PatK(torch.tensor(outputs_arr), torch.tensor(targets))
            SavePatK(mission, Patk, EpochNum, optimizer.param_groups[0]['lr'], batch_size)
    auroc = BinaryAUROC(thresholds=None)
    auroc_val = auroc(torch.tensor(outputs_arr), torch.tensor(targets))

    SaveStats(mission, a_val_dict, p_val_dict, r_val_dict, fpr_val_dict,f1_score_dict,auroc_val, EpochNum, optimizer.param_groups[0]['lr'], batch_size, fp='Stats/')
    PlotPrecisionThreshold(mission, p_val_dict, EpochNum , optimizer.param_groups[0]['lr'], batch_size)
    PlotF1Score(mission, f1_score_dict, EpochNum, optimizer.param_groups[0]['lr'], batch_size)
    PlotRecallThreshold(mission, r_val_dict, EpochNum, optimizer.param_groups[0]['lr'], batch_size)
    PlotAccuracyThreshold(mission, a_val_dict, EpochNum , optimizer.param_groups[0]['lr'], batch_size)
    PlotFprRecal(mission, fpr_val_dict, r_val_dict, EpochNum, optimizer.param_groups[0]['lr'], batch_size)
    PlotPercisionRecal(mission=mission, Percision=p_val_dict, Recall=r_val_dict, EpochNum=EpochNum, lr=optimizer.param_groups[0]['lr'], batch_size=batch_size)
    return None

def PlotF1Score(mission,f1_dict,EpochNum,lr,batch_size,fp = 'Figures/'):
    f1_temp = []
    for ci in f1_dict.keys():
        f1_temp.append(f1_dict[ci].item())
    plt.plot(f1_dict.keys(), f1_temp, label='F1 Score')
    v = f1_dict[.5]
    plt.scatter(.5,f1_dict[.5],label=f't=.5, F1={v:.2f}',c='r')
    plt.xlabel('Threshold')
    plt.ylabel('Precision')
    plt.title(mission + f' Precision vs Threshold lr={lr},batch_size={batch_size}')
    plt.legend()
    plt.savefig(fp + mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_F1ScoreVsThreshold.png')
    plt.close()

def PlotPrecisionThreshold(mission,Precision,EpochNum,lr,batch_size,fp = 'Figures/'):
    Precision_temp = []
    for ci in Precision.keys():
        Precision_temp.append(Precision[ci].item() )

    plt.plot(Precision.keys(),Precision_temp,label = 'Precision')
    v =Precision[.5]
    plt.scatter(.5,Precision[.5],label=f't=.5, Precision={v:.2f}',c='r')
    plt.xlabel('Threshold')
    plt.ylabel('Precision')
    plt.title(mission + f' Precision vs Threshold lr={lr},batch_size={batch_size}')
    plt.legend()
    plt.savefig(fp +mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_PrecisionVsThreshold.png')
    plt.close()
    return None

def PlotRecallThreshold(mission,Recall,EpochNum,lr,batch_size,fp = 'Figures/'):
    recall_temp = []
    for ci in Recall.keys():
        recall_temp.append(Recall[ci].item() )
    plt.plot(Recall.keys(),recall_temp,label = 'Recall')
    v = Recall[.5]
    plt.scatter(.5,Recall[.5],label=f't=.5, Recall={v:.2f}',c='r')
    plt.xlabel('Threshold')
    plt.ylabel('Recall')
    plt.title(mission + f' Recall vs Threshold lr={lr},batch_size={batch_size}')
    plt.legend()
    plt.savefig(fp +mission + f'/Epoch_{EpochNum}_lr_{lr}_batchsize_{batch_size}_RecallVsThreshold.png')
    plt.close()
    return None

def UnpackRawData(mission, fp='RawData/'):
    files = glob.glob('./' + fp + mission + r'/dataset_*.npy')
    idxs = ['load' not in file for file in files]
    f = []
    for i in range(0, len(idxs)):
        if (idxs[i]):
            f.append(files[i])
        else:
            pass
    files = f
    files.sort()
    data_return = {}
    for file in files:
        shutil.copyfile(file, file[:-4] + '_load.npy')
        shutil.copyfile('./RawData/'+mission+'/'+mission+'Scalars.npy', './RawData/'+mission+'/'+mission+'Scalars' + '_load.npy')
        data = np.load(file[:-4]+'_load.npy', allow_pickle=True).item()
        data2= np.load('./RawData/'+mission+'/'+mission+'Scalars' + '_load.npy',allow_pickle=True).item()
        for id in data.keys():
            data_return[id] = data[id]
            temp = data2[id]
            d = {}
            for key in list(temp.keys()):
                d[key] = temp[key]
            m = [1 if mission=='Kepler'  else 0] # mission feature to determine Kepler or TESS
            centroid_col = data_return[id]['Centroid Col']
            centroid_row = data_return[id]['Centroid Row']
            centroid_col = np.append(centroid_col,m)
            centroid_row = np.append(centroid_row,m)
            data_return[id]['Centroid Col'] =centroid_col
            data_return[id]['Centroid Row'] =centroid_row
            data_return[id].update(d)
    return data_return

def RunModelOnCandidates(mission,CandLoader,model,threshold =.5,fp='Predictions/'):
    batch_size = CandLoader.batch_size
    df = pd.DataFrame()
    mission_flag = 'Raw' in mission
    with torch.no_grad():
        for i, cdata in enumerate(CandLoader):
            vinputs, clabels,ids = cdata
            if (mission_flag):
                # run our model

                coutputs = model(
                    flux=ReshapeEnsureNotNan(vinputs[0], shape=(batch_size, 1, 6000)),  # flux
                    centroid=ReshapeEnsureNotNan2D([vinputs[1], vinputs[2]], shape=(batch_size, 2, 6001)),
                    # centroid data
                    f2_2=ReshapeEnsureNotNan(vinputs[3], shape=(batch_size, 1)),  # transit depth
                    f4_2=ReshapeEnsureNotNan(vinputs[4], shape=(batch_size, 5)),  # centroid scalars
                    f6_2=ReshapeEnsureNotNan(vinputs[5], shape=(batch_size, 4)),  # secondary Scalars
                    f7=ReshapeEnsureNotNan(vinputs[6], shape=(batch_size, 6)),  # Stellar parameters
                    f8=ReshapeEnsureNotNan(vinputs[7], shape=(batch_size, 6))  # DV Diagnostics
                    # model Vector
                )
            else:
                # run exominer
                coutputs = model(f1=ReshapeEnsureNotNan(vinputs[0], shape=(batch_size, 1, 301)),
                                 f2=ReshapeEnsureNotNan(vinputs[1], shape=(batch_size, 1, 31)),
                                 f2_2=ReshapeEnsureNotNan(vinputs[2], shape=(batch_size, 1)),
                                 f3=ReshapeEnsureNotNan(vinputs[3], shape=(batch_size, 1, 301)),
                                 f4=ReshapeEnsureNotNan(vinputs[4], shape=(batch_size, 1, 31)),
                                 f4_2=ReshapeEnsureNotNan(vinputs[5], shape=(batch_size, 5)),
                                 f5_odd=ReshapeEnsureNotNan(vinputs[6], shape=(batch_size, 1, 31)),
                                 f5_even=ReshapeEnsureNotNan(vinputs[7], shape=(batch_size, 1, 31)),
                                 f6=ReshapeEnsureNotNan(vinputs[8], shape=(batch_size, 1, 31)),
                                 f6_2=ReshapeEnsureNotNan(vinputs[9], shape=(batch_size, 4)),
                                 f7=ReshapeEnsureNotNan(vinputs[10], shape=(batch_size, 6)),
                                 f8=ReshapeEnsureNotNan(vinputs[11], shape=(batch_size, 6)))
            out = coutputs.detach().numpy()
            out = np.reshape(out, batch_size)
            ids = np.array(ids)
            dispo = ['Conf' if o>=threshold else 'unk' for o in out]
            mission_arr=[mission]*len(ids)
            df_temp = pd.DataFrame([mission_arr,out,dispo],index=['Mission','Score','Disposition'],columns=ids)
            df = pd.concat([df,df_temp.T])
    df.to_csv(fp+mission + '/'+'Predictions_threshold_{}_.csv'.format(threshold))
    return None

def LoadBestModel(batch_size,lr,mission,fp = 'ModelCheckpoints/'):
    files = glob.glob('./' + fp  + mission + r'/model_*_batch_size_{}_lr_{}*'.format(batch_size,lr))
    files.sort()
    checkpoint = torch.load(files[-1])
    return checkpoint

def LoadBestFitExoMiner(mission='Combo',fp = 'ModelCheckpoints/'):
    files = glob.glob('./' + fp  + mission + r'/*bf*')
    files.sort()
    checkpoint = torch.load(files[-1])
    return checkpoint

def UseExoMinerWeights(ExoMinerRawModel,ExoMiner):
    ExoMinerRawModel.f1_conv1.weight.data = ExoMiner['f1_conv1.weight']
    ExoMinerRawModel.f1_conv1.weight.requires_grad_(True)
    ExoMinerRawModel.f1_conv1.bias.data =ExoMiner['f1_conv1.bias']

    ###########
    ExoMinerRawModel.f1_conv2.weight.data =ExoMiner['f1_conv2.weight']
    ExoMinerRawModel.f1_conv2.weight.requires_grad_(True)
    ExoMinerRawModel.f1_conv2.bias.data =ExoMiner['f1_conv2.bias']

    ###########
    ExoMinerRawModel.f1_conv3.weight.data =ExoMiner['f1_conv3.weight']
    ExoMinerRawModel.f1_conv3.weight.requires_grad_(True)
    ExoMinerRawModel.f1_conv3.bias.data =ExoMiner['f1_conv3.bias']

    ###########
    ExoMinerRawModel.f1_lin1.weight.data =ExoMiner['f1_lin1.weight']
    ExoMinerRawModel.f1_lin1.weight.requires_grad_(True)
    ExoMinerRawModel.f1_lin1.bias.data =ExoMiner['f1_lin1.bias']

    #####
    ExoMinerRawModel.f2_conv1.weight.data = ExoMiner['f2_conv1.weight']
    ExoMinerRawModel.f2_conv1.weight.requires_grad_(True)
    ExoMinerRawModel.f2_conv1.bias.data =ExoMiner['f2_conv1.bias']

    ###########
    ExoMinerRawModel.f2_conv2.weight.data =ExoMiner['f2_conv2.weight']
    ExoMinerRawModel.f2_conv2.weight.requires_grad_(True)
    ExoMinerRawModel.f2_conv2.bias.data =ExoMiner['f2_conv2.bias']

    ###########

    ###########
    ExoMinerRawModel.f2_lin1.weight.data =ExoMiner['f2_lin1.weight']
    ExoMinerRawModel.f2_lin1.weight.requires_grad_(True)
    ExoMinerRawModel.f2_lin1.bias.data =ExoMiner['f2_lin1.bias']

    #####
    ExoMinerRawModel.f3_conv1.weight.data = ExoMiner['f3_conv1.weight']
    ExoMinerRawModel.f3_conv1.weight.requires_grad_(True)
    ExoMinerRawModel.f3_conv1.bias.data = ExoMiner['f3_conv1.bias']

    ###########
    ExoMinerRawModel.f3_conv2.weight.data = ExoMiner['f3_conv2.weight']
    ExoMinerRawModel.f3_conv2.weight.requires_grad_(True)
    ExoMinerRawModel.f3_conv2.bias.data = ExoMiner['f3_conv2.bias']

    ###########
    ExoMinerRawModel.f3_conv3.weight.data = ExoMiner['f3_conv3.weight']
    ExoMinerRawModel.f3_conv3.weight.requires_grad_(True)
    ExoMinerRawModel.f3_conv3.bias.data = ExoMiner['f3_conv3.bias']

    ###########
    ExoMinerRawModel.f3_lin1.weight.data = ExoMiner['f3_lin1.weight']
    ExoMinerRawModel.f3_lin1.weight.requires_grad_(True)
    ExoMinerRawModel.f3_lin1.bias.data = ExoMiner['f3_lin1.bias']

    #####
    ExoMinerRawModel.f4_conv1.weight.data = ExoMiner['f4_conv1.weight']
    ExoMinerRawModel.f4_conv1.weight.requires_grad_(True)
    ExoMinerRawModel.f4_conv1.bias.data = ExoMiner['f4_conv1.bias']

    ExoMinerRawModel.f4_conv2.weight.data = ExoMiner['f4_conv2.weight']
    ExoMinerRawModel.f4_conv2.weight.requires_grad_(True)
    ExoMinerRawModel.f4_conv2.bias.data = ExoMiner['f4_conv2.bias']

    ###########
    ExoMinerRawModel.f4_lin1.weight.data = ExoMiner['f4_lin1.weight']
    ExoMinerRawModel.f4_lin1.weight.requires_grad_(True)
    ExoMinerRawModel.f4_lin1.bias.data = ExoMiner['f4_lin1.bias']

    #####
    ExoMinerRawModel.f5_conv1.weight.data = ExoMiner['f5_conv1.weight']
    ExoMinerRawModel.f5_conv1.weight.requires_grad_(True)
    ExoMinerRawModel.f5_conv1.bias.data = ExoMiner['f5_conv1.bias']

    ###########
    ExoMinerRawModel.f5_conv2.weight.data = ExoMiner['f5_conv2.weight']
    ExoMinerRawModel.f5_conv2.weight.requires_grad_(True)
    ExoMinerRawModel.f5_conv2.bias.data = ExoMiner['f5_conv2.bias']

    ###########
    ExoMinerRawModel.f5_lin1.weight.data = ExoMiner['f5_lin1.weight']
    ExoMinerRawModel.f5_lin1.weight.requires_grad_(True)
    ExoMinerRawModel.f5_lin1.bias.data = ExoMiner['f5_lin1.bias']

    #####
    ExoMinerRawModel.f6_conv1.weight.data = ExoMiner['f6_conv1.weight']
    ExoMinerRawModel.f6_conv1.weight.requires_grad_(True)
    ExoMinerRawModel.f6_conv1.bias.data = ExoMiner['f6_conv1.bias']

    ###########
    ExoMinerRawModel.f6_conv2.weight.data = ExoMiner['f6_conv2.weight']
    ExoMinerRawModel.f6_conv2.weight.requires_grad_(True)
    ExoMinerRawModel.f6_conv2.bias.data = ExoMiner['f6_conv2.bias']

    ###########
    ExoMinerRawModel.f6_lin1.weight.data = ExoMiner['f6_lin1.weight']
    ExoMinerRawModel.f6_lin1.weight.requires_grad_(True)
    ExoMinerRawModel.f6_lin1.bias.data = ExoMiner['f6_lin1.bias']

    ####
    ExoMinerRawModel.FinalLin1.weight.data = ExoMiner['FinalLin1.weight']
    ExoMinerRawModel.FinalLin2.weight.data = ExoMiner['FinalLin2.weight']
    ExoMinerRawModel.FinalLin3.weight.data = ExoMiner['FinalLin3.weight']
    ExoMinerRawModel.FinalLin4.weight.data = ExoMiner['FinalLin4.weight']
    ExoMinerRawModel.FinalLin1.weight.requires_grad_(True)
    ExoMinerRawModel.FinalLin2.weight.requires_grad_(True)
    ExoMinerRawModel.FinalLin3.weight.requires_grad_(True)
    ExoMinerRawModel.FinalLin4.weight.requires_grad_(True)
    ExoMinerRawModel.FinalLin1.bias.data = ExoMiner['FinalLin1.bias']
    ExoMinerRawModel.FinalLin2.bias.data = ExoMiner['FinalLin2.bias']
    ExoMinerRawModel.FinalLin3.bias.data = ExoMiner['FinalLin3.bias']
    ExoMinerRawModel.FinalLin4.bias.data = ExoMiner['FinalLin4.bias']

    return ExoMinerRawModel

def RunExoMiner(mission,lr,batch_size,training_size,epochs,type_sample='Oversample',load=True):  # mission
    torch.manual_seed(1)
    np.random.seed(1)
    print('Running ' + mission)
    if(mission=='TESS' or mission=='Kepler'):
        DataObject = gd.GatherData(mission=mission)
        data = UnPackData(mission)
        DataObject.IdData = DataObject.IdData[DataObject.IdData.WrapperId.isin(data.keys())]
        TrainDataset, ValidDataset, CandDataset, weight = GrabDatasets(data, DataObject, training_size, batch_size,Combo = False,type_sample=type_sample)
        model = ExoMiner.ExoMiner()
    elif(mission=='Combo'):
        DataObjectTESS = gd.GatherData(mission='TESS')
        dataTESS = UnPackData('TESS')
        DataObjectKepler = gd.GatherData(mission='Kepler')
        dataKepler = UnPackData('Kepler')
        DataObjectTESS.IdData = DataObjectTESS.IdData[DataObjectTESS.IdData.WrapperId.isin(dataTESS.keys())]
        DataObjectKepler.IdData = DataObjectKepler.IdData[DataObjectKepler.IdData.WrapperId.isin(dataKepler.keys())]
        TrainDatasetTESS, ValidDatasetTESS, CandDatasetTESS, weight = GrabDatasets(dataTESS, DataObjectTESS, training_size, batch_size,type_sample=type_sample)
        TrainDatasetKepler, ValidDatasetKepler, CandDatasetKepler, weight = GrabDatasets(dataKepler, DataObjectKepler, training_size, batch_size,type_sample=type_sample)
        train_data =pd.concat([TrainDatasetTESS,TrainDatasetKepler])
        valid_data =pd.concat([ValidDatasetTESS,ValidDatasetKepler])
        unk_data  =pd.concat([CandDatasetTESS,CandDatasetKepler])
        TrainDataset = D.MyDataset(train_data)
        ValidDataset = D.MyDataset(valid_data)
        CandDataset = D.MyDataset(unk_data)
        model = ExoMiner.ExoMiner()
    elif(mission=='ComboRaw'):
        DataObjectTESS = gd.GatherData(mission='TESS')
        dataTESS = UnpackRawData('TESS')
        DataObjectKepler = gd.GatherData(mission='Kepler')
        dataKepler = UnpackRawData('Kepler')
        DataObjectTESS.IdData = DataObjectTESS.IdData[DataObjectTESS.IdData.WrapperId.isin(dataTESS.keys())]
        DataObjectKepler.IdData = DataObjectKepler.IdData[DataObjectKepler.IdData.WrapperId.isin(dataKepler.keys())]
        TraindfTESS, ValiddfTESS, CanddfTESS, weight = GrabDatasets(dataTESS, DataObjectTESS,
                                                                                training_size, batch_size,
                                                                                type_sample=type_sample,Raw =True)
        TraindfKepler, ValiddfKepler, CanddfKepler, weight = GrabDatasets(dataKepler, DataObjectKepler,
                                                                                training_size, batch_size,
                                                                                type_sample=type_sample,Raw =True)
        train_data = pd.concat([TraindfTESS, TraindfKepler])
        valid_data = pd.concat([ValiddfTESS, ValiddfKepler])
        unk_data = pd.concat([CanddfTESS, CanddfKepler])
        TrainDataset = R.RawDataset(train_data)
        ValidDataset = R.RawDataset(valid_data)
        CandDataset = R.RawDataset(unk_data)
        model = ExoMinerRaw.ExoMinerRaw()

    elif (mission == 'TESSRaw'):
        DataObject = gd.GatherData(mission='TESS')
        data = UnpackRawData('TESS')
        DataObject.IdData = DataObject.IdData[DataObject.IdData.WrapperId.isin(data.keys())]
        TrainDataset, ValidDataset, CandDataset, weight = GrabDatasets(data, DataObject, training_size, batch_size,
                                                                       Combo=False, type_sample=type_sample,Raw =True)
        model = ExoMinerRaw.ExoMinerRaw()
    elif (mission == 'KeplerRaw'):
        print('Running '+mission)
        DataObject = gd.GatherData(mission='Kepler')
        data = UnpackRawData('Kepler')
        DataObject.IdData = DataObject.IdData[DataObject.IdData.WrapperId.isin(data.keys())]
        TrainDataset, ValidDataset, CandDataset, weight = GrabDatasets(data, DataObject, training_size, batch_size,
                                                                       Combo=False, type_sample=type_sample,Raw =True)
        model = ExoMinerRaw.ExoMinerRaw()
    if('Raw' in mission and load==True):
        checkpoint =LoadBestFitExoMiner()
        model = UseExoMinerWeights(ExoMinerRawModel=model, ExoMiner=checkpoint)

    TrainLoader = DataLoader(TrainDataset,batch_size=batch_size,drop_last=True)
    ValidLoader = DataLoader(ValidDataset,batch_size=batch_size,drop_last=True)
    #CandLoader = DataLoader(CandDataset,batch_size=batch_size,drop_last=True)
    #PlotLoader = DataLoader(ValidDataset, batch_size=1)

    criterion = nn.BCELoss(weight = weight, size_average=False)
    optimizer = optim.Adam(model.parameters(), lr=lr)
    model = TrainAndValidate(NumEpochs=epochs,
                     training_loader=TrainLoader,
                     validation_loader=ValidLoader,
                     optimizer=optimizer,
                     model=model,
                     criterion=criterion,
                     mission=mission)
    return None

def ModelChoice(model,mission,lr,training_size,batch_size,epochs,type_sample):
    if (model == "ExoMiner"):

        RunExoMiner(mission=mission,
                    lr=lr,
                    training_size=training_size,
                    batch_size=batch_size,
                    epochs=epochs,
                    type_sample=type_sample)

    elif(model == 'ExoMinerRaw'):
        RunExoMiner(mission=mission,
                    lr=lr,
                    training_size=training_size,
                    batch_size=batch_size,
                    epochs=epochs,
                    type_sample=type_sample)
    else:
        print('Choose either "ExoMiner" or "ExoMinerRaw" for model argument')
    return None

if __name__=='__main__':
    ModelChoice(model='ExoMinerRaw',
                  mission='ComboRaw',
                  lr=6.37e-5,
                  training_size=.8,
                  batch_size=10,
                  epochs=500,
                  type_sample='Oversample')


# first test
lbs = int(input("Enter the weight in lbs: "))
kg = lbs * 0.45
print("The weight in KG: ", kg)

# second test
price = 1000000
credit = "good"
if credit == "good":
    downpayment = price*10/100
else:
    downpayment = price*20/100
print(f"Down payment: ${downpayment}")

# third exercise
wg = int(input("Weight: "))
unit = input("l (lbs) Or k (kg)")
if unit == 'k':
    wg = abs(wg/0.45)
    print(f"You are {wg} pounds")
elif unit == 'l':
    wg = wg*0.45
    print(f"You are: {wg} Kilo Grams")

# Guess number
guessno = 9
guess_count = 0
guess_limit = 3
while guess_count < guess_limit:
    no = int(input("Enter the guess number:"))
    if no == guessno:
        print('You win')
        break
    guess_count += 1
else:
    print("you lose")


from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from access.managers import ReadAccessControlManager, WriteAccessControlManager

from events.models import Event

class AccessControl(models.Model):
    event = models.ForeignKey(
        Event, related_name="access", verbose_name=_("event"), on_delete=models.CASCADE
    )

    name = models.CharField(_("name"), max_length=200)
    slug = models.SlugField(unique=True)

    location = models.CharField(_("location"), max_length=500, blank=True, null=True)
    mark_used = models.BooleanField(_("mark used"), default=True)

    read_objects = ReadAccessControlManager()
    objects = WriteAccessControlManager()

    class Meta:
        verbose_name = _("access control point")
        verbose_name_plural = _("access control points")

    def __str__(self):
        return self.name

    def checked_today(self, date, status=None):
        if not date:
            date = timezone.now()
        today = self.log_access.filter(
            date__year=date.year, date__month=date.month, date__day=date.day
        )
        if status:
            today = today.filter(status=status)
        return today.count()

AC_TYPES = (
    ("ok", _("ok")),
    ("right", _("ok")),
    ("wrong", _("wrong")),
    ("incorrect", _("incorrect")),
    ("used", _("used")),
    ("maybe", _("maybe")),
)

class LogAccessControl(models.Model):
    access_control = models.ForeignKey(
        AccessControl,
        related_name="log_access",
        verbose_name=_("access control"),
        on_delete=models.CASCADE,
    )
    date = models.DateTimeField(auto_now_add=True)
    status = models.CharField(
        _("status"), max_length=10, choices=AC_TYPES, default="right"
    )

    class Meta:
        verbose_name = _("access control point log")
        verbose_name_plural = _("access control point logs")


class Individual:
    def __init__(self, id, gender, age, intro, habits, coord):
        self.id = id
        self.gender = gender
        self.age = age
        self.intro = intro
        self.habits = habits
        self.coord = coord

    def get_habits_set(self):
        return set(self.habits.split(', '))


"""
Program:  modelview.py
Author:  Chad Lister
Date:  01/31/2021

This program defines the view for the PFEvaluatorModel class.

DS Chapter # 7 Project # 4:

    add the ^ operator to the expressions processed by the expression
    evaluator of the case study.

"""

from scanner import Scanner
from model import *

def main():
    """ The main function. """

    # Main display loop.
    while True:

        # String variable.
        st = ""

        sourceStr = input("\nEnter an expression: ")
        if sourceStr == "": break
        scanner = Scanner(sourceStr)
        while scanner.hasNext():
            st += str(scanner.next())

        print("\nPrefix expression is", st)

        # String variables.
        temp2 = ""
        temp = ""
        temp3 = ""

        # Divide string into digits and operators.
        for i in range(len(st)):
            c = st[i]
            b = c.isdigit()

            # Add to digit string.
            if b == True:
                temp += c

            # Add to operator string and pad digit string.
            else:
                temp2 = " " + c + " " + temp2
                temp += " "

        # Add digit and operator string.
        temp3 = temp + temp2

        print("\nPostfix expression is", temp3)

        # Make PFEvaluatorModel and pass string to evaluate.
        m = PFEvaluatorModel()
        r = m.evaluate(temp3)
        print("\nEvaluates to:  ", r)

main()

DS Chapter # 7 Projects # 1, 2, 4/DS Chapter # 7 Project # 4/modelview.py


## 作業目標

# * 請利用 API: https://www.dcard.tw/_api/forums/pet/posts?popular=true 回答下列問題：

# 1. 這個 API 一次會回傳幾筆資料？每一筆資料包含哪些欄位？

import requests
import json
r = requests.get('https://www.dcard.tw/_api/forums/pet/posts?popular=true')
response = json.loads(r.text)
len(response)
print(len(response))
# 30個

# 2. 取出每一筆資料的「標題」、「貼文時間」、「留言人數」、「按讚人數」
count_num = 0
likeCount_num = 0
for response_content in response:
    title = response_content['title']
    time = response_content['createdAt']
    count = response_content['commentCount']
    count_num+=count
    likeCount = response_content['likeCount']
    likeCount_num+=likeCount

# 3. 計算熱門/非熱門文章的「平均留言人數」與「平均按讚人數」
notPopular = requests.get('https://www.dcard.tw/_api/forums/pet/posts?popular=false')
notPopular_response = json.loads(notPopular.text)
notPopular_count_num = 0
notPopular_likeCount_num = 0
for notPopular_content in notPopular_response:
    count = notPopular_content['commentCount']
    notPopular_count_num += count
    likeCount = notPopular_content['likeCount']
    notPopular_likeCount_num += likeCount

not_popular_count_average = notPopular_count_num/len(notPopular_response)
print(not_popular_count_average)
not_popular_likeCount_average = notPopular_likeCount_num/len(notPopular_response)
print(not_popular_likeCount_average)
popular_count_average = count_num/len(response)
print(popular_count_average)
popular_likeCount_average = likeCount_num/len(response)
print(popular_likeCount_average)


n, s, l = map(int, input().split())
f = []
for _ in range(n):
    f.append(int(input()))
f.sort()

d = []
if n == 1:
    print(l)
    exit(0)

for i in range(n - 1):
    d.append(f[i + 1] - f[i])

if s > min(d):
    print(-1)
    exit(0)

boards = [s] * n
for i in range(n):
    boards[i] = [f[i] - s/2, f[i] + s/2]

zeroethBoardL = min(l/2, boards[1][0] - f[0])
boards[0] = [f[0] - zeroethBoardL, f[0] + zeroethBoardL]

for i in range(1, n - 1):
    maxL = min(l/2, f[i] - boards[i - 1][1], boards[i + 1][0] - f[i])
    boards[i] = [f[i] - maxL, f[i] + maxL]

lastBoardL = min(l/2, f[-1] - boards[-2][1])
boards[-1] = [f[-1] - lastBoardL, f[-1] + lastBoardL]
print(int(sum([boards[i][1] - boards[i][0] for i in range(n)])))
# print(boards)


import cx_Oracle

conn = cx_Oracle.connect('scott','tiger','192.168.1.11:1521/XE')
cursor = conn.cursor()
# sql = "insert into dept values(50,'databse','seoul')"
# cursor.execute(sql)
sql = "insert into dept values(:1,:2,:3)"
code = input('부서코드를 입력하세요(2자리 숫자) >>> ')
dname = input('부서명을 입력하세요  >>>')
city = input('위치를 입력하세요 >>> ')
data = (int(code),dname,city)
cursor.execute(sql,data)
cursor.close()
conn.commit()
conn.close()


from opentrons import labware
from opentrons import instruments
# from opentrons.data_storage import database

tiprack = labware.load('opentrons-tiprack-300ul', '1')

plate = labware.load('96-flat', '2')
#create custom plate if it does not currently exist in robot software
plate_agar='agar_plate_9_9_9'
if plate_agar not in labware.list():
	#print('hi')
	custom_plate = labware.create(
	        plate_agar,                    # name of you labware
	        grid=(12, 8),                    # specify amount of (columns, rows)
	        spacing=(9, 9),               # distances (mm) between each (column, row)
	        diameter=9,                     # diameter (mm) of each well on the plate
	        depth=5,                       # depth (mm) of each well on the plate
	        volume=200)

plate2 = labware.load(plate_agar, '5')

pipette = instruments.P300_Single(
	mount = 'right',
	tip_racks = [tiprack])

# Testing single drop
# pipette.pick_up_tip(tiprack.wells('A1'))
# pipette.aspirate(20, plate.wells('A1'))
# pipette.dispense(20, plate2.wells('A1'))

#Testing drops along single column
# pipette.transfer(7,
# 				plate.wells('A1', 'B1'),
# 				plate2.wells('A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1'))

# Testing drops along single row
# pipette.transfer(20,
# 				plate.wells('A1', 'B1', 'C1'),
# 				plate2.wells('A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12'))

# Testing drops along first row & column
pipette.transfer(14,
				plate.wells('A1'),
				plate2.wells('B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1'))
pipette.transfer(14,
				plate.wells('B1', 'C1'),
				plate2.wells('A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12'))


from urllib.request import urlopen
from bs4 import BeautifulSoup
import pyexcel
# 1. Tạo một kết nôi Create conection
url = "https://dantri.com.vn"
conn = urlopen(url)

# 2. Download page
raw_data = conn.read()

page_content  = raw_data.decode("utf8")

# print(page_content)
# 3. Find ro Region of interest
soup = BeautifulSoup(page_content, "html.parser")
ul = soup.find("ul", "ul1 ulnew")

# 4. Extra ROI
li_list = ul.find_all("li")

# print(li_list)
news_list = []
for li in li_list:
    h4 = li.h4
    a = h4.a
    link = url + a["href"]
    title = a.string
    news = {
        "link": link,
        "title": title
    }
    news_list.append(news)
    print(link)

    print(title)

# print(li)
# print(ul.prettify())
# print(soup.prettify())
# 5. Save
pyexcel.save_as(records=news_list, dest_file_name="your_file.xlsx")


from django.contrib import messages
from django.contrib.auth import login, logout
from django.contrib.auth.forms import AuthenticationForm
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.detail import DetailView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import FormView
from django.urls import reverse, reverse_lazy
from django.http import HttpResponseRedirect
from . import forms
from projects.forms import UserCompletedProjectFormset
from skills.forms import SkillFormset
from .models import User
from skills.models import Skill
from applications.models import Application
from projects.models import UserCompletedProject

class ProfileView(LoginRequiredMixin, UpdateView):
    model = User
    success_url = reverse_lazy('accounts:profile',
                               kwargs={'option': None}
                               )
    form_class = forms.ProfileCreateForm
    context_object = 'user'

    def get_object(self):
        return User.objects.get(pk=self.request.user.id)

    def get_template_names(self):
        """this method checks the option,
        if its not view it shows the edit template"""
        option = self.kwargs.get('option')
        if option is not None:
            return 'accounts/profile.html'
        else:
            return 'accounts/edit_profile.html'

    def get_context_data(self, *args, **kwargs):
        data = super().get_context_data(**kwargs)
        if self.request.POST:
            data['CompletedProjectsFormset'] = UserCompletedProjectFormset(
                                            self.request.POST,
                                            prefix='completed_projects')
            data['SkillsFormset'] = SkillFormset(
                                            self.request.POST,
                                            prefix='skills')
        else:
            data['accepted_applications'] = Application.objects.filter(
                                        employee=self.get_object(),
                                        accepted=True
                                    )
            data['CompletedProjectsFormset'] = UserCompletedProjectFormset(
                    prefix='completed_projects',
                    queryset=UserCompletedProject.objects.filter(
                                             user=self.get_object())
                                            )
            data['SkillsFormset'] = SkillFormset(
                    prefix='skills',
                    queryset=Skill.objects.filter(users=self.get_object())
                )
            # check if there are any completed projects for the user
            projects = UserCompletedProject.objects.filter(
                                             user=self.get_object()
                                            )
            # check if the user has added skills
            skills = Skill.objects.filter(users=self.get_object())
            if not projects:
                # if there are no projects send a extra form through
                data['CompletedProjectsFormset'].extra = 1
            if not skills:
                # if there are no skills send a extra form through
                data['SkillsFormset'].extra = 1
        return data

    def get_form(self, *args, **kwargs):
        form = super(ProfileView, self).get_form(*args, **kwargs)
        form.fields['full_name'].widget.attrs['class'] = 'circle--input--h1'
        form.fields['full_name'].widget.attrs['placeholder'] = 'Firstname'
        form.fields['full_name'].label = ''
        form.fields['bio'].widget.attrs['placeholder'] = (
                                            '''
                                              Tell us about yourself...
                                            '''
                                        )
        form.fields['bio'].label = ''
        return form

    def form_valid(self, form):
        data = self.get_context_data()
        instance = form.save(commit=False)
        completed_projects = data['CompletedProjectsFormset']
        skills_formset = data['SkillsFormset']
        if completed_projects.is_valid() and skills_formset.is_valid():
            projects = completed_projects.save(commit=False)
            skills = skills_formset.save(commit=False)
            for project in projects:
                project.user = instance
                project.save()
            for obj in skills_formset.deleted_objects:
                obj.users.remove(instance)
            for skill in skills:
                skill_instance, created = Skill.objects.get_or_create(
                                            name=skill.name
                                        )
                skill_instance.save()
                skill_instance.users.add(instance)
                skill_instance.save()
            instance.save()
            skills_formset.save_m2m()
            completed_projects.save()
        return super().form_valid(form)

class ApplicantView(LoginRequiredMixin, DetailView):
    model = User
    context_object = 'user'
    template_name = 'accounts/applicant.html'

class loginView(FormView):
    template_name = 'accounts/login.html'
    form_class = AuthenticationForm
    success_url = reverse_lazy('accounts:profile',
                               kwargs={'option': None})

    def form_valid(self, form):
        login(self.request, form.get_user())
        return super().form_valid(form)

class SignupView(CreateView):
    template_name = 'accounts/signup.html'
    success_url = reverse_lazy('accounts:login')
    form_class = forms.UserCreateForm

    def form_valid(self, form):
        user = form.save(commit=False)
        user.set_password(user.password)
        user.save()
        return super().form_valid(form)

def sign_out(request):
    logout(request)
    messages.success(request, "You've been signed out. Come back soon!")
    return HttpResponseRedirect(reverse('home'))


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        prev, curr, post = None, head, head
        for i in range(n):
            post = post.next
        while post:
            prev, curr, post = curr, curr.next, post.next
        if prev:
            prev.next = curr.next
        else: # curr == head
            head = head.next

        return head


import unittest
import sys
from datetime import datetime
from indicators.queries import IPTTIndicator
from indicators.models import Indicator
from factories.indicators_models import (
    IndicatorFactory,
    LevelFactory,
    PeriodicTargetFactory,
    ResultFactory
    )
from factories.workflow_models import ProgramFactory
from django import test
from django.core.exceptions import ValidationError

from django.conf import settings
from django.db import connection

""" cases
    - cumulative number +
        - lop target: last target
        - lop result: sum of all data
        - period result: sum of data to date
    - cumulative number - (this one might change) (flip bad and good)
        - same as above
    - cumulative percent +
        - lop target: last target
        - lop result: last result
        - period result: last result
    - cumulative percent -
        - same as above (flip bad and good)
    - noncumulative number +
        - lop target: sum of targets
        - lop result: sum of all data
        - period result: sum of data in period
    - noncumulative number -
        - same as above (flip bad and good)
    - noncumulative percent + : no results
    - noncumulative precent - : no results
"""

dates = {
    'program_start': (1, 1),
    'program_end': (12, 31),
    'targets': [
        ((1, 1), (4, 30)),
        ((5, 1), (8, 31)),
        ((9, 1), (12, 31))
    ],
    'collects': [
        ((2, 1),),
        ((6, 1), (8, 10)),
        ((10, 20),)
    ]
}

def get_date(date_tuple):
    return datetime(2016, date_tuple[0], date_tuple[1])

scenarios = [
    {
        'desc': 'cumulative number positive',
        'cumulative': True,
        'number': True,
        'positive': True,
        'targets': [50, 100, 150],
        'data': [(40,), (30, 30), (45,)],
        'blank': False,
        'lop_target': 150,
        'lop_sum': 145,
        'lop_met': '97%',
        'results': [40, 100, 145],
        'mets': ['80%', '100%', '97%'],
        'over_under': [-1, 0, 0],
        'semi_annual_results': [70, 145],
    },
    {
        'desc': 'cumulative number negative',
        'cumulative': True,
        'number': True,
        'positive': False,
        'targets': [300, 200, 100],
        'data': [(100,), (50, 80), (40,)],
        'blank': False,
        'lop_target': 100,
        'lop_sum': 270,
        'lop_met': '270%',
        'results': [100, 230, 270],
        'mets': ['33%', '115%', '270%'],
        'over_under': [1, 0, -1],
        'semi_annual_results': [150, 270]
    },
    {
        'desc': 'cumulative percent positive',
        'cumulative': True,
        'number': False,
        'positive': True,
        'targets': [50, 60, 70],
        'data': [(40,), (50, 55,), (75,)],
        'blank': False,
        'lop_target': 70,
        'lop_sum': 75,
        'lop_met': '107%',
        'results': [40, 55, 75],
        'mets': ['80%', '92%', '107%'],
        'over_under': [-1, 0, 0],
        'semi_annual_results': [50, 75]
    },
    {
        'desc': 'cumulative percent negative',
        'cumulative': True,
        'number': False,
        'positive': False,
        'targets': [80, 40, 20],
        'data': [(75,), (35, 55), (25,)],
        'blank': False,
        'lop_target': 20,
        'lop_sum': 25,
        'lop_met': '125%',
        'results': [75, 55, 25],
        'mets': ['94%', '138%', '125%'],
        'over_under': [0, -1, -1],
        'semi_annual_results': [35, 25]
    },
    {
        'desc': 'noncumulative number positive',
        'cumulative': False,
        'number': True,
        'positive': True,
        'targets': [100, 100, 100],
        'data': [(80,), (40, 70), (90,)],
        'blank': False,
        'lop_target': 300,
        'lop_sum': 280,
        'lop_met': '93%',
        'results': [80, 110, 90],
        'mets': ['80%', '110%', '90%'],
        'over_under': [-1, 0, 0],
        'semi_annual_results': [120, 160]
    },
    {
        'desc': 'noncumulative number negative',
        'cumulative': False,
        'number': True,
        'positive': False,
        'targets': [500, 500, 500],
        'data': [(400,), (300, 200), (600,)],
        'blank': False,
        'lop_target': 1500,
        'lop_sum': 1500,
        'lop_met': '100%',
        'results': [400, 500, 600],
        'mets': ['80%', '100%', '120%'],
        'over_under': [1, 0, -1],
        'semi_annual_results': [700, 800]
    },
    {
        'desc': 'noncumulative percent positive',
        'cumulative': False,
        'number': False,
        'positive': True,
        'targets': [30, 30, 30],
        'data': [(30,), (20,), (50,)],
        'blank': True
    },
    {
        'desc': 'noncumulative percent negative',
        'cumulative': False,
        'number': False,
        'positive': False,
        'targets': [30, 30, 30],
        'data': [(30,), (20,), (50,)],
        'blank': True
    }
]

class TestIndicatorInstance(test.TestCase):

    def setUp(self):
        self.level = LevelFactory()
        self.indicator = IndicatorFactory(name="testname", level=self.level)

    def tearDown(self):
        self.indicator.delete()
        self.level.delete()

    def test_instances(self):
        indicators = IPTTIndicator.timeperiods.all()
        self.assertEqual(len(indicators), 1)
        self.assertEqual(indicators[0].name, self.indicator.name)

    def test_level(self):
        indicator = IPTTIndicator.timeperiods.first()
        self.assertEqual(indicator.levelname, self.level.name)

@unittest.skip('needs updating')
class TestIndicatorScenarios(test.TestCase):
    def setUp(self):
        self.program = ProgramFactory(reporting_period_start=get_date(dates['program_start']),
                                      reporting_period_end=get_date(dates['program_end']))
        self.indicator = None
        self.targets = []
        self.data = []

    def tearDown(self):
        for data in self.data:
            data.delete()
        for target in self.targets:
            target.delete()
        if self.indicator is not None:
            self.indicator.delete()
        self.program.delete()

    def indicator_for_scenario(self, scenario):
        indicator = IndicatorFactory(
            target_frequency=Indicator.TRI_ANNUAL,
            is_cumulative=scenario['cumulative'],
            direction_of_change=(
                Indicator.DIRECTION_OF_CHANGE_POSITIVE
                if scenario['positive'] else Indicator.DIRECTION_OF_CHANGE_NEGATIVE
            ),
            unit_of_measure_type=(
                Indicator.NUMBER if scenario['number'] else Indicator.PERCENTAGE
            ),
            program=self.program
        )
        for target_number, period in zip(scenario['targets'], dates['targets']):
            self.targets.append(PeriodicTargetFactory(
                indicator=indicator,
                target=target_number,
                start_date=get_date(period[0]),
                end_date=get_date(period[1])
            ))
        for c, data_set in enumerate(scenario['data']):
            date_set = dates['collects'][c]
            for data, date in zip(data_set, date_set):
                self.data.append(ResultFactory(
                    periodic_target=self.targets[c],
                    achieved=data,
                    indicator=indicator,
                    program=self.program,
                    date_collected=get_date(date)
                ))
        return indicator

    def get_scenarios(self):
        for scenario in scenarios:
            try:
                self.indicator = self.indicator_for_scenario(scenario)
            except ValidationError:
                exc_type, exc_value = sys.exc_info()[:2]
                self.fail("{0} in {1} scenario: {2}".format(exc_type.__name__, scenario['desc'], ",".join(exc_value)))
            self.assertEqual(self.indicator.program, self.program)
            yield scenario

    @unittest.skip('not implemented - IPTT Indicator not currently in use, fix tests when it is reintroduced')
    def test_scenario_totals_targetperiods(self):
        settings.DEBUG = True
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            iptt_indicator = IPTTIndicator.withtargets.get(pk=self.indicator.pk)
            expected_queries = 2
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} query to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            # make sure iptt indicator proxy loaded correctly:
            self.assertEqual(iptt_indicator.pk, self.indicator.pk)
            if scenario['blank']:
                # no tests on a blank (unsupported for annotations) indicator:
                return
            self.assertEqual(
                iptt_indicator.lop_target_calculated,
                scenario['lop_target'],
                "In scenario {desc}: calculated lop_target_sum should be {1}, got {0}".format(
                    iptt_indicator.lop_target_calculated, scenario['lop_target'],
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_actual,
                scenario['lop_sum'],
                "In scenario {desc}: calculated lop sum should be {0}, got {1}".format(
                    scenario['lop_sum'], iptt_indicator.lop_actual,
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_percent_met,
                scenario['lop_met'],
                "In scenarios {desc}: lop met_target should be {0}, got {1}".format(
                    scenario['lop_met'], iptt_indicator.lop_percent_met,
                    desc=scenario['desc']))
        settings.DEBUG = False

    @unittest.skip('not implemented - IPTT Indicator not currently in use, fix tests when it is reintroduced')
    def test_scenario_totals_timeperiods(self):
        settings.DEBUG = True
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            iptt_indicator = IPTTIndicator.notargets.get(pk=self.indicator.pk)
            expected_queries = 1
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} query to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            # make sure iptt indicator proxy loaded correctly:
            self.assertEqual(iptt_indicator.pk, self.indicator.pk)
            if scenario['blank']:
                # no tests on a blank (unsupported for annotations) indicator:
                return
            self.assertEqual(
                iptt_indicator.lop_target_sum,
                scenario['lop_target'],
                "In scenario {desc}: calculated lop_target_sum should be {1}, got {0}".format(
                    iptt_indicator.lop_target_sum, scenario['lop_target'],
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_actual_sum,
                scenario['lop_sum'],
                "In scenario {desc}: calculated lop sum should be {0}, got {1}".format(
                    scenario['lop_sum'], iptt_indicator.lop_actual_sum,
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_met_target,
                scenario['lop_met'],
                "In scenarios {desc}: lop met_target should be {0}, got {1}".format(
                    scenario['lop_met'], iptt_indicator.lop_met_target,
                    desc=scenario['desc']))
        settings.DEBUG = False

    def test_periodic_target_scenarios(self):
        settings.DEBUG = True
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            indicator = IPTTIndicator.tva.get(pk=self.indicator.pk)
            expected_queries = 2 #one for each target
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} queries to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            if scenario['blank']:
                return
            self.assertEqual(
                len(indicator.data_targets),
                len(scenario['targets']),
                "In {desc}: expecting {0} indicator targets, got {1}".format(
                    len(scenario['targets']), len(indicator.data_targets),
                    desc=scenario['desc']))
            for c, target_actual in enumerate(indicator.data_targets):
                self.assertEqual(
                    target_actual.target,
                    scenario['targets'][c],
                    "In {desc}: expected {0} for target {1}, but got {2}".format(
                        scenario['targets'][c], c, target_actual.target,
                        desc=scenario['desc']))
                self.assertEqual(
                    target_actual.data_sum,
                    scenario['results'][c],
                    "In {desc}: expected a sum of {0} for target {1}, but got {2}".format(
                        scenario['results'][c], c, target_actual.data_sum,
                        desc=scenario['desc']))
                self.assertEqual(
                    target_actual.met,
                    scenario['mets'][c],
                    "In {desc}: expected a met of {0} for target {1}, but got {2}".format(
                        scenario['mets'][c], c, target_actual.met,
                        desc=scenario['desc']))
                self.assertEqual(
                    target_actual.within_target_range,
                    scenario['over_under'][c],
                    "In {desc}: expected {0} within_target_range to be {1}, got {2}".format(
                        c, scenario['over_under'][c], target_actual.within_target_range,
                        desc=scenario['desc']))
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "(after all tests): expecting {0} queries to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            settings.DEBUG = False

    def test_timeperiod_scenarios(self):
        settings.DEBUG = True
        periods = [
            {
                'start_date': datetime(2016, 1, 1),
                'end_date': datetime(2016, 6, 30),
            },
            {
                'start_date': datetime(2016, 7, 1),
                'end_date': datetime(2016, 12, 31)
            }]
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            indicator = IPTTIndicator.timeperiods.with_frequency_annotations(Indicator.SEMI_ANNUAL,
                                                                             self.indicator.program.reporting_period_start,
                                                                             self.indicator.program.reporting_period_end).get(pk=self.indicator.id)
            expected_queries = 1
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} queries to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            expected_results = len(scenario['semi_annual_results']) if not scenario['blank'] else 0
            self.assertEqual(
                len([p for p in indicator.timeperiods]),
                expected_results,
                "In {desc}, expected {0} timeperiods, got {1}".format(
                    expected_results, len([p for p in indicator.timeperiods]),
                    desc=scenario['desc']))
            for c, period in enumerate(periods):
                if scenario['blank'] != True:
                    period_sum = getattr(indicator, "{0}-{1}".format(
                        period['start_date'].strftime('%Y-%m-%d'),
                        period['end_date'].strftime('%Y-%m-%d')))
                    self.assertEqual(
                        period_sum, scenario['semi_annual_results'][c],
                        "{desc} expected {0} for sum of semi annual period {1}, got {2}".format(
                            scenario['semi_annual_results'][c], c, period_sum,
                            desc=scenario['desc']))
        settings.DEBUG = False


#1번째
def reverser(words, start=0, end=None):
    if len(words) < 2 :
        return words
    # end = end or len(words)-1
    if end is None:
        end = len(words)-1

    #스위칭
    while start < end:
        words[start], words[end] = words[end], words[start]
        start += 1
        end -= 1

def reversing_word_sentence_logic(words):
    #전체회전
    reverser(words)

    p = 0
    start = 0
    while p < len(words):
        if words[p] == u"\u0020":
            #띄어쓰기 찾기
            reverser(words, start, p-1)
            start = p+1
        p += 1

    #마지막 단어 reverse
    reverser(words, start, p-1)
    return "".join(words)

str1 = "파이썬 알고리즘 정말 재미있다"
str2 = reversing_word_sentence_logic(list(str1))
print(str2)

#반복문 하나로 처리
def reverse_words_brute(words):
    word, sentence = [], []
    for character in words:
        if character != " ":
            word.append(character)
        else:
            if word:
                sentence.append("".join(word))
            word = []

    if word != "":
        sentence.append("".join(word))
    sentence.reverse()
    return " ".join(sentence)

str1 = "파이썬 알고리즘 정말 재미있다"
str2 = reverse_words_brute(str1)
print(str2)

#list, slice 사용
def reversing_word_slice(sentence):
    new_word = []
    words = sentence.split(" ")

    for word in words[::-1]:
        new_word.append(word)
    return " ".join(new_word)

str1 = "파이썬 알고리즘 정말 재미있다"
str2 = reversing_word_slice(str1)
print(str2)

#내장 시퀀스 사용
def reversing_words(sentence):
    words = sentence.split(" ")
    rev_set = " ".join(reversed(words))
    return rev_set

str1 = "파이썬 알고리즘 정말 재미있다"
str2 = reversing_words(str1)
print(str2)


__author__ = 'seamus tuohy'
__date__ = '2020-12-05'
__version__ = '0.00.1'

import logging

FORMAT = "%(levelname)s [%(filename)s:%(lineno)s - %(funcName)s() ] %(message)s"
formatter = logging.Formatter(FORMAT)
default_handler = logging.StreamHandler()
default_handler.setFormatter(formatter)

logger = logging.getLogger(__name__)
logger.addHandler(default_handler)
logger.setLevel(logging.WARNING)

from RTFDE.deencapsulate import DeEncapsulator


# the below imports are not required by the executor.py but is required by dank.serverbuilder.py

import re
import random
import zipfile
import concurrent.futures

# required imports for executor.py

import os
import sys
import time
import requests
from packaging import version
from colorama import init, Fore, Style

os.system("title dank.serverbuilder [ initializing ]")

# colors

init(autoreset=True)
red = Fore.RED + Style.BRIGHT
magenta = Fore.MAGENTA + Style.BRIGHT
white = Fore.WHITE + Style.BRIGHT

if __name__ == "__main__":

    project = "dank.serverbuilder"
    current_version = 2.1
    print(f"\n  {white}> {magenta}Version{white}: {current_version}")
    os.chdir(os.path.dirname(sys.argv[0]))

    # remove old build if exists

    try:os.remove(f"{project}.exe")
    except:pass

    # get latest version

    while True:
        try:
            latest_version = requests.get(f"https://raw.githubusercontent.com/SirDankenstien/{project}/main/executor_version.txt").content.decode()
            if "Not Found" in str(latest_version):latest_version = 0
            else:latest_version = float(latest_version)
            break
        except:
            wait = input(f"\n  {white}> {red}Failed to check for an update! Make sure you are connected to the Internet! Press {white}Enter {red}to try again.")

    # version checker / updater

    def download_latest():
        print(f"\n  {white}> {magenta}Downloading {white}{project}-latest.exe{magenta}...")
        data = requests.get(f"https://github.com/SirDankenstien/{project}/blob/main/{project}.exe?raw=true", allow_redirects=True).content
        open(f"{project}-latest.exe","wb").write(data)
        data = None
        print(f"\n  {white}> {magenta}Downloaded!\n\n  {white}> {magenta}Starting in 5s...")
        time.sleep(5)
        os.system(f"start {project}-latest.exe")
        sys.exit()

    if version.parse(str(latest_version)) > version.parse(str(current_version)):
        choice = str(input(f"\n  {white}> {magenta}Update Found{white}: {latest_version}\n\n  {white}> {magenta}Download latest version? {white}[ {magenta}y {white}/ {magenta}n {white}]: {magenta}")).lower()
        if choice == "y":download_latest()

    elif latest_version == current_version:
        print(f"\n  {white}> {magenta}Latest Version!")
    else:
        print(f"\n  {white}> {magenta}Development Version!")
    time.sleep(3)

    # get src from github

    while True:
        try:
            code = str(requests.get(f"https://raw.githubusercontent.com/SirDankenstien/{project}/main/{project}.py").content.decode())
            break
        except:
            wait = input(f"\n  {white}> {red}Failed to get src! Make sure you are connected to the Internet! Press {white}Enter {red}to try again")

    # execute, catch errors if any

    os.system("title dank.serverbuilder")

    try:exec(code)
    except Exception as exp:

        exc_type, exc_obj, exc_tb = sys.exc_info()
        os.system('cls')
        print(f"\n  {white}> {red}Error: {str(exp)} {white}| {red}{exc_type} {white}| {red}Line: {exc_tb.tb_lineno}")
        print(f"\n  {white}> {magenta}Please take a screenshot of this and post it on {white}https://github.com/SirDankenstien/dank.serverbuilder/issues/new")

        if not latest_version == current_version:
            choice = input(f"\n{white}> {magenta}Would you like to download the latest executor [ {white}y {magenta}/ {white}n {magenta}]: {white}").lower()
            if choice == "y":download_latest()

        wait = print(f"\n  {white}> {magenta}Press {white}ENTER {magenta}to continue: ")


# NIM / Nama : 16521040 / Kenneth Ezekiel Suprantoni
# Tanggal : 03/11/2021
# Deskripsi : Program lampu tuan Kil

# Kamus
# n_lampu, n_tekan, tombol : int
# string : str
# array_lampu : arr

# Algoritma
n_lampu = int(input("Masukkan banyak lampu: "))
n_tekan = int(input("Masukkan berapa kali Tuan Kil menekan tombol: "))
string= ""
array_lampu = [0 for i in range(n_lampu)]
# print(array_lampu)

# indeks 0 == lampu 1
for i in range(n_tekan):
  tombol = int(input(f"Tombol yang ditekan ke {i+1}: "))
  for j in range(tombol-2, tombol+1, 1):
    if 0 <= j < n_lampu:          # Cek apakah lampu yang dinyala/matikan didalam indeks atau tidak
      if array_lampu[j] == 0:
        array_lampu[j] = 1
      elif array_lampu[j] == 1:
        array_lampu[j] = 0
  # print(array_lampu)

for e in array_lampu:
  string += str(e)
print("Keadaan akhir rangkaian lampu adalah", string)


from queue import *

class PriorityQueueSTN():

# Adding new methods to PriorityQueue
#
# Operator Output/Side Effect
# Q.state(x) returns the state of x, one of notYetInQ, inQ or alreadyPopped
# Q.key(x) returns the key/priority of x (which must be in the queue)
# Q.empty() returns true if the queue is currently empty
# Q.extractMinNode() pop the datum with minimum priority off the queue
# Q.insert(x, p) insert datum x into the queue Q with priority/key p
# Q.decreaseKey(x, p) decrease the priority/key of x (which must be in the queue) to p
# Q.clear () clear the contents of the queue

    def __init__(self, p_queue):
        self.p_queue = p_queue

        size = self.p_queue.qsize()
        counter = 0

        self.notYetinQ = 0
        self.inQ = 1
        self.alreadyPopped = 2

        self.queue = []
        self.popped = []

        while counter < size:
            #
            val = self.p_queue.get()
            self.queue.append(val)

            counter = counter + 1

    def state(self, x):
        # in popped
        if x in self.popped:
            return self.alreadyPopped
        # Not yet in Q
        elif x not in self.queue:
            return self.notYetinQ
        # in Q
        elif x in self.queue:
                return self.inQ

    def key(self, x):
        if self.state(x) == self.alreadyPopped:
            for val, index in self.popped:
                if x == val:
                    return index
        else:
            for i, val in enumerate(self.queue):
                if x == val:
                    return i

    def empty(self):
        return len(self.queue) == 0

    def extractMinNode(self):
        index = len(self.queue) - 1
        val = self.queue.pop()
        self.popped.append((val, index))
        return val

    def insert(self, x, p):
        self.queue.insert(p,x)

    def decreaseKey(self, x, p):
        self.queue.remove(x)
        self.queue.insert(p,x)

    def clear(self):
        self.queue = []

    def insertOrDecreaseKeyIfSmaller(self, x, p):
        state = self.state(x)
        if state == self.notYetinQ:
            self.insert(x,p)
        elif state == self.inQ:
            if p < self.key(x):
                self.decreaseKey(x,p)
        elif state == self.alreadyPopped:
            if p < self.key(x):
                return False
        return True

    def addToPopped(self, item):
        self.popped.insert(item)

    def setp_queue(self):
        p_queue = PriorityQueue()
        for i, val in enumerate(self.queue):
            p_queue.put((i, val))
        return p_queue


from typing import Dict, Generator, List
from uuid import uuid4

import pytest
from sqlalchemy.orm import Session

from fidesops.ops.models.connectionconfig import (
    AccessLevel,
    ConnectionConfig,
    ConnectionType,
)
from fidesops.ops.models.datasetconfig import DatasetConfig

@pytest.fixture(scope="function")
def email_connection_config(db: Session) -> Generator:
    name = str(uuid4())
    connection_config = ConnectionConfig.create(
        db=db,
        data={
            "name": name,
            "key": "my_email_connection_config",
            "connection_type": ConnectionType.email,
            "access": AccessLevel.write,
            "secrets": {"to_email": "test@example.com"},
        },
    )
    yield connection_config
    connection_config.delete(db)

@pytest.fixture(scope="function")
def email_dataset_config(
    email_connection_config: ConnectionConfig,
    db: Session,
    example_datasets: List[Dict],
) -> Generator:
    email_dataset = example_datasets[9]
    fides_key = email_dataset["fides_key"]
    email_connection_config.name = fides_key
    email_connection_config.key = fides_key
    email_connection_config.save(db=db)
    dataset = DatasetConfig.create(
        db=db,
        data={
            "connection_config_id": email_connection_config.id,
            "fides_key": fides_key,
            "dataset": email_dataset,
        },
    )
    yield dataset
    dataset.delete(db=db)


from random import randint
#-------------------------------Minha Resolução-----------------------------------
n = randint(0, 5)
n2 = int(input('Entre 0 e 5, qual número estou pensando? '))
if n == n2:
    print('Você acertou! o número que estou pensando é {}'.format(n))
else:
    print('Você errou! O número que estou pensando é {} e não {}'.format(n, n2))

#-------------------------------Resolução do Professor----------------------------
#from random import randint
#from time import sleep
#n = randint(0, 5)
#print('-=-' * 20)
#print('Vou pensar em um número entre 0 e 5. Tente adivinhar...')
#print('-=-' * 20)
#j = int(input('Em que número eu pensei? '))
#print('Processando...')
#sleep(3)
#if j == n:
#    print('Parabéns! Você conseguiu me vencer!')
#else:
#    print('Ganhei! Eu pensei no número {} e não no {}'.format(n, j))


#!/usr/bin/env python3

import helper

def get_column(i: int, lines: list[str]) -> list[str]:
    return [line[i] for line in lines]

def main():
    # lines = helper.read_lines("example.txt")
    lines = helper.read_lines("input.txt")
    # for line in lines:
        # print(line)
    no_of_columns = len(lines[0])
    # print(no_of_columns)
    result = []
    for i in range(no_of_columns):
        column = get_column(i, lines)
        zeros = column.count('0')
        ones = column.count('1')
        result.append('0' if zeros > ones else '1')
        # print("".join(column))
    #
    gamma_str = "".join(result)
    epsilon_str = gamma_str.replace('0', 'x').replace('1', '0').replace('x', '1')
    # print(gamma_str)
    # print(epsilon_str)
    gamma = int(gamma_str, 2)
    epsilon = int(epsilon_str, 2)

    print(gamma * epsilon)

##############################################################################

if __name__ == "__main__":
    main()