from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common import exceptions
from selenium.webdriver.support import expected_conditions as EC
from time import sleep
from datetime import datetime
import csv
import os

# Global variables
TIMEOUT = 5
LOGIN_PAGE = "https://mp.viennacontemporary.at/login"

# Initializes chromedriver and creates a global driver variable.
def driverInit():
    global driver
    settings = webdriver.ChromeOptions()
    # This option starts the webdriver maximized to the display.
    settings.add_argument("--start-maximized")
    settings.add_experimental_option("detach", True)
    driver = webdriver.Chrome(r"C:\Users\makan\Downloads\chromedriver.exe", options=settings)

    return driver

# Opens a specified URL with the webdriver.
def getPage(url:str):
    # strip() is used to make sure no special characters are left in the string. (such as \n)
    url = url.strip()
    driver.get(url)

# Logs into the specified account. Change variables in userData.py
def login():
    from userData import username, password
    address_xpath = "/html/body/div[1]/div/div[2]/div/div[2]/div/div/form/div[1]/div[1]/input"
    password_xpath = "/html/body/div[1]/div/div[2]/div/div[2]/div/div/form/div[1]/div[2]/input"
    enter_button_xpath = "/html/body/div[1]/div/div[2]/div/div[2]/div/div/form/div[2]/div/button"
    try:
        WebDriverWait(driver, timeout=TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, address_xpath)))
        driver.find_element_by_xpath(address_xpath).send_keys(username)
        driver.find_element_by_xpath(password_xpath).send_keys(password)
        driver.find_element_by_xpath(enter_button_xpath).click()
    except exceptions.TimeoutException:
        pass
    except exceptions.NoSuchElementException:
        pass
    except exceptions.ElementClickInterceptedException:
        pass

# Clicks on the accept cookies button in order to avoid ClickIntercepted exceptions.
def acceptCookies():
    cookie_button_xpath = "/html/body/div[1]/div/div[1]/button"
    driver.find_element_by_xpath(cookie_button_xpath).click()

# Clicks repetedly on the "Load More" button, until it no longer exists. -> Thus the page is fully loaded with all the artworks.
def expandPage():
    button_xpath = "/html/body/div[1]/div/div/div[2]/div/div[2]/div/div/div/div/div/button"
    status = True
    while status == True:
        attempts = 0
        while attempts < 10:
            try:
                driver.switch_to.window(driver.window_handles[0])
                WebDriverWait(driver, timeout=TIMEOUT).until(EC.element_to_be_clickable((By.XPATH, button_xpath)))
                e = driver.find_element_by_xpath(button_xpath)
                e.click()
                disabled = e.get_property("disabled")
                if disabled == True:
                    status = False
                    break
                else:
                    pass
            except exceptions.TimeoutException:
                attempts += 1
                try:
                    e = driver.find_element_by_xpath(button_xpath)
                    disabled = e.get_property("disabled")
                    if disabled == True:
                        status = False
                        break
                    else:
                        continue
                except:
                    status = False
                    break
            except exceptions.NoSuchElementException:
                status = False
                break
            except exceptions.StaleElementReferenceException:
                attempts += 1
                continue
            except exceptions.ElementNotInteractableException():
                status = False
                break

# Checks for the number of total artworks on the page.
# Currently not working (?)
def numberOfArtworks():
    number_xpath = "/html/body/div[1]/div/div/div[2]/div/div[2]/div/div/p/span"
    try:
        sleep(2)
        number = driver.find_element_by_xpath(number_xpath).text
        number = number.split(" ")[0]
    except:
        number = 800

    number = int(number)

    return number

# Gets the links for all the artworks on the page.
def getArtworks(indx:int):
    artwork_xpath = f"/html/body/div[1]/div/div/div[2]/div/div[2]/div/div/div/div/div[{indx+1}]/div[1]/div/div/img"
    art = driver.find_element_by_xpath(artwork_xpath)
    link = art.get_attribute("href")
    print(link)

    return link

# Exports the links variable into the data/links.txt file.
def exportLinks(links:list):
    with open("data/links.txt", "w") as f:
        for link in links:
            f.write(link + "\n")

# Imports the URLs into the links variable from the data/links.txt file.
def importLinks():
    links = []
    with open("data/links.txt", "r") as f:
        for line in f:
            links.append(line.strip())

    return links

def setupCSV():
    now = datetime.now()
    filename = (r"C:\Users\makan\OneDrive\Desktop\SBS\Artworks\Vienna_Contemporary\Vienna_Contemporary\data\\" + now.strftime("%d-%m-data.csv"))

    with open(filename, "w", encoding="UTF-8", newline="") as f:
        w = csv.writer(f, delimiter=";", quotechar = "|")
        w.writerow(["artist", "title", "year", "gallery", "price", "currency", "materials", "dimensions", "url", "image_url" , "sold"])

# Opens the individual artwork pages and scrapes price data.
def scrapeData(link:str):
    link = link.strip()
    getPage(link)
    try:
        WebDriverWait(driver, timeout=10).until(EC.element_to_be_clickable((By.XPATH, "/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[1]/div/a")))
        try:
            artist = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[1]/div/a/h1").text
        except:
            artist = ""
        try:
            title_bar = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[1]/p[2]/span").text
            try:
                title = title_bar.split(", ")[0]
                year = title_bar.split(", ")[1]
            except:
                title = title_bar
                year = ""
        except:
            title = ""
            year = ""
        try:
            gallery = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[5]/p[2]/a").text
        except:
            gallery = ""
        try:
            price = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/p[1]").text
        except:
            price = ""
        # Define by symbol at the start of the string!
        try:
            currency_prelim = price[0]
            if currency_prelim == "$":
                currency = "USD"
                price = price.replace("$", "")
            if currency_prelim == "€":
                currency = "EUR"
                price = price.replace("€", "")
        except:
            currency = ""
        try:
            materials = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[3]/p[2]").text
        except:
            materials = ""
        try:
            status = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/p[2]/span").text
        except:
            status = ""
        try:
            dimensions = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[2]/p[2]").text
        except:
            dimensions = ""

        try:
            url = link
        except:
            url = ""

        try:
            image_url = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[1]/div[1]/div/div[1]/img")["srcset"]
        except:
            image_url = ""

    except exceptions.TimeoutException:
        try:
            artist = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[1]/div/a/h1").text
        except:
            artist = ""
        try:
            title_bar = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[1]/p[2]/span").text
            try:
                title = title_bar.split(", ")[0]
                year = title_bar.split(", ")[1]
            except:
                title = title_bar
                year = ""
        except:
            title = ""
            year = ""
        try:
            gallery = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[5]/p[2]/a").text
        except:
            gallery = ""
        try:
            price = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/p[1]").text
        except:
            price = ""
        # Define by symbol at the start of the string!
        try:
            currency_prelim = price[0]
            if currency_prelim == "$":
                currency = "USD"
                price = price.replace("$", "")
            if currency_prelim == "€":
                currency = "EUR"
                price = price.replace("€", "")
        except:
            currency = ""
        try:
            materials = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[3]/p[2]").text
        except:
            materials = ""
        try:
            status = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/p[2]/span").text
        except:
            status = ""
        try:
            dimensions = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[2]/div[1]/div[2]/div/div[2]/p[2]").text
        except:
            dimensions = ""

        try:
            url = link
        except:
            url = ""

        try:
            img = driver.find_element_by_xpath("/html/body/div[1]/div/div/div/div[2]/div/div[1]/div[1]/div/div[1]/img")
            image_url = img.get_attribute("src")
        except:
            image_url = ""

    ret = [artist, title, year, gallery, price, currency, materials, dimensions, url, image_url, status]
    return ret

# Exports the price data into a csv file.
def exportData(l:list):
    now = datetime.now()
    filename = (r"C:\Users\makan\OneDrive\Desktop\SBS\Artworks\Vienna_Contemporary\Vienna_Contemporary\data\\" + now.strftime("%d-%m-data.csv"))
    with open(filename, "a", encoding="UTF-8", newline="") as f:
        w = csv.writer(f, delimiter=";", quotechar = "|")
        for item in l:
            w.writerow(item)

if __name__ == "__main__":
    driver = driverInit()
    # Goes to the login page and enters details.
    getPage(LOGIN_PAGE)
    login()
    links = []
    sleep(5)

    # IMPORTANT !
    # Set the BOOL value according to needs. If there are already URLs recorded => False

    ###################################################################################################################
    getLinks = True
    ###################################################################################################################

    # This process is in case we do not have a complete list of URLs already recorded in data/links.txt
    if getLinks == True:
        total_artworks = numberOfArtworks()
        print(total_artworks)
        acceptCookies()
        expandPage()
        for inx in range(total_artworks):
            links.append(getArtworks(inx))
        exportLinks(links)

    # This process is in case we already have a complete list of URLs recorded
    elif getLinks == False:
        links = importLinks()

    data = []
    for link in links:
        export = scrapeData(link)
        data.append(export)
        print(export)

    setupCSV()
    exportData(data)


from rest_framework import serializers
from api.models.model_year_report_compliance_obligation import ModelYearReportComplianceObligation
from api.models.model_year_report_credit_offset import ModelYearReportCreditOffset
from api.serializers.vehicle import ModelYearSerializer

## CAN BE REMOVED LATER
# class ModelYearReportComplianceObligationOutputSerializer(serializers.ModelSerializer):
#     model_year = ModelYearSerializer(read_only=True)
#     A = serializers.SerializerMethodField()
#     B = serializers.SerializerMethodField()

#     def get_A(self, obj, *args, **kwargs):
#         return obj.credit_a_value

#     def get_B(self, obj, *args, **kwargs):
#         return obj.credit_b_value

#     class Meta:
#         model = ModelYearReportComplianceObligation
#         fields = (
#             'model_year', 'A', 'B'
#         )

class ModelYearReportComplianceObligationOffsetSerializer(serializers.ModelSerializer):
    model_year = ModelYearSerializer(read_only=True)

    class Meta:
        model = ModelYearReportCreditOffset
        fields = (
            'credit_a_offset_value',  'credit_b_offset_value',
            'model_year', 'model_year_report'
        )

class ModelYearReportComplianceObligationSnapshotSerializer(serializers.ModelSerializer):
    model_year = ModelYearSerializer(read_only=True)

    class Meta:
        model = ModelYearReportComplianceObligation
        fields = (
            'credit_a_value',  'credit_b_value',
            'category', 'model_year', 'update_timestamp',
        )

# class ModelYearReportComplianceObligationDetailsSerializer(serializers.ModelSerializer):
#     """
#     """
#     prior_year_balance = serializers.SerializerMethodField()
#     report_year_balance = serializers.SerializerMethodField()
#     report_year_transactions = serializers.SerializerMethodField()
#     pending_balance = serializers.SerializerMethodField()
#     def retrieve_year(self, report_id):
#         report = ModelYearReport.objects.get(
#             id=report_id
#         )
#         report_year_obj = ModelYear.objects.get(
#             id=report.model_year_id
#         )
#         report_year = int(report_year_obj.name)
#         return report_year

#     def get_prior_year_balance(self, obj, *args, **kwargs):
#         ## this needs to change to grab to grab values from the prior year snapshot
#         kwargs = self.context.get('kwargs')
#         report_id = int(kwargs.get('id'))
#         report_year = self.retrieve_year(report_id)
#         prior_year = report_year-1
#         prior_year_balance_a = self.retrieve_balance(prior_year, 1)
#         prior_year_balance_b = self.retrieve_balance(prior_year, 2)
#         return {'year': prior_year, 'A': prior_year_balance_a, 'B': prior_year_balance_b}

    # def get_report_year_transactions(self, obj, *args, **kwargs):
    #     request = self.context.get('request')
    #     kwargs = self.context.get('kwargs')
    #     report_id = int(kwargs.get('id'))
    #     report_year = self.retrieve_year(report_id)

    #     transfers_in = CreditTransaction.objects.filter(
    #         credit_to=request.user.organization,
    #         transaction_type__transaction_type='Credit Transfer',
    #         transaction_timestamp__lte=date(report_year, 9, 30),
    #         transaction_timestamp__gte=date(report_year-1, 10, 1),
    #     ).values(
    #         'credit_class_id', 'model_year_id'
    #     ).annotate(
    #         total_value=Sum('total_value')
    #     ).order_by(
    #         'credit_class_id', 'model_year_id'
    #     )

    #     transfers_out = CreditTransaction.objects.filter(
    #         debit_from=request.user.organization,
    #         transaction_type__transaction_type='Credit Transfer',
    #         transaction_timestamp__lte=date(report_year, 9, 30),
    #         transaction_timestamp__gte=date(report_year-1, 10, 1),
    #     ).values(
    #         'credit_class_id', 'model_year_id'
    #     ).annotate(total_value=Sum(
    #         'total_value')
    #     ).order_by(
    #         'credit_class_id', 'model_year_id'
    #     )

    #     credits_issued_sales = CreditTransaction.objects.filter(
    #         credit_to=request.user.organization,
    #         transaction_type__transaction_type='Validation',
    #         transaction_timestamp__lte=date(report_year, 9, 30),
    #         transaction_timestamp__gte=date(report_year-1, 10, 1),
    #     ).values(
    #         'credit_class_id', 'model_year_id'
    #     ).annotate(
    #         total_value=Sum('total_value')
    #     ).order_by(
    #         'credit_class_id', 'model_year_id'
    #     )

    #     transfers_in_serializer = CreditTransactionObligationActivitySerializer(transfers_in, read_only=True, many=True)
    #     transfers_out_serializer = CreditTransactionObligationActivitySerializer(transfers_out, read_only=True, many=True)
    #     credit_sales_serializer = CreditTransactionObligationActivitySerializer(credits_issued_sales, read_only=True, many=True)
    #     content = {
    #         'transfers_in': transfers_in_serializer.data,
    #         'transfers_out': transfers_out_serializer.data,
    #         'credits_issued_sales': credit_sales_serializer.data,
    #         'adjustments_validation': adjustments_validation_serializer.data,
    #         'adjustments_reduction': adjustments_reduction_serializer.data
    #         }
    #     return content

    # def get_pending_balance(self, obj, *args, **kwargs):
    #     kwargs = self.context.get('kwargs')
    #     report_id = int(kwargs.get('id'))
    #     report_year = self.retrieve_year(report_id)
    #     request = self.context.get('request')
    #     pending_sales_submissions = SalesSubmission.objects.filter(
    #         organization=request.user.organization,
    #         validation_status__in=['SUBMITTED', 'RECOMMEND_APPROVAL', 'RECOMMEND_REJECTION', 'CHECKED']
    #     )
    #     totals = {}
    #     for obj in pending_sales_submissions:
    #         for record in obj.get_content_totals_by_vehicles():
    #             try:
    #                 model_year = float(record['xls_model_year'])
    #             except ValueError:
    #                 continue
    #             if int(model_year) != int(report_year):
    #                 continue
    #             vehicle = Vehicle.objects.filter(
    #                 make__iexact=record['xls_make'],
    #                 model_name=record['xls_model'],
    #                 model_year__name=int(model_year),
    #                 validation_status=VehicleDefinitionStatuses.VALIDATED,
    #             ).first()
    #             if vehicle:
    #                 model_year_str = str(int(model_year))
    #                 if model_year_str not in totals.keys():
    #                     totals[model_year_str] = {'A': 0, 'B': 0}
    #                 totals[model_year_str][vehicle.get_credit_class()] += vehicle.get_credit_value() * record['num_vins']
    #     return totals

    # class Meta:
    #     model = CreditTransaction
    #     fields = (
    #         'report_year_balance', 'pending_balance', 'prior_year_balance', 'report_year_transactions'
    #     )

## CAN BE REMOVED LATER
# class ModelYearReportComplianceObligationSaveSerializer(serializers.ModelSerializer):
#     model_year = ModelYearSerializer()

#     def create(self, obj, validated_data):
#         return obj

#     class Meta:
#         model = ModelYearReportComplianceObligation
#         fields = ('model_year_report', 'model_year', 'credit_a_value',
#                   'credit_b_value', 'category')


from rest_framework.permissions import BasePermission, SAFE_METHODS
from rest_framework import exceptions
from account.authentication import UserTokenAuthentication

class AdminPermissions(BasePermission):
    def has_permission(self, request, view):

        if request.method in ["POST", "PUT", "DELETE", "PATCH"]:
            try:
                user = request.user
            except:
                raise exceptions.AuthenticationFailed("No User Found")

            if user.is_superuser:
                return True
            else:
                return request.method in SAFE_METHODS
        else:
            return True


import pandas as pd
import numpy as np
from statsmodels.discrete.discrete_model import Logit
import statsmodels.api as sm
import json

model_fit = sm.load('logistic.pkl')

def f_tranform_customer_features(customer):
    columns = ['monto', 'numero_tc', 'hora', 'linea_tc', 'interes_tc', 'is_prime',
       'dcto', 'cashback', 'device_score', 'ciudad_Guadalajara',
       'ciudad_Merida', 'ciudad_Monterrey', 'ciudad_Otro', 'ciudad_Toluca',
       'genero_--', 'genero_F', 'genero_M', 'model_2020', 'tipo_tc_Física',
       'tipo_tc_Virtual', 'status_txn_Aceptada', 'status_txn_En proceso',
       'status_txn_Rechazada', 'establecimiento_Abarrotes',
       'establecimiento_Farmacia', 'establecimiento_MPago',
       'establecimiento_Otro', 'establecimiento_Restaurante',
       'establecimiento_Super', 'os_%%', 'os_.', 'os_ANDROID', 'os_WEB']

    df_customer = pd.DataFrame.from_dict(customer, orient='index').transpose()
    df_customer = pd.get_dummies(data=df_customer,
                                 columns=['genero','establecimiento','ciudad','tipo_tc','status_txn','os']
                                )
    df_customer['is_prime'] = df_customer['is_prime']*1
    for col in columns:
        try:
            df_customer[col] = df_customer[col]
        except:
            df_customer[col] = 0
    return df_customer[columns]

def f_detect_fraudster(customer):
    customer_mod = f_tranform_customer_features(customer)
    fraudster_risk = model_fit.predict(customer_mod.astype(float))[0]
    return {'risk':fraudster_risk,'fraudster':str(fraudster_risk>0.2)}

def lambda_handler(event, context):
    customer = json.loads(event['body'])
    print("Customer params: " + str(customer))

    return {  "body": json.dumps(f_detect_fraudster(customer)) }


"""
Streaming and collision step
============================
The streaming step is the first step of the Lattice Boltzmann Method (LBM).
The streaming step is a simple shift of the pdf in the direction of the
velocity. The collision step is the second step of the LBM. The collision step
is a relaxation of the pdf towards the equilibrium pdf. The equilibrium pdf is
the pdf that would be obtained if the fluid particles were in equilibrium. The
equilibrium pdf is calculated using the density and the velocity of the fluid
particles.
============================
"""
from matplotlib import cm
from array2gif import write_gif
from hpc_fluid_dynamics.lbm_utils import *
import ipyparallel as ipp
from mpi4py import MPI

NCPU = 4
cluster = ipp.Cluster(engines="mpi", n=NCPU)
client = cluster.start_and_connect_sync()
#
client.ids

comm = MPI.COMM_WORLD      # start the communicator assign to comm
size = comm.Get_size()     # get the size and assign to size
rank = comm.Get_rank()

dx = 0.1     # = dy
nt = 100  # timesteps to iterate
dt = 0.0001   # timestep length
D = 10        # diffusion constant

print('Rank/Size {}/{}'.format(rank,size))

NX = 250
NY = 250

### Domain decomposition
if NX < NY:
    sectsX=int(np.floor(np.sqrt(size*NX/NY)))
    sectsY=int(np.floor(size/sectsX))
    print('We have {} fields in x-direction and {} in y-direction'.format(sectsX,sectsY))
    print('How do the fractions look like?')
    print('NX/NY={} and sectsX/sectsY = {}\n'.format(NX/NY,sectsX/sectsY))
elif NX > NY:
    sectsY=int(np.floor(np.sqrt(size*NY/NX)))
    sectsX=int(np.floor(size/sectsY))
    print('We have {} fields in x-direction and {} in y-direction'.format(sectsX,sectsY))
    print('How do the fractions look like?')
    print('NX/NY={} and sectsX/sectsY = {}\n'.format(NX/NY,sectsX/sectsY))
elif NX==NY:
    sectsY=int(np.floor(np.sqrt(size)))
    sectsX=int(size/sectsY)
    if rank == 0: print('In the case of equal size we divide the processes as {} and {}'.format(sectsX,sectsY))

sectsX=int(np.floor(np.sqrt(size)))
sectsY=int(size//sectsX)

nxsub = NX//sectsX+2
nysub = NY//sectsY+2
boundary_k=[False,False,False,False]
cartcomm=comm.Create_cart(dims=[sectsX,sectsY],periods=[True,True],reorder=False)
rcoords = cartcomm.Get_coords(rank)

# where to receive from and where send to
sR,dR = cartcomm.Shift(1,1)
sL,dL = cartcomm.Shift(1,-1)

sU,dU = cartcomm.Shift(0,-1)
sD,dD = cartcomm.Shift(0,1)

sd = np.array([sR,dR,sL,dL,sU,dU,sD,dD], dtype = int)

allrcoords = comm.gather(rcoords,root = 0)
allDestSourBuf = np.zeros(size*8, dtype = int)
comm.Gather(sd, allDestSourBuf, root = 0)

if rank == 0:
    c_plot_list = []
    print(allrcoords)
    print(' ')
    cartarray = np.ones((sectsY,sectsX),dtype=int)
    allDestSour = np.array(allDestSourBuf).reshape((size,8))
    for i in np.arange(size):
        cartarray[allrcoords[i][0],allrcoords[i][1]] = i
        print('Rank {} all destinations and sources {}'.format(i,allDestSour[i,:]))
        sR,dR,sL,dL,sU,dU,sD,dD = allDestSour[i]
        print('Rank {} is at {}'.format(i,allrcoords[i]))
        print('sour/dest right {} {}'.format(sR,dR))
        print('sour/dest left  {} {}'.format(sL,dL))
        print('sour/dest up    {} {}'.format(sU,dU))
        print('sour/dest down  {} {}'.format(sD,dD))
        #print('[stdout:',i,']',allDestSour[i])
    print('')
    print(cartarray)

def Communicate(c,cartcomm,sd):
    recvbuf = np.zeros(c[:,1].shape[0])
    sR,dR,sL,dL,sU,dU,sD,dD = sd
    # Send to right which is destination rigth (dR) and receive from left which is source right (sR)
    # print(rank,'Right, source',sR,'destination',dR)
    sendbuf = c[:,-2].copy() # Send the second last column to dR
    cartcomm.Sendrecv(sendbuf, dR, recvbuf = recvbuf, source = sR)
    c[:,0] = recvbuf # received into the 0th column from sR
    # Send to left and receive from right
    #print(rank,'Left, source',sL,'destination',dL)
    sendbuf = c[:,1].copy()
    cartcomm.Sendrecv(sendbuf, dL, recvbuf = recvbuf, source = sL)
    c[:,-1] = recvbuf
    # Send to up and receive from down
    #print(rank,'Up, source',sU,'destination',dU)
    sendbuf = c[1,:].copy()
    cartcomm.Sendrecv(sendbuf, dU, recvbuf = recvbuf, source = sU)
    c[-1,:] = recvbuf
    # Send to down and receive from up
    #print(rank,'Down, source',sD,'destination',dD)
    sendbuf = c[-2,:].copy()
    cartcomm.Sendrecv(sendbuf, dD, recvbuf = recvbuf, source = sD)
    c[0,:]=recvbuf
#
    return c

## INTIALIZE THE GRID
x = np.arange(rcoords[0]*NX//sectsX,(rcoords[0]+1)*NX//sectsX)*dx
y = np.arange(rcoords[1]*NY//sectsX,(rcoords[1]+1)*NY//sectsY)*dx
X,Y = np.meshgrid(x,y)
sigma0 = 30*dx
c = np.zeros((nxsub,nysub))
c[1:-1,1:-1] = np.exp(-((X-NX/2*dx)**2+(Y-NY/2*dx)**2)/(2*sigma0**2)) / (np.sqrt(2*np.pi)*sigma0)

for t in np.arange(nt):
    # First we need a communication step
    c = Communicate(c,cartcomm,sd)
    # Then we do a timestep forward
    cup = (np.roll(c,shift=(1,0),axis=(0,1)).copy())[1:-1,1:-1]
    cdown = (np.roll(c,shift=(-1,0),axis=(0,1)).copy())[1:-1,1:-1]
    cleft = (np.roll(c,shift=(0,1),axis=(0,1)).copy())[1:-1,1:-1]
    crght = (np.roll(c,shift=(0,-1),axis=(0,1)).copy())[1:-1,1:-1]
    ccent = c.copy()[1:-1,1:-1]
    c[1:-1,1:-1] = c[1:-1,1:-1] + D*dt/dx**2*(cleft+cdown -4.*ccent+crght+cup)

    c_full_range = np.zeros((NX*NY))
    comm.Gather(c[1:-1,1:-1].reshape((nxsub-2)*(nysub-2)), c_full_range, root = 0)
    rcoords_x = comm.gather(rcoords[1], root=0)
    rcoords_y = comm.gather(rcoords[0], root=0)
    if rank == 0:

        X0, Y0 = np.meshgrid(np.arange(NX),np.arange(NY))
        xy = np.array([rcoords_x,rcoords_y]).T
        c_plot = np.zeros((NX,NY))
        #
        for i in np.arange(sectsX):
            for j in np.arange(sectsY):
                k = i*sectsX+j
                xlo = NX//sectsX*xy[k,1]
                xhi = NX//sectsX*(xy[k,1]+1)
                ylo = NY//sectsY*xy[k,0]
                yhi = NY//sectsY*(xy[k,0]+1)
                clo = k*NX*NY//(sectsX*sectsY)
                chi = (k+1)*NX*NY//(sectsX*sectsY)

                c_plot[xlo:xhi,ylo:yhi] = c_full_range[clo:chi].reshape(NX//sectsX,NY//sectsY)
        #print the middle of the grid
        print(c_plot[NX//2,NY//2])
        c_plot_list.append(c_plot)

if rank == 0:

    c_plot_list = np.array(c_plot_list)
    print(c_plot_list.shape)
    c_plot_list = c_plot_list[..., np.newaxis] * np.ones(3)
    c_plot_list = c_plot_list / np.max(c_plot_list) * 255
    write_gif(c_plot_list, 'results/diffusion.gif', fps=10)

client.shutdown()


from camera import CameraMan
from dobot_driver import DobotBot
from sheets_com import Sheet
from boardmanager import Board
from copy import deepcopy
import keyboard
import cv2

print("------------Welcome to dobot tic-tac-toe------------")
print("Please select the gamemode:")
print("Physical mode - place blocks by hand in the real world")
print("Virtual mode - place blocks in the sheet and the robot moves the blocks onto the board")
physical = False #if input("Physical or virtual? ").upper()=="Physical".upper() else False
print(physical)

cam = CameraMan()
dobot = DobotBot("/dev/ttyUSB0")

calib = False

coords = [0,0,0]
with open("coords.txt","r") as file:
    lines = file.readlines()
    if len(lines)>2:
        coords[0] = float(lines[0])
        coords[1] = float(lines[1])
        coords[2] = float(lines[2])
    else:
        calib = True

print(coords)
cam.startCam(False)

if not calib:
    if input("Do you want to calibrate again? ").lower() == "yes":
        calib = True

if calib:
    initializing = True
    print("Please align the robot to so the suction cup is on the playing field in the middle below the grid")
    print("After you're done, press space")
    while initializing:
        camera = cam.getCam("image")
        if camera is not None:
                if camera.shape[0] > 0:
                    cv2.imshow("Output",camera)
                    if cv2.waitKey(1) & 0xFF==ord('q'):
                        exit()
        if keyboard.is_pressed("space"):
            initializing = False

    dobot.calibHeight()
    print("Successfully initalized!")
    print("Calibrating...")

    calibrating = True
    while calibrating:
        print("Current offset:","X:",cam.getOffset()[1],"Y:",cam.getOffset()[0],)
        camera = cam.getCam("image")
        if camera is not None:
            if camera.shape[0] > 0:
                cv2.imshow("Output",camera)
        if cv2.waitKey(1) & 0xFF==ord('q'):
            exit()
        dobot.calibrate(cam.getOffset()[1],cam.getOffset()[0])
        if cam.getOffset()[0]>-2 and cam.getOffset()[0]<2 and cam.getOffset()[1]>-2 and cam.getOffset()[1]<2:
            print("Succesfully calibrated!")
            calibrating = False
            coords = dobot.setHome()
            with open("coords.txt","w") as file:
                file.writelines([str(i)+"\n" for i in coords])
    print("The robot will now proceed to go to the position of each square.")
    print("Please confirm that the square locations are correct and if nescessary, recalibrate")

    for i in range(12):
        dobot.goToPos(i)
    dobot.go_home()
else:
    print("bro")
    dobot.dobot.move_to(coords[0],coords[1],coords[2],0,wait=True)
    dobot.setHome()

print("Place the blocks and press space")
placing = True
while placing:
    if keyboard.is_pressed("space"):
            placing = False

sheet = Sheet("1YZTYIE4sfvTNDYRF8hQObEbRfSvaMIv0aTk6Cgih7gA","Sheet1")
boardman = Board()

if physical:
    running = True

    while running: #idk if this is the correct way of doing this, but it doesn't matter that much
        camera = cam.getCam("image")
        blue = cam.getCam("blue_out")
        red = cam.getCam("red_out")
        cv2.imshow("image",camera)
        cv2.imshow("blue",blue)
        cv2.imshow("red",red)
        if cv2.waitKey(1) & 0xFF==ord('q'):
            exit()
else:
    while True:
        input("Press enter to get data from google sheets and place the required blocks")

        data = sheet.get_data_from_sheet()
        print(data)
        data = [[ None if e == "nan" else e for e in i ] for i in data] #replace nan with None
        print(data)

        old_board = deepcopy(boardman.board)
        boardman.set_board(data)
        new_board = data

        boardman.print_board(old_board)
        boardman.print_board(data)

        count = 0

        to_remove = []
        to_place = []

        for i in range(len(old_board)):
            for e in range(len(old_board[i])):
                if old_board[i][e]!=new_board[i][e]:
                    if old_board[i][e] != None:
                        to_remove.append((old_board[i][e],count))
                    if new_board[i][e]!=None:
                        to_place.append((new_board[i][e],count))
                count+=1

        for block in to_remove:
            print("removing: ",block)
            dobot.remove_block(block[0],block[1])

        for block in to_place:
            print("placing: ",block)
            dobot.place_block(block[0],block[1])

    #print("lmao")

    #dobot.place_block("r",4)
    #dobot.place_block("r",2)
    #dobot.place_block("r",6)
    #dobot.place_block("r",0)
    #dobot.place_block("r",8)
    #dobot.place_block("b",4)
    #dobot.place_block("b",2)
    #dobot.place_block("b",6)
    #dobot.place_block("b",0)
    #dobot.place_block("b",8)


import random
import sys
from termcolor import colored
import colorama
from colorama import Fore
import nltk
import socket
nltk.download("words.txt")
from nltk.corpus import words

print("\n")
print("__          __           _ _        _ ")
print("\ \        / /          | | |      | |")
print(" \ \  /\  / /__  _ __ __| | | ___  | |")
print("  \ \/  \/ / _ \| '__/ _` | |/ _ \ | |")
print("   \  /\  / (_) | | | (_| | |  __/ |_|")
print("    \/  \/ \___/|_|  \__,_|_|\___| (_)")
print("\n")

host = '192.168.1.16'
port = 21568

counter = 0
backCounter = -1
wordsUsed = ["","","","","",""]

rows, cols = (6,5)
wordleBoard = [["" for i in range(cols)] for j in range(rows)]

status = [["" for i in range(cols)] for j in range(rows)]

for i in range(len(wordleBoard)):
    for j in range(len(wordleBoard[i])):
        wordleBoard[i][j] = " _ "

def print_menu():
    print("Enter a 5 letter word")

nltk.data.path.append("words.txt")

wordList = words.words()
#splits up the word
words_five = [word for word in wordList if len(word) == 5]
#print(len(words_five))
print_menu()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))

while True:
    punishment = 1
    #makes it lower case
    word = random.choice(words_five).lower()
    for attempt in range(1, 7):
        guess = input().lower()
        sys.stdout.write('\x1b[1A')
        sys.stdout.write('\x1b[2K')
        if wordsUsed[backCounter] == guess:
                print("You've already guessed this word. Try again")
        else:
            for i in range( min(len(guess), 5) ):
                #right, right place
                    wordsUsed[counter] = guess
                    if guess[i] == word[i]:
                        #print(colored(guess[i], 'green'), end=" ")
                        wordleBoard[counter][i] = "\x1b[92m " + guess[i] + " "
                        status[counter][i] = 0
                    #right, wrong place
                    elif guess[i] in word:
                        #print(colored(guess[i], 'yellow'), end=" ")
                        wordleBoard[counter][i] = "\x1b[93m " + guess[i] + " "
                        status[counter][i] = 1
                        punishment+=0.05
                    #wrong
                    else:
                        #print(guess[i], end=" ")
                        wordleBoard[counter][i] = "\x1b[37m " + guess[i] + " "
                        status[counter][i] = 2
                        punishment+=0.1

        print()
        counter+=1
        backCounter+=1

        #got it right
        if guess == word:
            print(colored(f"Congrats you got the word in {attempt}", 'blue'))
            break

        #six attempts
        elif attempt == 6:
            print(f"You're ass, the word was {word}")
            break

        #print with colors
        for i in range(len(wordleBoard)):
            print("")
            for j in range(len(wordleBoard[i])):
                line = ""
                if("\x1b[92m" in wordleBoard[i][j]):
                    print(Fore.GREEN + " " + wordleBoard[i][j] + " ", end = '')
                elif("\x1b[91m" in wordleBoard[i][j]):
                    print(Fore.RED + " " + wordleBoard[i][j] + " ", end = '')
                elif("\x1b[93m" in wordleBoard[i][j]):
                    print(Fore.YELLOW + " " + wordleBoard[i][j] + " ", end = '')
                else:
                    print(Fore.WHITE + " " + wordleBoard[i][j] + " ", end = '')
        #pain...
        s.send(str.encode(str(punishment)))
        reply = s.recv(1024)
        print(" \n\n"+reply.decode('utf-8'))
        print("\n")
    break
s.close()

# while True:
#     command = input("Enter your command: ")
#     if command == 'EXIT':
#         s.send(str.encode(command))
#         break
#     elif command == 'KILL':
#         s.send(str.encode(command))
#         break
    # s.send(str.encode(command))
    # reply = s.recv(1024)
    # print(reply.decode('utf-8'))
# s.close()


import torch.nn as nn
import torch
import random
import librosa
import numpy as np
import mido
import os

num_epochs = 10
batch_size = 1
learning_rate = 0.001

input_size = 1025
output_size = 88
# sequence_length = 28
hidden_size = 128
num_layers = 2

device = torch.device('cpu')

FRAME_SIZE = 2048
HOP_SIZE = 512
SAMPLE_RATE = 22050

class Encoder(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers):
        super().__init__()

        self.num_layers = num_layers
        self.hidden_size = hidden_size

        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        # inpt -> (batch_size, seq_len, input_size)

    def forward(self, x):
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(device)
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(device)

        out, (hidden, cell) = self.lstm(x, (h0, c0))
        # out -> (batch_size, seq_len, hidden_size)

        return hidden, cell
class Decoder(nn.Module):
    def __init__(self, output_size, hidden_size, num_layers):
        super().__init__()

        self.output_size = output_size
        self.hidden_size = hidden_size
        self.num_layers = num_layers

        self.rnn = nn.LSTM(output_size, hidden_size, num_layers, batch_first=True)

        self.fc_out = nn.Linear(hidden_size, output_size)

    def forward(self, inpt, hidden, cell):
        inpt = inpt.unsqueeze(0)
        output, (hidden, cell) = self.rnn(inpt, (hidden, cell))

        prediction = torch.sigmoid(self.fc_out(output.squeeze(0)))

        #prediction -> [batch size, output dim]

        return prediction, hidden, cell

class Seq2Seq(nn.Module):
    def __init__(self, encoder, decoder, device):
        super().__init__()

        self.encoder = encoder
        self.decoder = decoder
        self.device = device

        assert encoder.hidden_size == decoder.hidden_size, \
            "Hidden dimensions of encoder and decoder must be equal!"
        assert encoder.num_layers == decoder.num_layers, \
            "Encoder and decoder must have equal number of layers!"

    def forward(self, src, trg, teacher_forcing_ratio = 0.5):
        if str(type(trg)) =="<class 'bool'>":
            if trg == False:
                batch_size = src.shape[0]
                trg_len = src.shape[1] * 22
                trg_note_count = self.decoder.output_size

                #store decoder outputs
                outputs = torch.zeros(trg_len, batch_size, trg_note_count).to(self.device)

                #last hidden state of the encoder is used as the initial hidden state of the decoder
                hidden, cell = self.encoder(src.to(self.device))
                hidden, cell = hidden.to(self.device), cell.to(self.device)
                #first input to the decoder is zerozerozero and soo ooonnnn
                inpt = torch.zeros(batch_size, trg_note_count)

                for t in range(1, trg_len):
                    output, hidden, cell = self.decoder(inpt.to(self.device), hidden, cell)

                    #place predictions in a tensor holding predictions for each token
                    outputs[t] = output

                    inpt = output

                return outputs

        batch_size = trg.shape[0]
        trg_len = trg.shape[1]
        trg_note_count = self.decoder.output_size

        #store decoder outputs
        outputs = torch.zeros(trg_len, batch_size, trg_note_count).to(self.device)

        #last hidden state of the encoder is used as the initial hidden state of the decoder
        hidden, cell = self.encoder(src.to(self.device))
        hidden, cell = hidden.to(self.device), cell.to(self.device)
        #first input to the decoder is zerozerozero and soo ooonnnn
        inpt = torch.zeros(batch_size, trg_note_count)

        for t in range(1, trg_len):
            output, hidden, cell = self.decoder(inpt.to(self.device), hidden, cell)

            #place predictions in a tensor holding predictions for each token
            outputs[t] = output

            #decide if we are going to use teacher forcing or not
            teacher_force = random.random() < teacher_forcing_ratio

            #if teacher forcing, use actual next token as next input
            #if not, use predicted token
            inpt = trg[:, t, :] if teacher_force else output

        return outputs

encoder = Encoder(input_size, hidden_size, num_layers).to(device)
decoder = Decoder(output_size, hidden_size, num_layers).to(device)
model = Seq2Seq(encoder, decoder, device)

def wavfile2spec(wavfile):
    wavRaw, sample_rate = librosa.load(wavfile)
    wavSpec = librosa.stft(wavRaw, n_fft=FRAME_SIZE, hop_length=HOP_SIZE)
    wavSpec = np.abs(wavSpec) ** 2
    wavSpec = librosa.power_to_db(wavSpec)
    return torch.from_numpy(wavSpec)

model_load = torch.load(os.path.join('model', 'notescribe.pt'), map_location=device)
# print(model_load.items())
for k, v in model_load.items():
    print(k, v)
# model_load.eval()
# print(model_load)
model = model.load_state_dict(model_load)
print('MODEL: ', model, 'TYPE: ', type(model))
model.eval()

def wavfile2midifile(wavfile, out_path):
    wavSpec = wavfile2spec(wavfile).transpose(0, 1).unsqueeze(0)
    midiData = torch.round(model(wavSpec, False).squeeze()).int()
    midi = tnsr2mid(midiData)
    midi.save(out_path)

def tnsr2mid(tnsr, tempo=500000):
    ary = tnsr.cpu().detach().numpy()
    # get the difference
    new_ary = np.concatenate([np.array([[0] * 88]), np.array(ary)], axis=0)
    changes = new_ary[1:] - new_ary[:-1]
    # create a midi file with an empty track
    mid_new = mido.MidiFile()
    track = mido.MidiTrack()
    mid_new.tracks.append(track)
    track.append(mido.MetaMessage('set_tempo', tempo=tempo, time=0))
    # add difference in the empty track
    last_time = 0
    for ch in changes:
        if set(ch) == {0}:  # no change
            last_time += 1
        else:
            on_notes = np.where(ch > 0)[0]
            on_notes_vol = ch[on_notes]
            off_notes = np.where(ch < 0)[0]
            first_ = True
            for n, v in zip(on_notes, on_notes_vol):
                new_time = last_time if first_ else 0
                track.append(mido.Message('note_on', note=n + 21, velocity=v, time=new_time))
                first_ = False
            for n in off_notes:
                new_time = last_time if first_ else 0
                track.append(mido.Message('note_off', note=n + 21, velocity=0, time=new_time))
                first_ = False
            last_time = 0
    return mid_new


import numpy as np

from pytest import approx

from spike2py_preprocess import study
import spike2py

def test_preprocess_study_no_subject_preprocess(study1_path, sub01_paths, sub02_paths):
    study.study(study1_path)
    file1 = sub01_paths.proc / "conv_biphasic.pkl"
    file2 = sub01_paths.proc / "khz_biphasic.pkl"
    assert file1.exists()
    assert file2.exists()
    data1 = spike2py.trial.load(file1)
    data2 = spike2py.trial.load(file2)
    channels = [("W_Ext", "waveform"), ("Fdi", "waveform"), ("Stim", "waveform")]
    assert data1.channels == channels
    assert data2.channels == channels
    assert np.mean(data1.Fdi.values) == approx(1.8046494617496535e-09)
    file3 = sub02_paths.proc / "conv_biphasic.pkl"
    file4 = sub02_paths.proc / "khz_biphasic.pkl"
    file1.unlink()
    file2.unlink()
    file3.unlink()
    file4.unlink()


# BOT TESTING SERVER LINK:
# https://discord.gg/UEwf6RWqU2
# Created by: Mizu (M1zushi)

import os

# PACKAGES & IMPORTS
import discord
import discord.ext.commands as commands
from dotenv import load_dotenv

load_dotenv('.env')

# SETS UP BASICS
client = commands.Bot(command_prefix=('ps ', 'ps', 'Ps ', 'PS '),
                      case_insensitive=True,
                      activity=discord.Activity(
                          type=discord.ActivityType.playing, name='sls spam'),
                      status=discord.Status.dnd,
                      )

@client.event
async def on_ready():
    print('Time to spam sls.')

@client.command(name="ping", brief="Shows bot latency",
                help="Type ``ping`` after the respective prefix to see the bot's latency.")
async def _ping(ctx):
    await ctx.send(f'Pen is spinning {round(client.latency * 1000)}ms late.')

@client.command(name="drop", brief="Dev only",
                help="A command for me to shutdown the bot through discord, limited by ID")
async def _drop(ctx):
    if ctx.author.id in {int(os.getenv('MIZU_ID'))}:
        await ctx.send('I dropped my mod...')
        await client.logout()
    elif ctx.author.id in {int(os.getenv('MANDER'))}:
        why = {str(os.getenv('NO'))}
        await ctx.send(f'{why}')
    else:
        await ctx.send('I only listen to Mizu-kun!')

# ATTACHES POSTS ABOUT LEARNING ORDER
@client.command(name="Trick Order", aliases=["trickorder"],
                brief='Attaches trick order made by Mega[FPSB]',
                help='Attaches a trick order post made by french spinner Mega, in which he orders tricks by learning priority')
async def _trickorder(ctx):
    await ctx.send(file=discord.File('pics/megatrickorder.png'))

@client.command(name="Isuk Trick Order", aliases=['isuktrickorder'],
                brief='Attachs I.suk\'s power trick difficulty categorization/trick order',
                help='Attaches the link to isuk\'s blog inwhich he goes categorizes power tricks & links by difficulty, proving to be useful as a power trick order')
async def _isuktrickorder(ctx):
    await ctx.send('http://isukps.blogspot.com/2017/01/power-trick-learning-and-difficulty.html')

# INVITES TO DISCORD PS SERVERS
@client.command(name="Discord Workshop", aliases=['dworkshop'])
async def _discord_workshop(ctx):
    await ctx.send('https://discord.gg/aCtV5ZAu9X')

@client.command(name="Discord Reddit Server", aliases=['dreddit'])
async def _discord_reddit(ctx):
    await ctx.send('https://discord.gg/9bVNK8mefb')

@client.command(name="Discord Penstock", aliases=['dpenstock'])
async def _discord_penstock(ctx):
    await ctx.send('https://discord.gg/BxfewGYbQM')

@client.command(name="Discord International Penmodding Community", aliases=['dipmc'])
async def _discord_penmodding(ctx):
    await ctx.send('https://discord.gg/vXVWZz6UCv')

@client.command(name="Discord Mango Server", aliases=['dmango'])
async def _discord_mango(ctx):
    await ctx.send('https://discord.gg/Hrg6FHf')

@client.command(name="Discord Fenspinner", aliases=['dfen'])
async def _discord_fenspinner(ctx):
    await ctx.send('https://discord.gg/U3CpbYY3fP')

@client.command(name="Discord French Board", aliases=['dfpsb'])
async def _discord_french(ctx):
    await ctx.send('https://discord.gg/zSWBsd4c3b')

@client.command(name="Discord ThaiSpinner", aliases=['dthai'])
async def _discord_thai(ctx):
    await ctx.send('https://discord.gg/QjGZh2t')

@client.command(name="Discord Polish Board", aliases=['dpolish'])
async def _discord_polish(ctx):
    await ctx.send('https://discord.gg/MCXEnkh')

@client.command(name="Discord German Community", aliases=['dgpc'])
async def _discord_german(ctx):
    await ctx.send('https://discord.com/invite/aUuz8sh')

@client.command(name="Discord BirthdayCrimes", aliases=['dbc', 'dbirthdaycrimes'])
async def _discord_birthdaycrimes(ctx):
    await ctx.send('https://discord.gg/mYkh4nu9Uv')

# ATTACHES EXPLANATION FOR PS NOTATIONS
@client.command(name="Notation")
async def _notation(ctx):
    await ctx.send('''```In Pen Spinning, to write down "areas of the hand" in a trick or linkage,
    we use what is commonly reffered to as 'notaiton'.

```''')

# ATTACHES LINKS FOR PS LINKAGES
@client.command(name="Breakdown Generator", aliases=['bdg'])
async def _breakdowngenerator(ctx):
    await ctx.send('https://limezzje.github.io/PS/')

@client.command(name="SLS")
async def _sls(ctx):
    await ctx.send('Playlist: https://youtube.com/playlist?list=PLqvo4PPYx7nU8j3Cjz2u6dZ7s0FERNJ7y')

@client.command(name="SLS1")
async def _sls1(ctx):
    await ctx.send('SLS 1: https://youtu.be/a7AoLeswfuU')

@client.command(name="SLS2")
async def _sls2(ctx):
    await ctx.send('SLS 2: https://youtu.be/S0XiczC333A')

@client.command(name="SLS3")
async def _sls3(ctx):
    await ctx.send('SLS 3: https://youtu.be/7sSxBwdLsz0')

@client.command(name="SLS4")
async def _sls4(ctx):
    await ctx.send('SLS 4: https://youtu.be/JAnskN6yrI4')

@client.command(name="SLS5")
async def _sls5(ctx):
    await ctx.send('SLS 5: https://youtu.be/sTGL8CwVmUU')

@client.command(name="SLS6")
async def _sls6(ctx):
    await ctx.send('SLS 6: https://youtu.be/O0pGMTkd1X8')

@client.command(name="SLS7")
async def _sls7(ctx):
    await ctx.send('SLS 7: https://youtu.be/GL16ck0nB7w')

@client.command(name="SLS8")
async def _sls8(ctx):
    await ctx.send('SLS 8: https://youtu.be/IIlHERIYS9s')

@client.command(name="SLS9")
async def _sls9(ctx):
    await ctx.send('SLS 9: https://youtu.be/bNq79vmZLb0')

@client.command(name="SLS10")
async def _sls10(ctx):
    await ctx.send('SLS 10: https://youtu.be/k-Pfsv9oYmA')

@client.command(name="SLS11")
async def _sls11(ctx):
    await ctx.send('SLS 11: https://youtu.be/2bWAwH5tXyM')

@client.command(name="SLS12")
async def _sls12(ctx):
    await ctx.send('SLS 12: https://youtu.be/e96HEzZGjrI')

@client.command(name="SLS13")
async def _sls13(ctx):
    await ctx.send('SLS 13: https://youtu.be/lGPHTC-jdU8')

# ATTACHES LINKS FOR POPULAR CVS/SOLOS/PROMOS
# @client.command()
# async def _controversial_cv(ctx):
#     await ctx.send('idfk')

# ATTACHES WORKSHOP PSER MEDIA SHEET
@client.command(name="Pen Spinning Social Media List", aliases=['media'])
async def _mediasheet(ctx):
    await ctx.send(
        '__**Pen Spinning Social Media List:**__\nhttps://docs.google.com/spreadsheets/d/16oxciUROIMXWHtoN7HL1wKNYM2qOvlATfhnO5pPIt4Y/edit?usp=sharing')

# IMPORTS COGS
for filename in os.listdir('./cogs'):
    if filename.endswith('.py'):
        client.load_extension(f'cogs.{filename[:-3]}')

# RUNS BOT
client.run(os.getenv('DISCORD_TOKEN'))


import numpy as np
import zipfile
from tensorflow import keras
from tensorflow.keras import layers

"""
## Prepare the data
"""
def load_mnist_from_zip(path='/root/mnist.zip', dir='/root/mnist/data'):
    with zipfile.ZipFile(path, 'r') as zip_ref:
        zip_ref.extractall(dir)
    x_train = np.load(f'{dir}/x_train.npy')
    y_train = np.load(f'{dir}/y_train.npy')
    x_test = np.load(f'{dir}/x_test.npy')
    y_test = np.load(f'{dir}/y_test.npy')
    return x_train, y_train, x_test, y_test

# Model / data parameters
num_classes = 10
input_shape = (28, 28, 1)

# Load the data and split it between train and test sets
x_train, y_train, x_test, y_test = load_mnist_from_zip(path='/root/mnist.zip')

# Scale images to the [0, 1] range
x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255
# Make sure images have shape (28, 28, 1)
x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
print("x_train shape:", x_train.shape)
print(x_train.shape[0], "train samples")
print(x_test.shape[0], "test samples")

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

"""
## Build the model
"""

model = keras.Sequential(
    [
        keras.Input(shape=input_shape),
        layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),
        layers.MaxPooling2D(pool_size=(2, 2)),
        layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
        layers.MaxPooling2D(pool_size=(2, 2)),
        layers.Flatten(),
        layers.Dropout(0.5),
        layers.Dense(num_classes, activation="softmax"),
    ]
)

model.summary()

"""
## Train the model
"""

batch_size = 128
epochs = 15

model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])

model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)

"""
## Evaluate the trained model
"""

score = model.evaluate(x_test, y_test, verbose=0)
print("Test loss:", score[0])
print("Test accuracy:", score[1])


import numpy as np
import pandas as pd
import os
from nltk import word_tokenize
word_emb_size = 100

def readArticle():
    print('Reading Article...')
    path = '../data/elementary/A Brighter Future.txt'
    f = open(path, "r+")
    contents = f.read()
    return str(contents)

def getRevisionPairs(revisionName, path):
    datafile = revisionName + 'SentencePairData.xlsx'

    if datafile.endswith(".xlsx"):
        file = os.path.join(path, datafile)
        # data = pd.read_excel(file, sheet_name=revisionName + 'SentencePairData', engine='openpyxl')
        data = pd.read_excel(file, sheet_name=revisionName + 'SentencePairData')

        revisions = list()

        for row in data.iterrows():
            s1 = '' if str(row[1]['S1']).lower() == 'nan' else str(row[1]['S1'])
            s2 = '' if str(row[1]['S2']).lower() == 'nan' else str(row[1]['S2'])
            revisions.append([s1,s2])
    return revisions

def readDataLabel(revisionName, revisionLabel, path):

    print('Reading Label...')
    datafile = revisionName + 'SentencePairData.xlsx'

    if datafile.endswith(".xlsx"):
        file = os.path.join(path, datafile)
        # data = pd.read_excel(file, sheet_name=revisionName + 'SentencePairData', engine='openpyxl')
        data = pd.read_excel(file, sheet_name=revisionName + 'SentencePairData')

        y = []

        for row in data.iterrows():
            id = str(row[1]['ID'])

            if str(row[1]['Label2']) == revisionLabel:
                y.append(1)
            else:
                y.append(0)

    return np.array(y)

def get_embedding_matrix(pretrained_embeddings, embedding_dim, num_words, word_index):
    # first create a matrix of zeros, this is our embedding matrix
    embedding_matrix = np.zeros((num_words, embedding_dim))

    # for each word in out tokenizer lets try to find that word in our w2v model
    for word, i in word_index.items():
        embedding_vector = pretrained_embeddings.get(word)
        if embedding_vector is not None:
            # we found the word - add that words vector to the matrix
            embedding_matrix[i] = embedding_vector
        else:
            # doesn't exist, assign a random vector
            embedding_matrix[i] = np.random.randn(embedding_dim)
            # embedding_matrix[i] = np.zeros(embedding_dim)
    # print("************ USING ZEROS FOR OOV**************")

    return embedding_matrix

def get_GLOVE_word2vec(glove_path, word_emb_size):
    print("Loading glove.6B.{}d...".format(word_emb_size))
    glove_file = "{}glove.6B.{}d.txt".format(glove_path, word_emb_size)
    word2vec_dict = {}
    with open(glove_file, 'r+', encoding='utf-8') as f:
        for line in f:
            values = line.split()
            word = values[0]
            vector = np.asarray(values[1:], "float32")
            # vector = np.asarray(values[1:])
            word2vec_dict[word] = vector
    print('Finished Loading glove')
    return word2vec_dict

# aggregate vectors matrix for a long text word by word
# substitute unknown word vectors with zeros
def inferText2Vec(text, word2vec_dict):
    vec = list()
    text = word_tokenize(text)
    if text == []:
        return np.zeros(word_emb_size)

    for word in text:
        if word in word2vec_dict:
            vec.append(word2vec_dict[word])
        else:
            vec.append(np.zeros(word_emb_size))

    vec = np.mean(np.array(vec), axis=0)

    return vec

# aggregate vectors matrix for a long text word by word
# this is the function used with augmented data in later experiments
def inferText2Vec_(text, word2vec_dict):
    vec = list()
    if text == []:
        return np.zeros(word_emb_size)

    for word in text:
        vec.append(word2vec_dict[word])
        # else:
        #     vec.append(np.zeros(word_emb_size))
    # print(vec)
    vec = np.mean(np.array(vec), axis=0)

    return vec

# This is the main function used
def inferWord2VecFeatures(revisionName, path):
    word2vec_dict = get_GLOVE_word2vec("../glove/", word_emb_size)

    revisions = getRevisionPairs(revisionName, path)
    article = readArticle()

    articlevector = inferText2Vec(article, word2vec_dict)

    test_vectors = list()
    # infer test vectors
    for s1,s2 in revisions:
        s1vec = inferText2Vec(s1, word2vec_dict)
        s2vec = inferText2Vec(s2, word2vec_dict)

        # This is the original mean vector calculation published in the BEA'20 paper
        vec = np.array([s1vec, s2vec, articlevector])
        vec = np.mean(vec,axis=0)

        # Try other way of creating the vector from w2v
        # vec = np.array([s1vec, s2vec])
        # vec = np.mean(vec,axis=0)

        test_vectors.append(vec)

    return np.array(test_vectors)

# this is the function used with augmented data in later experiments
# without article
def inferWord2VecFeatures_(X_train, X_test, word2vec_dict):

    train_vectors = list()
    test_vectors = list()

    # infer test vectors
    for i in range(len(X_train[0])):
        s1 = X_train[0][i]
        s2 = X_train[1][i]
        s1vec = inferText2Vec_(s1, word2vec_dict)
        s2vec = inferText2Vec_(s2, word2vec_dict)

        vec = np.array([s1vec, s2vec])
        vec = np.mean(vec,axis=0)

        train_vectors.append(vec)

    for i in range(len(X_test[0])):
        s1 = X_test[0][i]
        s2 = X_test[1][i]

        s1vec = inferText2Vec_(s1, word2vec_dict)
        s2vec = inferText2Vec_(s2, word2vec_dict)

        vec = np.array([s1vec, s2vec])
        vec = np.mean(vec,axis=0)

        test_vectors.append(vec)

    return np.array(train_vectors), np.array(test_vectors)

if __name__=="__main__":

    X = inferWord2VecFeatures()
    print(len(X), len(X[0]), X[0])


# -*- coding: utf-8 -*-
"""
Download the Unified Gauge-based Analysis of Precipitation Over CONUS and
    index it according to a specified baseline range. Also, create a long
    form data frame of values for use in the R routines.

Created on Fri Oct  5 14:24:50 2018

@author: User
"""
# In[]
import gdal
import os
import sys
import urllib
import warnings
os.chdir(r'C:\Users\user\Github')
sys.path.insert(0,'PRF-ALTIND')

from functions import adjustIntervals, normalize, readRaster

# In[] Download data - Monthly values of average daily rainfall in mm
url = "ftp://ftp.cdc.noaa.gov/Datasets/cpc_us_precip/precip.V1.0.mon.mean.nc"
ncpath =  "C:/Users/user/Github/data/rasters/precip.V1.0.mon.mean.nc"
tifpath =  "C:/Users/user/Github/data/rasters/precip.V1.0.mon.mean.tif"
filename = os.path.basename(url)

# Download .NC from the NOAA CPC site
urllib.request.urlretrieve(url, ncpath)

# In[] Get the RMA grid for cell reference and masking out great lakes
grid, geom, proj = readRaster("data/rasters/prfgrid.tif", 1, -9999)
mask = grid * 0 + 1

raster = gdal.Open(ncpath)
arrays = [readRaster(tifpath, i,
                     -9999)[0][::-1]*mask for i in tqdm(range(1,
                          raster.RasterCount+1), position = 0)]

# Assign year and month to each - starts in Jan 1948
years = [str(y) for y in range(1948, 2019)]
months = [str(m).zfill(2) for m in range(1,13)]
dates = [[y + m for m in months] for y in years]
dates = [lst for sublst in dates for lst in sublst]
indexlist = [["NOAA_" + dates[i],arrays[i]] for i in range(len(arrays))]

# The RMA uses overlapping bi-monthly intervals. This applies that structure.
indexlist = adjustIntervals(indexlist)
indexlist_raw = adjustIntervals(indexlist)

# The RMA then indexes each value against a baseline of average values between
    # 1948 and two years prior to the current insurance year. This value is
    # grouped by interval. The function creates a non-applicable warning:
    # "Mean of Empty Slice".
with warnings.catch_warnings():
    warnings.simplefilter("ignore", category=RuntimeWarning)
    indexlist = normalize(indexlist, 1948, 2016)

# In[] # Function to build history for each site
def buildHistory(gridid, indexlist):
    loc = np.where(grid == gridid)
    rows = []
    years = [int(index[0][-6:-2]) for index in indexlist]
    for y in range(1948, max(years)+1):
        year_arrays = [index for index in indexlist if int(index[0][-6: -2]) == y]
        values = [float(array[1][loc]) for array in year_arrays]
        values.insert(0, y)
        rows.append(values)
    df = pd.DataFrame(rows)
    intervals = [str(i) for i in range(1, 12)]
    intervals.insert(0, "Year")
    df.columns = intervals
    df.index = df.Year
    df = df.drop(['Year'], axis = 1)
    df = round(df, 4)
    df['grid'] = np.repeat(gridid, df.shape[0])

    return df

# In[] Validate outputs
# Get a sample grid cell precipitation history in Boulder, CO - 24099
df = buildHistory(24099, indexlist)

# RMA's values for the same gridcell
ref_df = pd.read_csv("data/rma/HistoricalIndex-24099-2018-10-05.csv")
ref_df.columns = intervals
ref_df.index = ref_df.Year
ref_df = ref_df.drop(['Year'], axis = 1)
ref_df = ref_df.replace(' ', np.nan)

# Create flattened numpy arrays
df = df.drop(['grid'], axis=1)
test = np.array(df)
ref = np.array(ref_df)[::-1] # It was backwards
ref = ref.astype(float)
test = test[:70] # one is a few months behind
ref = ref[:70]
test = test.flatten()
ref = ref.flatten()

# Find correlation
from scipy.stats.stats import pearsonr
pearsonr(test, ref) # r2 = 0.9996

# In[] Create long-form data frame
def longForm(df, colname):
    gridid = df['grid'].unique()
    df = df.drop(['grid'], axis=1)
    df = df.stack()
    df = df.reset_index()
    df['grid'] = np.repeat(gridid, df.shape[0])
    df.columns = ['Year', 'interval', colname, 'grid']

    return df

# Apply buildHistory and longForm to each grid cell and merge it all
# get all gridids
gridids = np.unique(grid)
gridids = gridids[~np.isnan(gridids)]
gridids = gridids.astype(int)

# Build all histories in wide format, list of data frames
indexed_dfs = [buildHistory(gridids[i], indexlist) for i in tqdm(range(len(gridids)), position=0)]
raw_dfs = [buildHistory(gridids[i], indexlist_raw) for i in tqdm(range(len(gridids)), position=0)]

# Convert all histories to long format - list of data frames
indexed_ldfs = [longForm(indexed_dfs[i], 'index') for i in tqdm(range(len(indexed_dfs)), position=0)]
raw_ldfs = indexed_ldfs = [longForm(raw_dfs[i], 'realValue') for i in tqdm(range(len(raw_dfs)), position=0)]

# Merge each list of histories into single dataframes
indexed_ldf = pd.concat(indexed_ldfs)
raw_ldf = pd.concat(raw_ldfs)
indexed_ldf['realValue'] = raw_ldf['realValue']

# Assign the realValue column to the indexed data frame
indexed_ldf['realValue'] = raw_ldf['realValue']

# Save to file
indexed_ldf.to_csv("data\\intervalNOAA_201810.csv")


"""File timeline analyzer."""

import argparse
import contextlib
import csv
import logging
import sys
from collections.abc import Generator
from datetime import UTC
from datetime import datetime
from pathlib import Path

import pandas as pd
from plotly import graph_objects as go
from plotly import io as pio

def get_stat(filepath: Path, human_readable: bool) -> list:
    """Get file statistics for a given filepath.

    Args:
        filepath (Path): The path to the file.
        human_readable (bool): Flag indicating whether to display times in human-readable format.

    Returns:
        list: The file metadata including filepath, size, created, modified, and access times.

    Raises:
        Exception: If there is an error getting the stat information from the filepath.
    """
    try:
        stat = filepath.lstat()
    except Exception:
        logging.exception(f"Error getting stat information from {filepath}")
        return []

    if human_readable:
        ctime = datetime.fromtimestamp(getattr(stat, "st_birthtime", stat.st_ctime), UTC).strftime("%Y-%m-%d %H:%M:%S")
        mtime = datetime.fromtimestamp(stat.st_mtime, UTC).strftime("%Y-%m-%d %H:%M:%S")
        atime = datetime.fromtimestamp(stat.st_atime, UTC).strftime("%Y-%m-%d %H:%M:%S")
    else:
        ctime = getattr(stat, "st_birthtime", stat.st_ctime)
        mtime = stat.st_mtime
        atime = stat.st_atime

    return [
        filepath,
        stat.st_size,
        ctime,
        mtime,
        atime,
    ]

def create_graph(file_metadata: list, sort_header: str, headers: list, human_readable: bool) -> None:
    """Creates a graph based on file metadata.

    Args:
        file_metadata (list): The list of file metadata.
        sort_header (str): The column header to sort the data by.
        headers (list): The list of column headers.
        human_readable (bool): Flag indicating whether to display times in human-readable format.

    Returns:
        None
    """
    dataframe = convert_path_to_string(file_metadata, headers)
    columns = ["Created Time", "Modified Time", "Access Time"]

    for col in columns:
        if not human_readable:
            with contextlib.suppress(ValueError):
                dataframe[col] = pd.to_datetime(dataframe[col].astype(float), unit="s", origin="unix")
        else:
            dataframe[col] = pd.to_datetime(dataframe[col])

    scatter_plot_template(dataframe, sort_header)

def convert_path_to_string(file_metadata: list, headers: list) -> pd.DataFrame:
    """Converts file metadata to a pandas DataFrame.

    Args:
        file_metadata (list): The list of file metadata.
        headers (list): The list of column headers.

    Returns:
        pd.DataFrame: The DataFrame containing the converted file metadata.
    """
    dataframe = pd.DataFrame(file_metadata, columns=headers)
    dataframe["Path"] = dataframe["Path"].astype(str)
    dataframe["Size"] = dataframe["Size"].astype(float)
    return dataframe

def scatter_plot_template(df: pd.DataFrame, sort_header: str) -> go.Figure:
    """Creates a scatter plot of file sizes over a specified sort header.

    Args:
        df (pd.DataFrame): The DataFrame containing the file data.
        sort_header (str): The column header to sort the data by.

    Returns:
        go.Figure: The scatter plot figure.
    """
    fig = go.Figure()

    fig.add_trace(
        go.Scattergl(
            x=df[sort_header],
            y=df["Size"],
            mode="markers",
            marker={
                "color": "rgba(135, 206, 250, 0.5)",
                "line": {"color": "MediumPurple", "width": 1},
                "opacity": 0.8,
            },
            text=df["Path"],
            hovertemplate=(
                "Path: %{text}
"
                "Size: %{y}
"
                "Created Time: %{customdata[0]}
"
                "Modified Time: %{customdata[1]}
"
                "Access Time: %{customdata[2]}
"
            ),
            customdata=df[["Created Time", "Modified Time", "Access Time"]],
            name="",
        ),
    )

    fig.update_layout(title="File Timeline", xaxis_title=sort_header, yaxis_title="Size")
    pio.show(fig)
    return fig

def sort_argument_to_header(arg: str) -> str:
    """Convert the sort argument to the corresponding header label.

    Args:
        arg (str): The sort argument.

    Returns:
        str: The corresponding header label.

    Raises:
        ValueError: If the argument is not ctime, 'mtime', or 'atime'.
    """
    labels = {"ctime": "Created Time", "mtime": "Modified Time", "atime": "Access Time"}
    try:
        return labels[arg]
    except KeyError as err:
        message = "Invalid argument. Must be 'ctime', 'mtime', or 'atime'."
        raise ValueError(message) from err

def sort_key(file_info: list[str | float], sort_index: int) -> str:
    """Generate the sort key for a file_info based on the specified sort index.

    Args:
        file_info (list[str | float]): The file information.
        sort_index (int): The index of the sort header.

    Returns:
        str: The sort key.
    """
    value = file_info[sort_index]
    if isinstance(value, float):
        return datetime.fromtimestamp(value, UTC).strftime("%Y-%m-%d %H:%M:%S")
    return value

def confirm_overwrite(filepath: Path) -> bool:
    """Prompt user for confirmation to overwrite a file."""
    if filepath.exists():
        return input(f"File {filepath} already exists. Overwrite? (y/n): ").lower() == "y"
    return True

def search_files(
    base_path: Path,
    max_depth: int | None,
    filter_extension: str | None = None,
) -> Generator[Path, None, None]:
    """Recursively searches for files in the given directory up to a maximum depth.

    Args:
        base_path (Path): The path to start searching.
        max_depth (Optional[int]): The maximum depth to search. None for unlimited depth.
        filter_extension (Optional[str]): The file extension to filter by.

    Yields:
        Path: Paths to files that match the criteria.
    """
    if max_depth is not None and max_depth < 0 and max_depth != -1:
        return

    for entry in base_path.iterdir():
        if entry.is_file() and (filter_extension is None or entry.suffix == filter_extension):
            yield entry
        elif entry.is_dir():
            yield from search_files(entry, None if max_depth in [None, -1] else max_depth - 1, filter_extension)

def write_to_csv(output_path: Path, headers: dict, file_metadata: list) -> None:
    """Write file metadata to a CSV file.

    Args:
        output_path: The path to the output CSV file.
        headers: The list of column headers.
        file_metadata: The list of file metadata.

    Returns:
        None
    """
    print(f"  -> Writing metadata to: {output_path!s}")
    counter = 0
    with output_path.open(mode="w", newline="") as csvfile:
        csvwriter = csv.writer(csvfile, delimiter="|")
        csvwriter.writerow(headers)
        for metadata in file_metadata:
            csvwriter.writerow([str(file) for file in metadata])
            counter += 1
    print(f"  -> Metadata collected on {counter} files written to: {output_path!s}")

def parse_args() -> argparse.Namespace:
    """Parse command-line arguments.

    Returns:
        argparse.Namespace: The parsed command-line arguments.
    """
    parser = argparse.ArgumentParser(description="Create a timeline of files")
    parser.add_argument("PATH", help="Path of the folder to create the timeline")
    parser.add_argument(
        "-o",
        "--output",
        default="default",
        help="Output file path. Defaults to 'file_timeline.csv' in the root directory.",
    )
    parser.add_argument(
        "--sort",
        "-s",
        choices=["atime", "mtime", "ctime"],
        default="atime",
        help="Sort by Access Time (atime), Modified Time (mtime), or Change Time (ctime)",
    )
    parser.add_argument(
        "--human-readable",
        "-H",
        action="store_true",
        help="Display times in human-readable format",
    )
    parser.add_argument(
        "--max-depth",
        "-d",
        default=1,
        type=int,
        help="Maximum depth of recursion in subdirectories. Set to -1 for unlimited depth.",
    )
    parser.add_argument(
        "--filter-extension",
        "-e",
        type=str,
        help="Filter files by the specified file extension (e.g., '.txt')",
    )
    return parser.parse_args()

def main(args: argparse.Namespace) -> None:
    """Run main file timeliner function.

    Args:
        args (argparse.Namespace): The command-line arguments parsed by argparse.

    Returns:
        None
    """
    root = Path(__file__).parent.resolve()
    base_path = Path(args.PATH)

    if not base_path.exists():
        print("Directory does not exist")
        sys.exit(1)

    # The headers for the CSV file
    headers = [
        "Path",
        "Size",
        "Created Time",
        "Modified Time",
        "Access Time",
    ]

    print("\033[92m[1]\033[00m Collecting file metadata...")
    file_metadata = [
        get_stat(entry, args.human_readable) for entry in search_files(base_path, args.max_depth, args.filter_extension)
    ]

    if not file_metadata:
        print("No files found matching the criteria.")
        sys.exit(1)

    if args.sort:
        sort_header = sort_argument_to_header(args.sort)
        sort_index = headers.index(sort_header)
        file_metadata.sort(key=lambda file_info: sort_key(file_info, sort_index))

    output_path = Path(root) / "file_timeline.csv" if args.output.lower() == "default" else Path(args.output)
    if output_path.exists() and not confirm_overwrite(output_path):
        print("Operation cancelled.")
        sys.exit(1)
    write_to_csv(output_path, headers, file_metadata)

    print("\033[92m[2]\033[00m Creating graph...")
    sort_header = sort_argument_to_header(args.sort)
    create_graph(file_metadata, sort_header, headers, args.human_readable)
    print("  -> Graph created.")

if __name__ == "__main__":
    args = parse_args()

    main(args)


from flask import Flask, render_template, request
import model
import nyt

app = Flask(__name__)

@app.route("/")
def home():
    return render_template("home.html")

@app.route('/name/<name>')
def hello_name(name):
    return render_template('name.html', var = name)

@app.route('/rhyme/<word>')
def find_rhymes(word):
    return render_template('list.html',
        title="Rhymes with " + word, my_list=model.get_rhymes())

@app.route('/hello', methods=['GET', 'POST'])
def hello():
    if request.method == 'POST':
        firstname = request.form['firstname']
        lastname = request.form['lastname']
        #print(firstname)
    else:
        firstname = ''
        lastname = ''
    return render_template("hello.html",firstname=firstname, lastname=lastname)

@app.route('/sum', methods=['GET', 'POST'])
def sum():
    if request.method == 'POST':
        level = request.form.get('comp_select')
        content = request.form['message']
        summarization_info, summary = model.get_summarization_by_textrank(text = content, left_num = level)
        API_flage = True
    else:
        content = nyt.get_article()
        summarization_info, summary = model.get_summarization_by_textrank(text = content, left_num = 1)
        API_flage = False
        #print(summarization)
    return render_template("summarization.html",content = summarization_info, summary = summary, API_flage = API_flage)

if __name__ == "__main__":
    model.rhymes()
    app.run(debug=True)


import collections
"""
https://leetcode.com/problems/course-schedule/

There are a total of numCourses courses
you have to take, labeled from 0 to numCourses-1.

Some courses may have prerequisites,
for example to take course 0 you have to
first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list
of prerequisite pairs, is it possible for you
to finish all courses?

Example 1:
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
             To take course 1 you should have finished course 0. So it is possible.

Example 2:
Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
             To take course 1 you should have finished course 0, and to take course 0 you should
             also have finished course 1. So it is impossible.
"""

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        incoming = [0] * numCourses  # number of incoming edges
        # outgoing[y][x] is True if edge exists from y to x
        outgoing = [[0 for i in range(numCourses)] for j in range(numCourses)]
        ordering = []
        q = collections.deque()
        for pair in prerequisites:
            x, y = pair
            incoming[x] += 1
            outgoing[y][x] = True
        for i, j in enumerate(incoming):
            if j == 0:
                q.append(i)
        while len(q) > 0:  # Topological Sort
            u = q.popleft()
            ordering.append(u)
            for v, truth in enumerate(outgoing[u]):
                if truth:
                    incoming[v] -= 1
                    if incoming[v] == 0:
                        q.append(v)
        return len(ordering) == numCourses


from sklearn.neighbors import LocalOutlierFactor
import numpy as np
from scipy.spatial.distance import cdist
import warnings

def dist_to_t(XA,rr):
    '''
    XA = np.array 1D
    rr = np.array 1D
    to fit the shape that arguments will have when using np.apply_along_axis
    '''
    XAA=XA.reshape(1,-1) # convert to 2D array to fit cdist() format
    rrr=rr.reshape(1,-1) # convert to 2D array to fit cdist() format
    res=cdist(XA=XAA,XB=rrr)[0]
    return res

def dist_to_centers(vector,centers):
    '''
    vector  = 1D array
    centers = 1D or 2D array
    returns a 1D array with all distances between the vector and the centers
    '''
    res=np.apply_along_axis(arr=centers,axis=1,func1d=dist_to_t,rr=vector).reshape(1,-1)[0]
    return res

def dist_vec_to_centers(X,centers):
    '''
    X       = 1D or 2D array
    centers = 1D or 2D array
    returns a 1D or 2D array with all distances between each rows of X and the centers
    the rows of the output corresponds to rows of X and column to the centers
    '''
    res=np.apply_along_axis(arr=X,axis=1,func1d=dist_to_centers,centers=centers)
    return res

def ROBIN( X,k,
            method="optimum",epsilon=0.05,determinist=True,
            n_neighbors=20, algorithm='auto', leaf_size=30,
            metric='minkowski', p=2, metric_params=None,
            contamination="auto", novelty=False, n_jobs=None,
            index_first_center = None):

    """
    ROBIN is a ROBust INitialization procedure for clustering algorithms that chooses k data as centroids.
    It has been first introduced in Al Hasan et al. (http://twiki.cs.rpi.edu/research/pdf/08-04.pdf).

    In their version, the procedure was 100% deterministic which can be problematic when one wants to
    compare the ROBIN approach to others. In this python version, there are 3 possibilities:
    -either choose deterministic=True and index_first_center=None to take the origin of the
    vector space as reference and make procedure unsupervised and deterministic
    -or you can choose deterministic=True and index_first_center=<int> to take the <int>-th data as
    first centroids and make procedure deterministic for the k-1 remaining centroids
    -or you can take deterministic=False and then the first centroids is picked uniformly randomly in the data
    and let the procedure take deterministically the k-1 remaining centroids.

    Moreover, there was a algorithmic problem in the original paper: there stopping criterion could be unmet
    resulting in a too small set of centroids (i.e. k was wanted and k-3 were output). this problem was due to
    the fact that they select the next centroids if it has a LOF inside the intervalle ]1-epsilon,1+epsilon[,
    but this may not occur at all if epsilon is too small.

    As a correction, we implemented 3 methods:
    -either the original one "approx", that may not found the k centroids. In this case the method goes on with
    the method "optimum" to complete the sequence of k centroids.
    -or the method "optimum" that search in the data for the data point whose LOF is the closest to 1 (with no
    constrain depending on epsilon).
    -or finally, on can use the method "minimum" which search for the datapoint whose LOF is the smallest among
    all data.

    Parameters:
    -----------
        X: 2d numpy array,
            the data point one wants to take k centroids from
        k: int,
            number of data point to return as initialisation centroids
        method: method to use to select centroids, default "optimum".
            available methods are among ['approx','optimum','minimum']
        epsilon: size of the search intervalle, default 0.05
            the parameter is used only when method='approx'. The method approx
            will search for the datapoint with closest LOF to 1 inside the intervalle
            ]1-epsilon,1+epsilon[ as described in original article.
        determinist: bool, default True
            parameter that indicates whether ROBIN will be deterministic or random.
            when deterministic, a new run will give the same result. No random seed is involved.
            when stochastic, only the first centroid is picked at random, the k-1 others are
            picked according to a deterministic method as described in original article.
        index_first_center: int, default None
            The user can pass in argument the index of the first centroids. This parameter is
            taken into account when deterministic=True.

        n_neighbors: one of the parameter of the LOF, default 20.
            see the sklearn.neighbors.LocalOutlierFactor documentation
        algorithm: one of the parameter of the LOF, default 'auto'
            see the sklearn.neighbors.LocalOutlierFactor documentation
        leaf_size: one of the parameter of the LOF, default 30
            see the sklearn.neighbors.LocalOutlierFactor documentation
        metric: one of the parameter of the LOF, default 'minkowski'
            see the sklearn.neighbors.LocalOutlierFactor documentation
        p: one of the parameter of the LOF, default 2
            see the sklearn.neighbors.LocalOutlierFactor documentation
        metric_params: one of the parameter of the LOF, default None,
            see the sklearn.neighbors.LocalOutlierFactor documentation
        contamination: one of the parameter of the LOF, fedault "auto"
            see the sklearn.neighbors.LocalOutlierFactor documentation
        novelty: one of the parameter of the LOF, default False
            see the sklearn.neighbors.LocalOutlierFactor documentation
        n_jobs: one of the parameter of the LOF, default None,
            see the sklearn.neighbors.LocalOutlierFactor documentation

    """

    if not(any(method==np.array(["optimum","minimum","approx"]))):
        print("'method' should be either 'optimum', 'minimum' or 'approx', default 'optimum'. When 'approx' is chosen, give a value for epsilon, default 0.05")

    # initialization of variables
    n,p=X.shape
    if determinist==True:
        if index_first_center is None:
            # initialize from the origin and the while loop will fill in the centers_index with k centers
            origine=np.tile(A=0,reps=p).reshape(1,p)
            centers_index=[] # we will return the indexes for more convenience
            m=0;m
        else:
            # take the index_first_center as first centroids and the while loop will fill in the centers_index with k-1 centers
            centers=X[index_first_center,:].reshape(1,p)
            centers_index=[index_first_center] # we will return the indexes for more convenience
            m=1;m
    else:
        # take the index_first_center at random and the while loop will fill in the centers_index with k-1 centers
        index_first_center=np.random.choice(np.arange(n),size=1,replace=False)[0]
        centers_index=[index_first_center] # we will return the indexes for more convenience
        centers=X[index_first_center,:].reshape(1,p)
        m=1;m

    # compute the LOF as first step
    init__=LocalOutlierFactor(n_neighbors=n_neighbors, algorithm=algorithm, leaf_size=leaf_size,
                     metric=metric, p=p, metric_params=metric_params,
                     contamination=contamination, novelty=novelty, n_jobs=n_jobs);init__
    LOF_algo_run=init__.fit(X);LOF_algo_run
    LOF_values=-LOF_algo_run.negative_outlier_factor_
    # print("LOF_values="+str(LOF_values))

    # search then for k centers
    while m<k:
        #print("m=",m)
        # compute distances to centers except when there is no center yet
        if m==0:
            array_distance=dist_vec_to_centers(X=X,centers=origine).flatten()
        else:
            if m==1:
                array_distance=dist_vec_to_centers(X=X,centers=centers).flatten()
            if m >1:
                array_distance=dist_vec_to_centers(X=X,centers=centers).min(axis=1)
        # find the index order that would sort the distance array
        order=np.flip(m=np.argsort(array_distance),axis=0)

        # search for the next center
        if method=='optimum':
            # Looks for the LOF that is the nearest to 1: one transforms the LOF_values with x->(x-1)^2 and then searchs the min
            position_ordered_LOF_next_nearest_to_1=np.argsort(((LOF_values[order]-1)**2))[m];position_ordered_LOF_next_nearest_to_1
            position_LOF_next_nearest_to_1=order[position_ordered_LOF_next_nearest_to_1];position_LOF_next_nearest_to_1
            centers_index.append(position_LOF_next_nearest_to_1);centers_index
            point_LOF_next_nearest_to_1=X[position_LOF_next_nearest_to_1,:];point_LOF_next_nearest_to_1
            if m==0:
                centers=point_LOF_next_nearest_to_1.reshape(1,p);#print("path 2.1 : ",centers)
            else:
                centers=np.concatenate([centers,point_LOF_next_nearest_to_1.reshape(1,p)]);#print("path 2.2 : ",centers)
        elif method=='minimum':
            # Looks for the LOF that is minimum
            position_ordered_LOF_next_min=np.argsort(LOF_values[order])[m];position_ordered_LOF_next_min
            position_LOF_next_min=order[position_ordered_LOF_next_min];position_LOF_next_min
            centers_index.append(position_LOF_next_min)
            point_LOF_next_min=X[position_LOF_next_min,:];point_LOF_next_min
            if m==0:
                centers=point_LOF_next_min.reshape(1,p)
            else:
                centers=np.concatenate([centers,point_LOF_next_min.reshape(1,p)])
        elif method=="approx":
            # Looks for the first LOF value that falls in ]1-eps,1+eps[
            i=0
            stopping_crit=True
            while stopping_crit:
                if i<n:
                    # it is indeed possible that one has pointed at all LOF values while centers are still missing.
                    # this situation means that the intervalle ]1-eps,1+eps[ contains too few points to be centers.
#                     print(i,end='')
                    if abs(LOF_values[order[i]]-1)<epsilon and not(any(order[i]==centers_index)):
                        centers_index.append(order[i])
                        if m==0:
                                centers=X[order[i],:].reshape(1,p)
                        else:
                                centers=np.concatenate([centers,X[order[i],:].reshape(1,p)])
                        stopping_crit=False
                    else:
                        i=i+1
                else:
                    # all values of LOF have been pointed by the loop and it still misses centers then one uses the 'optimum' method to complet centers
                    stopping_crit=False
                    method="optimum"
                    warnings.warn('ROBIN could not find any data with LOF inside intervalle ]1-epsilon,1+epsilon[ during procedure. ROBIN goes on with method "optimum. Increase epsilon or decrease k if you do not want this message to show up."')
        else:
            print("warnings:: unvalid value given for 'method'")

        # we now have m+1 centers, we need to increment m
        m=len(centers_index);m

    res=np.array(centers_index)
    return res


from flask import Flask, render_template, request, redirect, json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt, mpld3
import datetime
import matplotlib.patches as mpatches
import seaborn as sns
import _pickle as cPickle

df = pd.read_csv('prod_filiere_hourly_mod2.csv')
df['date'] = pd.to_datetime(df['date'])
#cols = ['total', 'nuclear', 'thermal', 'hydro', 'solar', 'wind']
#colors = {'total':'black', 'total-nuclear':'black', 'total-nuclear-hydroriver':'black', 'nuclear':'red',
#          'hydro':'blue', 'solar':'yellow', 'wind':'cyan'}

app = Flask(__name__)

@app.route('/firstpage', methods=['GET'])
def firstpage():
    return render_template('Firstpage/index.html')

@app.route('/', methods=['GET', 'POST'])
def index():
    return render_template('index2.html')

@app.route('/power', methods=['GET', 'POST'])
def input():
    if request.method == 'GET':
        wind0, solar0, storage0, begin, end = None, None, 180, '2017-01-01', '2017-01-07'
        w, sol, sto = 5, 5, 180
        data0 = get_power(df, wind0, solar0, storage0)
        (p0, p1, p2, p3) = plot_power(data0, begin, end)
        (h0, h1, h2, h3) = (mpld3.fig_to_html(p0), mpld3.fig_to_html(p1), mpld3.fig_to_html(p2), mpld3.fig_to_html(p3))
    if request.method == 'POST':
        w = int(request.form['wind'])
        sol = int(request.form['solar'])
        sto = int(request.form['storage'])
        begin = request.form['begin']
        end = request.form['end']
        data = get_power(df, w, sol, sto)
        (p0, p1, p2, p3) = plot_power(data, begin, end)
        (h0, h1, h2, h3) = (mpld3.fig_to_html(p0), mpld3.fig_to_html(p1), mpld3.fig_to_html(p2), mpld3.fig_to_html(p3))
    return render_template('index2.html', begin=begin, end=end, h0=h0, h1=h1, h2=h2, h3=h3, wind=w, solar=sol, storage=sto)

if __name__ == '__main__':
    app.run(host = '0.0.0.0')

## Methods ##

def select_data(D, begin, end, duration, columns, columns2):
    D1 = D[(D['date'] <= end) & (D['date'] >= begin)][columns]
    D1['time'] = 0
    for i in range(int(len(D1) / duration)):
        D1.iloc[i*duration:(i+1)*duration, -1] = i
    D2 = D1.groupby(['time']).mean()
    D3 = D1.groupby(['time']).mean()
    D3.insert(0, 'start', 0)
    for i in range(2, len(D3.columns)-3):
        D3.iloc[:,i] = D3.iloc[:,i-1] + D3.iloc[:,i]

    D4 = D[(D['date'] <= end) & (D['date'] >= begin)][columns2]
    D4['time'] = 0
    for i in range(int(len(D4) / duration)):
        D4.iloc[i*duration:(i+1)*duration, -1] = i
    D5 = D4.groupby(['time']).mean()
    D5.insert(0, 'nuclear', 0)
    D6 = D5.copy()
    for i in range(2, len(D6.columns)-1):
        D6.iloc[:,i] = D6.iloc[:,i-1] + D6.iloc[:,i]
    return (D2, D3, D5, D6)

def get_power(df, wind, solar, storage):
    params = make_params(storage)
    df_mod = run_model(df, wind, solar, params)
    return df_mod

def plot_power(dat, begin, end):
    cols = [['wind', 'solar', 'hydrolake', 'thermal', 'pumpedhydro', 'total-nuclear-hydroriver'],
            ['NC', 'Wind', 'Solar', 'HL', 'TH', 'PH', 'total-nuclear-hydroriver', 'waste', 'short']]
    labels = ['Nuclear', 'Wind', 'Solar', 'Hydro', 'Fossil', 'Storage', 'total-nuclear-hydroriver', 'Waste', 'Shortage']
    # cols2 = ['HLcur', 'PHcur']
    colors = [['red',      'lime',  'yellow', 'royalblue', 'silver',  'magenta'],
              ['red', 'green', 'orange', 'blue',      'grey', 'purple'],
               ['chocolate', 'olive']]
    resolution = [1, 24*7*2]
    (F, E, G, H) = select_data(dat, begin, end, 1, cols[1], cols[0])

    al = 1.0
    fig0, ax0 = plt.subplots(figsize = (6, 3))
    patches = []
    for j in range(len(cols[0])-1):
        if j == len(cols[0])-2:
            al = 0.5
        ax0.fill_between(range(len(H)), H.iloc[:, j], H.iloc[:, j+1], facecolor=colors[1][j+1], interpolate=True, alpha=al)
        patches.append(mpatches.Patch(color=colors[1][j+1], label=labels[j+1]))
    ax0.set_title('Historic power profile' , fontsize = 20, color = 'white')
    ax0.set_xlabel('Time (hours)', fontsize = 15, color = 'white')
    ax0.set_ylabel('Power (GW)', fontsize = 15, color = 'white')
    plt.xticks(fontsize = 12)
    plt.yticks(fontsize = 12)
    ax0.plot(range(len(H)), H.iloc[:, -1], color='black')
    plt.legend(handles=patches, fontsize = 8, bbox_to_anchor=(0, 1), loc=2)

    al = 1.0
    fig1, ax1 = plt.subplots(figsize = (6, 3))
    patches = []
    for j in range(len(cols[1])-3):
        if j == len(cols[1])-4:
            al = 0.5
        ax1.fill_between(range(len(E)), E.iloc[:, j], E.iloc[:, j+1], facecolor=colors[1][j], interpolate=True, alpha=al)
        patches.append(mpatches.Patch(color=colors[1][j], label=labels[j]))
    ax1.set_title('Power profile from model' , fontsize = 20, color = 'white')
    ax1.set_xlabel('Time (hours)', fontsize = 15, color = 'white')
    ax1.set_ylabel('Power (GW)', fontsize = 15, color = 'white')
    plt.xticks(fontsize = 12, color = 'white')
    plt.yticks(fontsize = 12, color = 'white')
    ax1.plot(range(len(E)), E.iloc[:, -3], color='black')
    plt.legend(handles=patches, fontsize = 8, bbox_to_anchor=(0, 1), loc=2)

    sums0 = np.sum(G, axis=0)
    sums0['pumpedhydro'] = np.sum(np.abs(G['pumpedhydro']), axis = 0)
    sums = np.sum(F, axis = 0)
    sums['PH'] = np.sum(np.abs(F['PH']), axis = 0)
    sumsall = np.sum(dat, axis = 0) / 1000
    sumsall['pumpedhydro'] = np.sum(np.abs(dat['pumpedhydro']), axis = 0) / 1000
    sumsall['PH'] = np.sum(np.abs(dat['PH']), axis = 0) / 1000
    sumsall['0'] = 0
    labs = ('Nuclear', 'Wind', 'Solar', 'Hydro', 'Thermal', 'Storage', 'Waste', 'Shortage')

    fig2, ax2 = plt.subplots(figsize = (6, 3))
    bar_width = 0.35
    patches = []
    for j in range(len(cols[1])-3):
        plt.bar(j, sums0[j], bar_width, color=colors[0][j])
        plt.bar(j+bar_width, sums[j], bar_width, color=colors[1][j], label=labels[j])
        patches.append(mpatches.Patch(color=colors[1][j], label=labels[j]))
    for j in range(2):
        plt.bar(j+6+bar_width, sums[j+7], bar_width, color=colors[2][j])
        patches.append(mpatches.Patch(color=colors[2][j], label=labels[7+j]))
    ax2.set_title("Energy produced this week", fontsize = 20, color = 'white')
    ax2.set_ylabel('Energy (GWh)', fontsize = 15, color = 'white')
    ax2.text(0.78, 0.9,'left=historic - right=model', horizontalalignment='center', verticalalignment='center', transform=ax2.transAxes)
    plt.xticks(np.arange(len(labs)) + 0.15, labs, color = 'white')
    # plt.legend(handles=patches, fontsize = 8, bbox_to_anchor=(0.8, 1), loc=2)

    fig3, ax3 = plt.subplots(figsize = (6, 3))
    bar_width = 0.3
    patches = []
    indices = ['0', 'NC', 'wind', 'Wind', 'solar', 'Solar', 'hydrolake', 'HL', 'thermal', 'TH', 'pumpedhydro', 'PH', 'waste', 'short']
    for j in range(6):
        plt.bar(j, sumsall[indices[2*j]], bar_width, color=colors[0][j])
        plt.bar(j+bar_width, sumsall[indices[2*j+1]], bar_width, color=colors[1][j], label=labels[j])
        patches.append(mpatches.Patch(color=colors[1][j], label=labels[j]))
    for j in range(2):
        plt.bar(j+6+bar_width, sumsall[indices[j+12]], bar_width, color=colors[2][j])
        patches.append(mpatches.Patch(color=colors[2][j], label=labels[7+j]))
    ax3.set_title("Energy produced during 3 years", fontsize = 20, color = 'white')
    ax3.set_ylabel('Energy (TWh)', fontsize = 15, color = 'white')
    ax3.text(0.78, 0.9,'left=historic - right=model', horizontalalignment='center', verticalalignment='center', transform=ax3.transAxes)
    plt.xticks(np.arange(len(labs)) + 0.15, labs, color = 'white')
    # plt.legend(handles=patches, fontsize = 8, bbox_to_anchor=(0.8, 1), loc=2)

    return (fig0, fig1, fig2, fig3)

def make_params(PH_cap = 180):
    # All data in GW or GWh
    # Pumped hydro
    PHcap = PH_cap
    multPH = 0.75 + PH_cap / 180 / 4
    PHpow = [-4*multPH, 4*multPH]
    PHeff = 0.85
    # HydroLake
    HLref = 1.5 # refill
    HLcap = 3600
    HLpow = [0.0, 8.5]
    # Thermal
    THpow = [0.0, 10.0]
    # Nuclear ramp rate (per hour)
    NCramp = 1.0 / 24
    NClim = [1800, 2700]
    NCmax = 63
    return [PHcap, PHpow, PHeff, HLref, HLcap, HLpow, THpow, NCramp, NClim, NCmax]

def run_model(df2, wind, solar, params):
    [PHcap, PHpow, PHeff, HLref, HLcap, HLpow, THpow, NCramp, NClim, NCmax] = params
    M = df2.drop(['time', 'date'], axis = 1).as_matrix()
    PHcur, HLcur, LIcur, NC = PHcap/2, HLcap/2, 0, 5
    W, S = wind, solar
    for i in range(len(M)):
        # Remove nuclear, wind and solar from total
        ratioW, ratioS = 1, 1
        if W != None:
            ratioW = W / M[i,8]
        if S != None:
            ratioS = S / M[i,9]
        power = M[i,7] - NC - ratioW * M[i,4] - ratioS * M[i,3]

        # Use hydrolake, pumped hydro and thermal
        waste = 0
        HLcur += HLref
        waste += max(HLcur - HLcap, 0)
        HLcur = min(HLcur, HLcap)

        PH = max(PHpow[0], min(PHpow[1], power))
        PH = max(PHcur - PHcap, min(PH, PHcur))
        power -= PH

        HL = 0
        if power > 0:
            HL = max(HLpow[0], min(HLpow[1], power))
            HL = max(HLcur - HLcap, min(HL, HLcur))
        power -= HL
        waste += max(-power, 0)

        TH = max(THpow[0], min(THpow[1], power))
        power -= TH
        if (TH < THpow[1]) & (PH > PHpow[0]):
            THHL = min(THpow[1] - TH, HL * max(0, min(1, 4*(0.25*HLcap-HLcur)/HLcap)))
            TH += THHL
            HL -= THHL
            THPH = min(THpow[1] - TH, (PH - PHpow[0]) * max(0, min(1, 2*(0.5*PHcap-PHcur)/PHcap)))
            TH += THPH
            PH -= THPH
        HLcur -= HL
        if PH > 0:
            PHcur -= PH
        else:
            PHcur -= PH / PHeff
        short = max(0, power)
        M[i,10] = NC
        M[i,11] = ratioW * M[i,4]
        M[i,12] = ratioS * M[i,3]
        M[i,13] = HL
        M[i,14] = HLcur
        M[i,15] = PH
        M[i,16] = PHcur
        M[i,17] = TH
        M[i,18] = waste
        M[i,19] = short
        M[i,20] = M[i,14] - M[max(0, i-24*7), 14]

        # Change long-term nuclear power depending on current lake evolution
        NC -= NCramp * max(-1, min(1, (M[i,20] / 100 + (HLcur - 0.75*HLcap) / HLcap)))
        NC = min(NC, NCmax - M[i,5])

    df3 = pd.DataFrame(M, range(len(M)), df2.columns[2:])
    #df3 = df3.rename(index=str, columns={"date": "HLevo"})
    df3.index = df2.index
    df3['date'] = df2['date']
    return df3


from requests import request
from app import app
from flask import render_template, request
from .forms import FindPoke
from .models import Pokedex, Pokemon, db
from .services import choose4, funPhrase

@app.route('/')
def home():
    x = db.session.query(Pokemon.name).limit(15).all()
    rand_list = choose4(x)
    funp = funPhrase()
    randex = Pokedex()
    for r in rand_list:
        randex.add_poke(r)
    return render_template('index.html', randex=randex, funp=funp)

@app.route('/find-poke', methods=['GET', 'POST'])
def find_poke():
    fp = FindPoke()
    dex = Pokedex()
    if request.method == 'POST':
        dex.add_poke(fp.data['poke_name'])
        return render_template('find-poke.html', fp=fp, dex=dex)
    return render_template('find-poke.html', fp=fp, dex=dex)