import tensorflow as tf
import numpy as np
from tensorflow.keras.layers import Dense,Dropout,BatchNormalization,Flatten,MultiHeadAttention

##### transformer layer definitions #####

dnn = 64
drop_rate = 0.2

feedforward = tf.keras.Sequential([
	Dense(dnn,activation='relu'),
	Dense(dnn,activation='relu'),

	],name="feedforward")

class gt_model(tf.keras.Model):
	def train_step(self,data):
		x,y = data

		with tf.GradientTape() as tape:
			y_pred = self(x,training=True)# preform predictions
			loss = self.compiled_loss(y,y_pred,regularization_losses=self.losses) # compute difference between true and predicted values

		trainable_vars = self.trainable_variables # establish all trainable variables
		gradients = tape.gradient(loss,trainable_vars) # preform autograd to calculate adjusted new state of variables

		self.optimizer.apply_gradients(zip(gradients,trainable_vars)) # apply change calculated
		self.compiled_metrics.update_state(y,y_pred) # calculate new metrics and update
		return {m.name: m.result() for m in self.metrics}

xp = np.load("p_multi.npy")
y = np.load("labels.npy")

inputs = tf.keras.Input(shape=(xp.shape[1:]))
ff = feedforward(inputs)
flat = Flatten()(ff)

outputs = tf.keras.layers.Dense(1,activation='softmax')(flat)

model = gt_model(inputs, outputs)
model.compile(optimizer="nadam", loss="BinaryFocalCrossentropy", metrics=["accuracy"])
model.summary()

model.fit(xp, y, epochs=3)


import pipes
import subprocess
from pathlib import Path
from typing import NamedTuple

import pytest

from ..conftest import TESTS_PATH

EXTENSIONS = {
    "python": "py",
    "go": "go",
    "javascript": "js",
}

def sloc(path, extension=None):
    """Quick hack in lieu of installing sloccount"""
    if extension:
        extension_pat = f"-name '*.{extension}'"
    else:
        extension_pat = ""
    t = pipes.Template()
    t.prepend(f"find {path} {extension_pat}", ".-")
    t.append("xargs cat", "--")
    t.append("wc -l", "--")
    f = t.open("pipefile", "r")
    return int(f.read())

def test_sloc():
    assert sloc(path=".", extension="py") > 0

class RepoCase(NamedTuple):
    url: str
    sha: str
    language: str
    # used to decouple test runs when we change what the
    # test is actually doing for benchmarking
    version: str = "1"

    @property
    def test_id(self):
        repo_name = self.url.split("/")[-1]
        return f"{repo_name}-v{self.version}"

@pytest.mark.parametrize(
    "repo_case",
    [
        # SHAs of May 6, 2020
        RepoCase(
            "https://github.com/coinbase/node-process-lock", "dd687bc", "javascript"
        ),
        RepoCase("https://github.com/getsentry/sentry", "f25ea5dc", "python"),
        RepoCase(
            "https://github.com/bkimminich/juice-shop.git", "e03629b", "javascript"
        ),
    ],
    ids=lambda case: case.test_id,
)
def test_default_packs(run_semgrep_in_tmp, clone_github_repo, benchmark, repo_case):
    """
    This test _intentionally_ depends on the composition of the packs. If we add a slow check to a pack, we want to know
    about it!
    """
    repo_path = clone_github_repo(repo_url=repo_case.url, sha=repo_case.sha)
    repo_ksloc = sloc(str(repo_path), extension=EXTENSIONS[repo_case.language]) / 1000

    # In general, aim for 1ksloc / rule. The packs are "special" though -- composed of rules we know will run
    # fast and give great UX
    DEFAULT_PACK_MIN_SPEED_KSLOCS = 0.1
    timeout = max(repo_ksloc / DEFAULT_PACK_MIN_SPEED_KSLOCS, 20)
    print(
        f"checking with timeout of {timeout} (required analysis speed of {repo_ksloc / timeout} kslocs"
    )

    def run_benchmark():
        try:
            subprocess.check_output(
                [
                    "python3",
                    "-m",
                    "semgrep",
                    "--jobs",
                    "1",
                    "--config",
                    f"https://semgrep.dev/c/p/{repo_case.language}",
                    str(repo_path),
                ],
                timeout=timeout,
            )
        except subprocess.TimeoutExpired:
            # Avoid a giant and useless pytest stack trace
            pytest.fail(f"check-pack benchmark timed out (running {repo_case})")

    benchmark(run_benchmark)

@pytest.mark.parametrize(
    "repo_case",
    [
        RepoCase(
            "https://github.com/draios/sysdig-inspect", "63f17b9", language="javascript"
        ),
        RepoCase("https://github.com/getsentry/sentry", "f25ea5dc", language="python"),
        RepoCase(
            "https://github.com/signalapp/signal-webrtc-ios",
            "6edf8c3",
            language="python",
        ),
        RepoCase("https://github.com/secdev/scapy", "41862d7", language="python"),
        RepoCase(
            "https://github.com/coinbase/rosetta-sdk-go", "26ae7bd", language="go"
        ),
        # NOTE: this repo is mostly typescript
        RepoCase(
            "https://github.com/coinbase/rest-hooks", "d5cf9c7", language="javascript",
        )
        # "https://github.com/getsentry/sentry-python",
        # "https://github.com/apache/airflow",
        # "https://github.com/preset-io/elasticsearch-dbapi",
        # "https://github.com/apache/libcloud",
        # "https://github.com/keybase/pykeybasebot",
        # "https://gitbox.apache.org/repos/asf/cassandra",
        # "https://github.com/coinbase/coinbase-commerce-python",
        # "https://github.com/keybase/python-triplesec",
        # "https://github.com/psycopg/psycopg2",
        # "https://github.com/preset-io/flask-jwt-extended",
        # "https://github.com/vstinner/pyperf",
        # "https://github.com/mysql/mysql-connector-python",
        # "https://github.com/Netflix/lemur",
    ],
    ids=lambda case: case.test_id,
)
@pytest.mark.parametrize(
    "language", ["python", "java", "go", "javascript"], ids=lambda lang: lang
)
def test_trivial_pattern(
    run_semgrep_in_tmp, clone_github_repo, benchmark, repo_case, language
):
    """
    This test runs a trivial pattern over the repo, essentially tracking our performance of parsing the language code.
    We run every repo for all the primary languages. This allows us to also track off-language results (how fast do
    we detect that we don't need to scan this repo)
    """
    repo_path = clone_github_repo(repo_url=repo_case.url, sha=repo_case.sha)
    print(repo_path)
    benchmark(
        subprocess.check_output,
        [
            "python3",
            "-m",
            "semgrep",
            "--jobs",
            "1",
            "--pattern",
            "$X = 156128578192758",
            "--lang",
            language,
            repo_path,
        ],
    )

class PerformanceBug(NamedTuple):
    rule: Path
    file: Path

@pytest.mark.parametrize(
    "perf_bug",
    [
        PerformanceBug(
            rule=Path("rules/request-data-write.yaml"),
            file=Path("targets/definitions.py"),
        )
    ],
    ids=lambda lang: lang,
)
def test_performance_bugs(benchmark, perf_bug):
    """
    Benchmark for file/rule combinations that have been historically slow to watch for regressions.
    """
    benchmark.extra_info = {"ksloc": sloc(TESTS_PATH / "performance" / perf_bug.file)}
    benchmark(
        subprocess.check_output,
        [
            "python3",
            "-m",
            "semgrep",
            "--jobs",
            "1",
            "--config",
            TESTS_PATH / "performance" / perf_bug.rule,
            TESTS_PATH / "performance" / perf_bug.file,
        ],
    )

def test_version(benchmark):
    benchmark(subprocess.check_output, ["python3", "-m", "semgrep", "--version"])


import SimpleITK as sitk
import numpy as np

def rotation_angles_from_matrix(matrix, order):
    """
    input
        matrix = 3x3 rotation matrix (numpy array)
        order(str) = rotation order of x, y, z : e.g, rotation XZY -- 'xzy'
    output
        theta1, theta2, theta3 = rotation angles in rotation order
    """
    r11, r12, r13 = matrix[0]
    r21, r22, r23 = matrix[1]
    r31, r32, r33 = matrix[2]
    theta1_ = []
    theta2_ = []
    theta3_ = []

    if order == 'xzx':
        theta1_ = np.arctan(r31 / r21)
        theta2_ = np.arctan(r21 / (r11 * np.cos(theta1_)))
        theta3_ = np.arctan(-r13 / r12)

    elif order == 'xyx':
        theta1_ = np.arctan(-r21 / r31)
        theta2_ = np.arctan(-r31 / (r11 * np.cos(theta1_)))
        theta3_ = np.arctan(r12 / r13)

    elif order == 'yxy':
        theta1_ = np.arctan(r12 / r32)
        theta2_ = np.arctan(r32 / (r22 * np.cos(theta1_)))
        theta3_ = np.arctan(-r21 / r23)

    elif order == 'yzy':
        theta1_ = np.arctan(-r32 / r12)
        theta2_ = np.arctan(-r12 / (r22 * np.cos(theta1_)))
        theta3_ = np.arctan(r23 / r21)

    elif order == 'zyz':
        theta1_ = np.arctan(r23 / r13)
        theta2_ = np.arctan(r13 / (r33 * np.cos(theta1_)))
        theta3_ = np.arctan(-r32 / r31)

    elif order == 'zxz':
        theta1_ = np.arctan(-r13 / r23)
        theta2_ = np.arctan(-r23 / (r33 * np.cos(theta1_)))
        theta3_ = np.arctan(r31 / r32)

    elif order == 'xzy':
        theta1_ = np.arctan(r32 / r22)
        theta2_ = np.arctan(-r12 * np.cos(theta1_) / r22)
        theta3_ = np.arctan(r13 / r11)

    elif order == 'xyz':
        theta1_ = np.arctan(-r23 / r33)
        theta2_ = np.arctan(r13 * np.cos(theta1_) / r33)
        theta3_ = np.arctan(-r12 / r11)

    elif order == 'yxz':
        theta1_ = np.arctan(r13 / r33)
        theta2_ = np.arctan(-r23 * np.cos(theta1_) / r33)
        theta3_ = np.arctan(r21 / r22)

    elif order == 'yzx':
        theta1_ = np.arctan(-r31 / r11)
        theta2_ = np.arctan(r21 * np.cos(theta1_) / r11)
        theta3_ = np.arctan(-r23 / r22)

    elif order == 'zyx':
        theta1_ = np.arctan(r21 / r11)
        theta2_ = np.arctan(-r31 * np.cos(theta1_) / r11)
        theta3_ = np.arctan(r32 / r33)

    elif order == 'zxy':
        theta1_ = np.arctan(-r12 / r22)
        theta2_ = np.arctan(r32 * np.cos(theta1_) / r22)
        theta3_ = np.arctan(-r31 / r33)

    # theta1_ = theta1_ * 180 / np.pi
    # theta2_ = theta2_ * 180 / np.pi
    # theta3_ = theta3_ * 180 / np.pi

    return theta1_, theta2_, theta3_

# test_transform, original
rotTransl_matrix = np.array([[ 0.79327354,  0.01318472, -0.60872264, 1130],
                             [-0.02211240, -0.99848211, -0.05044315,  453],
                             [-0.60846375,  0.05347554, -0.79177789,  764],
                             [ 0.00000000,  0.00000000,  0.00000000,    1]])

Test_trans_4_4 = rotTransl_matrix
mask_path = '/home/biomech/Downloads/'

img_mask = sitk.ReadImage(mask_path+'BoneTest.mhd')

img_mask_np = np.transpose(sitk.GetArrayFromImage(img_mask), [2, 1, 0])

greypath = '/home/biomech/Documents/01_Icotec/01_Experiments/02_Scans/Pilot3/04_Registered/'

img_grey = sitk.ReadImage(greypath + 'XCT_Icotec_S130672_L5_intact_planned.mhd')
img_grey.SetOrigin([0, 0, 0])

[theta1, theta2, theta3] = rotation_angles_from_matrix(Test_trans_4_4[:3, :3], 'zyx')
print(theta1)

Center = np.array([img_mask_np.shape[0]/2, img_mask_np.shape[1]/2, 0]) * img_mask.GetSpacing()
print(Center)
angle = [theta1, theta2, theta3]

trans = Test_trans_4_4[:3, 3]*img_mask.GetSpacing()-Center

print(angle)

f = open(mask_path + 'Test_transformation.tfm', "w")
f.write(
    "#Insight Transform File V1.0\n"
    "#Transform 0\n"
    "Transform: CompositeTransform_double_3_3\n"
    "#Transform 1\n"
    "Transform: Euler3DTransform_double_3_3\n"
    "Parameters:  " + f'{theta3}' + " " + f'{theta2-np.pi}' + " " + f'{theta1}'
    + " " + f'{trans[0]}' + " " + f'{trans[1]}' + " " + f'{trans[2]}' + "\n"
    "FixedParameters: " + f'{Center[0]}' + " " + f'{Center[1]}' + " " + f'{Center[2]}' + " 0\n")  # CoR
f.close()

transform = sitk.ReadTransform(mask_path + 'Test_transformation.tfm')
transform_inv = transform.GetInverse()
print(transform)
print(transform_inv)

img_mask_trans = sitk.Resample(img_mask, img_grey, transform_inv, sitk.sitkNearestNeighbor, 0.0, img_grey.GetPixelID())
sitk.WriteImage(img_mask_trans, mask_path + 'Test_mask_trans.mhd')
sitk.WriteImage(img_grey, mask_path + 'test_CT.mhd')

print("\n<<<<<<<<<<\nFinished.\n<<<<<<<<<<\nSome text to be visible.\n<<<<<<<<<<")


# by Josenalde Oliveira
import unicodedata
s = input('string a comparar: ')
s = s.lower()  # passa para minusculo (retorna string)

s = ''.join(ch for ch in unicodedata.normalize('NFKD', s)
            if not unicodedata.combining(ch))

v = ['a', 'e', 'i', 'o', 'u']
c_v = 0
for l in v:
    c_v += s.count(l)

print(s + ' with: ' + str(c_v) + ' vowels')


"""first

Revision ID: 8223465e4710
Revises:
Create Date: 2022-11-19 22:40:39.505434

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '8223465e4710'
down_revision = None
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('CATEGORY',
    sa.Column('Id', sa.Integer(), nullable=False),
    sa.Column('Name', sa.String(length=255), nullable=True),
    sa.PrimaryKeyConstraint('Id')
    )
    op.create_table('CITY',
    sa.Column('Id', sa.Integer(), nullable=False),
    sa.Column('Name', sa.String(length=255), nullable=True),
    sa.PrimaryKeyConstraint('Id')
    )
    op.create_table('USER',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('UserName', sa.String(length=20), nullable=False),
    sa.Column('Password', sa.String(length=80), nullable=False),
    sa.Column('Name', sa.String(length=255), nullable=True),
    sa.Column('PhoneNumber', sa.String(), nullable=True),
    sa.Column('Address', sa.String(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('entry',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('title', sa.String(length=64), nullable=False),
    sa.Column('description', sa.String(length=120), nullable=False),
    sa.Column('status', sa.Boolean(), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_entry_description'), 'entry', ['description'], unique=False)
    op.create_index(op.f('ix_entry_title'), 'entry', ['title'], unique=False)
    op.create_table('PRODUCT',
    sa.Column('Id', sa.Integer(), nullable=False),
    sa.Column('Name', sa.String(length=255), nullable=True),
    sa.Column('Description', sa.String(), nullable=True),
    sa.Column('ImageUrl', sa.String(), nullable=True),
    sa.Column('Status', sa.Integer(), nullable=True),
    sa.Column('Surcharge', sa.Integer(), nullable=True),
    sa.Column('Receiver', sa.Integer(), nullable=True),
    sa.Column('UserId', sa.Integer(), nullable=True),
    sa.Column('CategoryId', sa.Integer(), nullable=True),
    sa.Column('CityId', sa.Integer(), nullable=True),
    sa.ForeignKeyConstraint(['CategoryId'], ['CATEGORY.Id'], ),
    sa.ForeignKeyConstraint(['CityId'], ['CITY.Id'], ),
    sa.ForeignKeyConstraint(['UserId'], ['USER.id'], ),
    sa.PrimaryKeyConstraint('Id')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('PRODUCT')
    op.drop_index(op.f('ix_entry_title'), table_name='entry')
    op.drop_index(op.f('ix_entry_description'), table_name='entry')
    op.drop_table('entry')
    op.drop_table('USER')
    op.drop_table('CITY')
    op.drop_table('CATEGORY')
    # ### end Alembic commands ###


from boggle import Boggle
from flask import Flask, request, render_template, redirect, flash, session, jsonify
from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)

app.config["SECRET_KEY"] = "shhhItsAsecret"
app.config["DEBUG_TB_INTERCEPT_REDIRECTS"] = False
debug = DebugToolbarExtension(app)

boggle_game = Boggle()

@app.route("/")
def home():
    """Home page with game instructions"""
    return render_template("start.html")

@app.route("/play")
def start_game():
    """generates boggle game board and adds it to session"""
    board = boggle_game.make_board()
    session["board"] = board
    if session.get("plays") is None:
        session["plays"] = 0
        session["high_score"] = 0

    plays = session["plays"]
    high_score = session["high_score"]
    return render_template("play.html", board=board, plays=plays, high_score=high_score)

@app.route("/check")
def check_word():
    """Checks if a word is valid and on the board. Sends the results to front end """
    word = request.args["guess"]
    board = session["board"]
    res = boggle_game.check_valid_word(board, word)
    return jsonify({"result": res})

@app.route("/score", methods=["POST"])
def set_score():
    """Checks if current score is greater than the high score in session. Adds 1 to games played and returns message with high score to front end"""
    plays = session["plays"]
    high_score = session["high_score"]
    score = request.json["score"]
    session["plays"] = plays + 1
    if score > high_score:
        session["high_score"] = score
        return jsonify({"msg": "New High Score!", "high": session["high_score"]})
    return jsonify({"msg": "Final Score:", "high": session["high_score"]})


from numpy import zeros
from sympy import factorial,diff,Symbol,cos, sin, tan, log, ln, exp, cot, sec, csc, asin, acos, atan,sqrt
from FormulaEngine import convertir_funcion
from Utilidades import tablita, limpiar
x=Symbol("x")

def imprimir(Matriz,nivel):
    tabla=tablita(["Xi","Yi"],show_iteracion=False)
    for xn in range(nivel):
        columna=[]
        for fx in range(2):
            columna.append(Matriz[xn,fx])
        tabla.add_fila(columna)
    tabla.print_table()

def f(xi, yi, funcion):
    valor = 0
    if "x" in funcion:
        funcion = funcion.replace("x", str(xi))
    if "y" in funcion:
        funcion = funcion.replace("y", str(yi))
    valor = eval(funcion)
    return valor

def calcularDerivadas(dy,tamanio):
    derivadas=[]
    for i in range(tamanio-1):
        if "y" in dy:
            dy=dy.replace("y", "f(x)")
        d= diff(dy)
        d=str(d)
        if "Derivative(f(x), x)" in d:
            dy=dy.replace("f(x)", "y")
            d=d.replace("Derivative(f(x), x)",dy)
        if "f(x)" in d:
            dy=dy.replace("f(x)", "y")
            d=d.replace("f(x)",dy)
        derivadas.append(str(d))
    return derivadas

def taylor():
    print("Metodo de Taylor")
    dy=input("Ingrese el valor de y' ")
    dy=convertir_funcion(dy)
    x0 = float(input("Ingrese el valor de x0 "))
    y0 = float(input("Ingrese el valor de y0 "))
    h = float(input("Ingrese el valor de H "))
    orden = int(input("Ingrese el numero de orden que desea "))
    fx=float(input("Ingrese el valor f(x) que desea evaluar "))
    limpiar()
    derivadas=[]
    derivadas=calcularDerivadas(dy, orden)
    derivadas.insert(0, dy)
    tamanio = orden + 1
    i=x0
    a=0
    while i <= fx:
        a = a+1
        i += h
    estimado = zeros(shape=(a+1,2))
    estimado[0] = [x0,y0]
    x = x0
    y = y0
    muestras=1
    for i in range(1,a,1):
        y0=y
        for j in range(1,tamanio,1):
            if j>0:
                d2y= derivadas[j-1]
                y= y + f(x,y0,d2y)*(h**j/factorial(j))
                if i==1:
                    estimado[i]= [f(x,y,dy),f(x,y,dy)]
            else:
                y=y
        muestras+=1
        x = x+h
        estimado[i] = [x,y]
    imprimir(estimado, muestras)


import os
import sys
#sys.path.insert(0, os.path.abspath('/home/jrodrigues/Documents/PhD/fulmar'))

from astropy.table import Table
from astropy.time import Time
from astropy.timeseries import TimeSeries
import astropy.units as u

from fulmar.func import (
    mission_identifier,
    target_identifier,
    read_lc_from_file,
    normalize_lc,
    time_flux_err,
    ts_binner,
    fbn
)
from fulmar.time import TimeRJD
from fulmar.utils import FulmarWarning
import lightkurve as lk
import numpy as np
import numpy.testing as npt

import pytest

def test_mission_identifier():
    """Test the mission_identifier function""
    """
    # Start by checking if ValueError is raised for a bad target
    with pytest.raises(ValueError):
        mission_identifier('Hello')

    # Check for the Kepler mission
    assert mission_identifier('Kepler-10') == 'Kepler'
    assert mission_identifier('KIC11904151') == 'Kepler'

    # Check for K2
    assert mission_identifier('K2-109') == 'K2'
    assert mission_identifier('EPIC201437844') == 'K2'

    # Check for TESS
    assert mission_identifier('TOI-175') == 'TESS'
    assert mission_identifier('TIC307210830') == 'TESS'

def test_target_identifier():
    """Test the target_identifier function
    """
    # Start by checking if ValueError is raised for a bad target
    with pytest.raises(ValueError):
        target_identifier('World')

    # target is int
    # Check if the correct message is passed for Kepler
    with pytest.raises(ValueError, match='range 1 to 13161029'):
        target_identifier(0, mission='Kepler')
        target_identifier(123456789, mission='Kepler')

    # Check if the correct message is passed for K2
    with pytest.raises(ValueError, match='range 201000001 to 251813738'):
        target_identifier(0, mission='K2')
        target_identifier(260000000, mission='Kepler')

    # Check if the correct message is passed for no mission
    with pytest.raises(ValueError, match='supported mission'):
        target_identifier(42, mission='H2G2')

    # Check for the No prefix warning
    with pytest.warns(FulmarWarning, match='target is assumed'):
        target_identifier(11904151, mission='Kepler')
        target_identifier(201537844, mission='K2')
        target_identifier(307210830, mission='TESS')

def test_read_lc_from_file():
    """Test the read_lc_from_file function
    """
    # First, the simple case of a 3 column file with no headers
    lc = read_lc_from_file('simple_table.txt')

    assert lc.colnames == ['time', 'flux', 'flux_err', 'exptime']
    assert lc.time.format == 'jd'
    npt.assert_array_equal(lc.time.value, np.array([1, 2, 3]))
    npt.assert_array_equal(lc.flux.value, np.array([1, 1, 1]))
    npt.assert_array_equal(lc.flux_err.value, np.array([0.1, 0.1, 0.1]))

    # Test if the exptime column behaves as expected
    npt.assert_array_equal(lc['exptime'].value, np.array([86400, 86400, 86400]))

    # Now let's try naming the columns
    # Using expected names:
    lc = read_lc_from_file('simple_table.txt',
                           colnames=['time', 'flux', 'flux_err'])
    npt.assert_array_equal(lc.time.value, np.array([1, 2, 3]))
    npt.assert_array_equal(lc.flux.value, np.array([1, 1, 1]))
    npt.assert_array_equal(lc.flux_err.value, np.array([0.1, 0.1, 0.1]))

    # Not using 'time'
    with pytest.raises(ValueError, match='time'):
        read_lc_from_file('simple_table.txt',
                          colnames=['t', 'flux', 'flux_err'])
        read_lc_from_file('simple_table_wrong_t.txt')

    # Not using 'flux'
    with pytest.raises(ValueError, match='flux'):
        read_lc_from_file('simple_table.txt',
                          colnames=['time', 'y', 'flux_err'])
        read_lc_from_file('simple_table_wrong_flux.txt')

    # Using 'time' anf 'flux' but not 'flux_err'
    # Correct number of columns
    with pytest.warns(FulmarWarning):
        lc = read_lc_from_file('simple_table.txt',
                               colnames=['time', 'flux', 'err'])
        # Check if a 'flux_err' column was created with Nans
        assert len(lc.colnames) == 5
        npt.assert_array_equal(
            np.array([True, True, True]), np.isnan(lc.flux_err))
    # Wrong number of columns
    with pytest.warns(FulmarWarning, match='should match'):
        read_lc_from_file('simple_table.txt',
                          colnames=['time', 'flux', 'flux_err', '4th_column'])

    # Then, the same file but with author, exptime and timeformat
    lc = read_lc_from_file(
        'simple_table.txt', author='TELESCOPE', exptime=120, timeformat='mjd')

    assert lc.author == 'TELESCOPE'
    assert lc.meta['EXPTIME'] == 120
    assert lc.time.format == 'mjd'
    npt.assert_array_equal(lc['exptime'], np.array([120, 120, 120]))

    # Correct headers in the file
    lc = read_lc_from_file('simple_table_correct_cols.txt')
    assert lc.colnames == ['time', 'flux', 'flux_err', 'exptime']
    assert lc.time.format == 'jd'
    npt.assert_array_equal(lc.time.value, np.array([1, 2, 3]))
    npt.assert_array_equal(lc.flux.value, np.array([1, 1, 1]))
    npt.assert_array_equal(lc.flux_err.value, np.array([0.1, 0.1, 0.1]))

def test_normalize_lc():
    """Test if normalize_lc actually normalizes the lightcurve"""
    lc = lk.LightCurve(time=np.arange(5), flux=3 * np.ones(5),
                       flux_err=0.1 * np.ones(5))

    npt.assert_allclose(np.median(normalize_lc(lc).flux), 1)
    npt.assert_allclose(np.median(normalize_lc(lc).flux_err), 0.1 / 3)

def test_time_flux_err():
    """Test if time_flux_err returns the expected values"""
    dirty_array = np.ones(10, dtype=object)
    time_array = np.linspace(1, 10, 10)
    dy_array = np.ones(10, dtype=object)
    dirty_array[1] = None
    dirty_array[2] = np.inf
    dirty_array[3] = -np.inf
    dirty_array[4] = np.nan
    dirty_array[5] = -99
    # time_array[8] = np.nan
    dy_array[9] = np.inf
    ts = TimeSeries([Time(time_array, format='jd'), dirty_array, dy_array],
                    names=['time', 'flux', 'flux_err'])
    ts.time[8] = np.nan

    t, y, dy = time_flux_err(ts)
    npt.assert_equal(t, [1, 7, 8])
    npt.assert_equal(y, [1, 1, 1])
    npt.assert_equal(dy, [1, 1, 1])

    # Now with flux_err as only nan
    dy_array = time_array * np.nan
    ts = TimeSeries([Time(time_array, format='jd'), dirty_array, dy_array],
                    names=['time', 'flux', 'flux_err'])
    ts.time[8] = np.nan
    # replace_nan_err is True
    t, y, dy = time_flux_err(ts)
    npt.assert_equal(dy, [0, 0, 0, 0])
    # replace_nan_err is False
    t, y, dy = time_flux_err(ts, replace_nan_err=False)
    npt.assert_array_equal(np.array([True, True, True, True]), np.isnan(dy))

def test_ts_binner():
    """test if ts_binner behaves as expected"""

    testtime = np.linspace(1, 10, 100)
    testflux = np.linspace(0.99, 1.01, 100)
    ts = TimeSeries([Time(testtime * u.d, format='jd'),
                     testflux], names=['time', 'flux'])
    ts_bin = ts_binner(ts, bin_duration=1 * u.d)

    expected_t = np.array([1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5])
    expected_flux = np.array([0.99111111, 0.99343434, 0.99565657, 0.99787879,
                              1.00010101, 1.00232323, 1.00454545, 1.00676768,
                              1.00888889])
    npt.assert_almost_equal(ts_bin.time.value, expected_t)
    npt.assert_almost_equal(ts_bin['flux'], expected_flux)
    npt.assert_equal(len(ts_bin), 9)

def test_fbn():
    """test if fbn behaves as expected"""

    testtime = np.linspace(1, 10, 1000)

    testflux = np.sin(np.pi * testtime)

    ts = TimeSeries([Time(testtime * u.d, format='jd'),
                     testflux], names=['time', 'flux'])
    ts_fold, ts_fold_bin = fbn(ts, 2, 5, 2 * u.d, 20)

    npt.assert_equal(len(ts_fold), 1000)
    npt.assert_equal(len(ts_fold_bin), 20)
    expected_time_bin = np.array([-0.95, -0.85, -0.75, -0.65, -0.55, -0.45,
                                  -0.35, -0.25, -0.15, -0.05, 0.05, 0.15, 0.25,
                                  0.35, 0.45, 0.55, 0.65, 0.75, 0.85, 0.95])

    npt.assert_almost_equal(ts_fold['phase_norm'], ts_fold.time.value / 2)
    npt.assert_almost_equal(ts_fold_bin['time'].value, expected_time_bin)
    npt.assert_almost_equal(ts_fold['phase_norm'].max(), 0.4954955)
    npt.assert_almost_equal(ts_fold['phase_norm'].min(), -.5)


import pygame
from classes.Controller import Controller
from classes.mothership.Mothership import Mothership

class MothershipController(Controller):
    def __init__(self, config):
        super().__init__(config)
        self.mothership_config = config.get("mothership")

        # set-up non-config related variables
        self.cycles_lapsed = 0
        self.spawned = False
        self.shot_counter = 0

        # callbacks injected by game_controller
        self.get_invader_count_callback = lambda: None
        self.get_lowest_invader_y_callback = lambda: None
        self.get_missile_callback = lambda: None
        self.get_score_text_callback = lambda: None

        # mothership sprite stored in sprite group
        self.mothership_group = pygame.sprite.Group()

    def mothership_is_exploding(self):
        if not self.mothership_group.sprites():
            return False
        if self.mothership_group.sprites()[0].active == False:
            return True

    def update(self, events, dt):
        if not self.spawned:
            self.update_spawn_logic()
        else:
            return self.update_mothership(dt)

    def update_mothership(self, dt):
        if self.mothership_group.sprites():
            self.check_missile_collision()
            mothership = self.mothership_group.sprites()[0]
            return mothership.update(self.shot_counter, dt)

        # If there are no motherships in the group, reset the spawn state.
        self.reset_spawn_state()
        self.event_manager.notify("mothership_exit")

    def update_spawn_logic(self):
        # if not self.check_invader_criteria():
        #     return False

        self.cycles_lapsed += 1
        if self.cycles_lapsed == self.mothership_config["cycles_until_spawn"]:
            self.spawn()

    def check_missile_collision(self):
        missile = self.get_missile_callback()
        mothership = self.mothership_group.sprites()
        if missile is not None and missile.active:
            collided = pygame.sprite.spritecollide(missile, mothership, False)
            if collided:
                self.mothership_hit()
                missile.remove()

    def mothership_hit(self):
        mothership = self.mothership_group.sprites()[0]
        points = mothership.calculate_points()
        score_text_surface = self.get_score_text_callback(str(points))
        mothership.explode(score_text_surface)
        self.event_manager.notify("mothership_hit", points)

    def reset_spawn_state(self):
        self.shot_counter = 0
        self.cycles_lapsed = 0
        self.spawned = False

    def on_update_shot_counter(self, data):
        self.shot_counter = (self.shot_counter + 1) % 16

    def check_invader_criteria(self):
        invader_count = self.get_invader_count_callback()
        lowest_invader_y = self.get_lowest_invader_y_callback()

        return (
            invader_count >= 8
            and lowest_invader_y
            >= self.mothership_config["qualifying_invader_y_position"]
        )

    def get_spawn_direction(self):
        return -1 if self.shot_counter % 2 == 1 else 1

    def get_spawn_position(self):
        return (
            self.mothership_config["spawn_right_position"]
            if self.shot_counter % 2 == 1
            else self.mothership_config["spawn_left_position"]
        )

    def spawn(self):
        self.event_manager.notify("mothership_spawned")
        self.spawned = True
        print("Spawning mothership")
        self.mothership_group.add(
            Mothership(
                self.get_spawn_position(),
                self.get_spawn_direction(),
                self.mothership_config["points_table"],
            )
        )


from selenium.webdriver.common.by import By

class TableDataExtractor:
    @staticmethod
    def get_column_data(table_element, col_index):
        """
        获取表格数据，返回指定列的列表
        :param table_element: 表格元素对象
        :param col_index: 列索引，从0开始
        :return: 包含指定列数据的列表
        """
        # 使用 TAG_NAME 找到所有行元素
        table_tr_list = table_element.find_elements(By.TAG_NAME, "tr")

        result_list = []

        # 遍历行元素，逐一获取每行的文本内容
        for tr in table_tr_list:
            # 使用 text 属性获取文本内容，并使用 split() 方法分割字符串
            list_2 = tr.text.split()
            if len(list_2) > col_index:
                result_list.append(list_2[col_index])

        return result_list


import streamlit as st
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error, mean_absolute_error

def calculate_smape(actual, predicted): # Function to calculate "Symmetric Mean Absolute Percentage Error"
    return round(
        np.mean(
            np.abs(predicted - actual) /
            ((np.abs(predicted) + np.abs(actual))/2)
        )*100, 2
    )

def calculate_mdape(actual, predicted): # Function to calculate "Median Absolute Percentage Error"
    return round(
        np.median(
            np.abs(predicted - actual) /
            ((np.abs(predicted) + np.abs(actual))/2)
        )*100, 2
    )

def convert_df(df):
    # IMPORTANT: Cache the conversion to prevent computation on every rerun
    return df.to_csv().encode('utf-8')

def get_adi_cv2(series):

    total_periods=series.count()
    non_zero_periods=series.loc[series!=0].count()
    adi=total_periods/non_zero_periods

    non_zero_series=series.loc[series!=0]
    cv2=(np.std(non_zero_series)/np.mean(non_zero_series))**2

    return adi,cv2

from prophet import Prophet
from prophet.diagnostics import cross_validation
from prophet.utilities import regressor_coefficients
import plotly.graph_objects as go

st.set_option('deprecation.showPyplotGlobalUse', False) # to prevent display of warnings
st.title("Time Series Forecasting for Different Demand Patterns") # Heading of App.
st.sidebar.image("https://www.analyticssteps.com/backend/media/thumbnail/6350483/7807104_1578230927_time_analysis_tittle-banner%20(1).jpg") # Adding Image to the Sidebar.

# TAKING INPUT FROM USER

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

st.sidebar.header("1. DATA")

with st.sidebar.expander("Uploading Dataset"): # Exapander inside Sidebar for UPLOADING DATA

    default_data=st.checkbox("Use Demo Dataset", value=False,key="default data")

    upload = st.file_uploader("Upload File", type={"csv"}) # File Uploader

    if upload is not None: # Checking if File is uploaded or not, If Something is Uploaded then only the "if" condition gets satisfied.
        df = pd.read_csv(upload,index_col=0) # Reading the CSV file as 'df' DataFrame. There should be one index like column in data other than Date and other important columns.
        df2 = df.copy()  #  'df2' is initialized so that we can always have a copy of Original Uploaded Dataset. All processing will be done on 'df'

    if default_data==True:
        df=pd.read_csv("trial_dataset2.csv", index_col=0)
        df2=df.copy()
        upload=1

with st.sidebar.expander("Info About Data"):

    if upload is not None:
        date_column=st.selectbox("Please Select Date Column",df.columns.to_list(),index=1 if default_data else 0) # The User has to select the Column name that has Dates/Time in the Time Series Data.
        date_format=st.text_input("What is the Date Format", value=r"%Y-%m-%d", help=r"For example '%Y-%m-%d' or '%d/%m/%Y %H:%M:%S'") # User has to mention the format in which Date is there in the Date Column.
        target_column=st.selectbox("Please Select Target Column",df.columns.to_list(), index=2 if default_data else 0) # The User has to select the Target column/y_variable which will be varying with Time and whose value has to be forecasted.

        freq_unit=st.selectbox("Unit of time present in time Series Data",["days","hours","minutes","seconds"])  # the frequency unit of Time Series data.
        freq=st.number_input(f"Frequency of Time Series in {freq_unit}",value=7.0 if default_data else 1.0,min_value=0.0) # Magnitude of frequency in 'freq_unit'.

with st.sidebar.expander("Filtering Data"): # Exapander inside Sidebar for FILTERING DATASET

    if upload is not None:  # Checking if File is uploaded or not, If Something is Uploaded then only the "if" condition gets satisfied.
        cat_cols = st.multiselect("Select Categorical Columns for Filtering Dataset",df.columns.to_list(), help='''Prophet can only forecast one series at a time. In case your dataset contains several time series, you might want to filter or aggregate these different series.
                Dimensions are the Categorical Columns on which the dataset can be filtered and aggregated on. In case no dimension is selected, all target values at the same date will be aggregated in order to get one target value per date.
                Example: For a dataset containing sales from 3 countries A, B and C, you could select countries B and C, and forecast the sum of their sales.''')
        # 'cat_cols' represent list containing Independent Categorical Features on which Data is to be filtered as per User's requirement.

        col_list=[st.checkbox(f"Keep all values for '{i}'", value=True, help=f"Check to keep all values or uncheck to filter values on column '{i}'.", key=f'{i}') for i in cat_cols] # 'key' parameter is used so that we can access the value of the streamlit object again using 'session_state'. Every streamlit object's value can be accessed using 'key' parameter.
        # 'col_list' contains checkboxes for all Categorical columns selected in 'cat_cols' multiselect object.
        for j in range(len(col_list)): # If we uncheck a checkbox (present in 'col_list') corresponding to a column in "cat_cols", then for that column a multiselect object is created that is used for selecting values from that column for filtering Data.
            if not col_list[j]:
                col=cat_cols[j]
                st.multiselect(f"Values for '{col}'",df[col].unique(),key=f'{col}m') # 'key' parameter is used so that we can access the value of this streamlit object somewhere else, using 'session_state' method. Every streamlit object's value can be accessed using it unique 'key' parameter.

        num_cols=st.multiselect("Select Numerical Independent Features that are to be Aggregated",df.columns.to_list(), help="Numerical Columns which will be aggregated so that a Single Time Series can be made. Add Columns that will be used as Extra Regressors.") # Many numerical columns, present in data, that may be used as regressors are specified here.
        agg_func_list=[st.selectbox(f"Aggregation Function for '{col}'",["mean","sum","min","max","median"], key=f"{col} agg") for col in num_cols] # Aggregation function for every column added in "num_cols" is specified here. We create a selectbox object for every column mentioned in 'num_cols' object.

        y_variable_agg_func=st.selectbox(f"Aggregation Function for '{target_column}'",["mean","sum","min","max","median"])  # Even after filtering the dataset based on columns in 'cat_cols', if we don't get one y value for one date value, then we need to aggregate the y_variable using the aggregation function mentioned in 'y_variable_agg_func'.

# We need to check everytime whether 'upload' is None or not, i.e whether the User has uploaded the Data or not. If the User hasn't uploaded the Data then we need not calculate/display anything on the site.
if upload is not None:
    filter_button=st.sidebar.checkbox("Apply Filter to Dataset",value=True if default_data else False) # Only After checking this button all the filtering, that user mentioned in different containers of DATA and MODELLING sections, takes place on the Dataset that user uploaded in "Uploading Datset" container.

    adi_value,cv2_value=get_adi_cv2(df[target_column])
    adi,cv2=st.columns(2)

    adi.metric("**:red[ADI]**", f"{round(adi_value,2)}")
    cv2.metric("**:red[CV Squared]**", f"{round(cv2_value,2)}")

    if adi_value<1.32 and cv2_value<0.49:
        classification="SMOOTH PATTERN"
    elif adi_value>=1.32 and cv2_value<0.49:
        classification="INTERMITTENT PATTERN"
    elif adi_value<1.32 and cv2_value>=0.49:
        classification="ERRATIC PATTERN"
    else:
        classification="LUMPY PATTERN"

    st.subheader(body=f"The Data Shows : {classification}")
    st.write("To read about Demand Classification, ADI and CV2 in Detail Check Out : [Link](https://frepple.com/blog/demand-classification/)")

if upload is not None:
    st.sidebar.header("2. MODELLING")

with st.sidebar.expander("Change Point Detection"):  # Exapander inside Sidebar for change_point_detection

    if upload is not None: # Checking if File is uploaded or not, If Something is Uploaded then only the "if" condition gets satisfied.
        # Parameters that affect the trend/changepoint detection in Prophet Model
        change_point_prior_scale=st.number_input("Change_Point_Prior_Scale", min_value=0.0, value=0.050, help="Prophet automatically detects changepoints in the trend.\nThis parameter determines trend flexibility by adjusting the number of changepoints detected.\nIf you make it high, the trend will be more flexible (more changepoints), and Vice Versa")
        change_point_range=st.number_input("Change_Point_Range", min_value=0.0, value=0.8,max_value=1.0, help="Proportion of history in which trend changepoints will be estimated. Defaults to 0.8 for the first 80%.")
        n_change_points=st.number_input("n_change_points", min_value=1, value=25, help="Number of potential changepoints to include.")
        growth_pattern=st.selectbox("Growth_Pattern",["linear","flat","logistic"], index=0, help="Specify the Pattern of Growth/Declination")
        if growth_pattern=="logistic": # If the Growth Pattern is 'logistic' then we need to add columns 'cap','floor' in our dataset that mentions a max_value and min_value of Prediction for every row, that 'cap','floor' value is mentioned in 'cap_value','floor_value' streamlit object respectively.
            cap_value=st.number_input("cap_value",value=10.0, help="If the Growth_Pattern is 'Logisitic', then you need to specify the max prediction value of y variable.")
            floor_value=st.number_input("floor_value",value=1.0, help="If the Growth_Pattern is 'Logisitic', then you need to specify the min prediction value of y variable.")

# Parameters that set the Seasonality aspect in Prophet Model
with st.sidebar.expander("Seasonalities"):  # Exapander inside Sidebar for Seasonalities

    if upload is not None:

        daily_seasonality=st.selectbox("Daily_Seasonality",["auto", False], index=1, help="Choose whether or not to include this seasonality in the model. In 'auto' mode, Prophet will include a seasonality only if there are at least 2 full periods of historical data (for example 2 years of data for yearly seasonality).")
        # daily seasonality means seasonality in a cycle from 12am one day to 12am another day i.e a period of 24hrs.

        weekly_seasonality=st.selectbox("Weekly_Seasonality",["auto", False, "Custom"], index=0, help="Choose whether or not to include this seasonality in the model. In 'auto' mode, Prophet will include a seasonality only if there are at least 2 full periods of historical data (for example 2 years of data for yearly seasonality).")
        # weekly seasonality by default means seasonality in a cycle from Sunday to Saturday and not any other consecutive 7 days. For any other consecutive 7 days, for example from Wednesday to next Tuesday, we need to add custom seasonality.
        if weekly_seasonality=="Custom":
            mode_w=st.selectbox("Mode for Weekly Seasonality", ["additive","multiplicative"], index=0, help="Determines how seasonality components should be integrated with the predictions:\nUse 'additive' when seasonality trend should be “constant” over the entire period (typically for linear trends).\nUse 'multiplicative' to increase the importance of the seasonality over time (typically for exponential trends).")
            fourier_order_w=st.number_input("Fourier Order for Weekly Seasonality", value=7, min_value=1, help="Each seasonality is a fourier series as a function of time. The fourier order is the number of terms in the series. A higher order can fit more complex and quickly changing seasonality patterns, but it will also make overfitting more likely. You can use the seasonality components plots of this app to tune this parameter visually.")
            prior_scale_w=st.number_input("Prior_Scale for Weekly Seasonality", value=10.0, min_value=1.0, help="Determines the magnitude of seasonality effects on your predictions. Decrease this value if you observe that seasonality effects are overfitted.")
            weekly_seasonality_value=False
        else:
            weekly_seasonality_value=weekly_seasonality

        yearly_seasonality=st.selectbox("Yearly_Seasonaltiy",["auto", False, "Custom"], index=0, help="Choose whether or not to include this seasonality in the model. In 'auto' mode, Prophet will include a seasonality only if there are at least 2 full periods of historical data (for example 2 years of data for yearly seasonality).")
        # yearly seasonality by default means seasonality in a cycle from January 1 to December 31 and not any other period of 365 days. For any other period of 365 days, example for June 30 2021 to June 30 2022, we need to add custom seasonality.
        if yearly_seasonality=="Custom":
            mode_y=st.selectbox("Mode for Yearly Seasonality", ["additive","multiplicative"], index=0, help="Determines how seasonality components should be integrated with the predictions:\nUse 'additive' when seasonality trend should be “constant” over the entire period (typically for linear trends).\nUse 'multiplicative' to increase the importance of the seasonality over time (typically for exponential trends).")
            fourier_order_y=st.number_input("Fourier Order for Yearly Seasonality", value=7, min_value=1, help="Each seasonality is a fourier series as a function of time. The fourier order is the number of terms in the series. A higher order can fit more complex and quickly changing seasonality patterns, but it will also make overfitting more likely. You can use the seasonality components plots of this app to tune this parameter visually.")
            prior_scale_y=st.number_input("Prior_Scale for Yearly Seasonality", value=10.0, min_value=1.0, help="Determines the magnitude of seasonality effects on your predictions. Decrease this value if you observe that seasonality effects are overfitted.")
            yearly_seasonality_value=False
        else:
            yearly_seasonality_value=yearly_seasonality

        add_custom_seasonality=st.checkbox("Add a Custom Seasonality in Model", help="Check to add a Custom Seasonality in Model (Other than ones listed above)")
        if add_custom_seasonality: # if 'add_custom_seasonality' is checked then this 'if' condition gets satisfied.
            name_c=st.text_input("Name of Seasonality", help="Give a Name for Your Custom Seasonality", value="Custom_Seasonality") # Specify the name of the custom Seasonality.
            period_c=st.number_input("Periods (in Days)", min_value=1, value=30, help="Number of days of each cycle.") # Period of Seasonality, means the length of 1 cycle of seasonality.
            mode_c=st.selectbox("Mode", ["additive","multiplicative"], index=0, help="Determines how seasonality components should be integrated with the predictions:\nUse 'additive' when seasonality trend should be “constant” over the entire period (typically for linear trends).\nUse 'multiplicative' to increase the importance of the seasonality over time (typically for exponential trends).")
            fourier_order_c=st.number_input("Fourier Order", value=7, min_value=1, help="Each seasonality is a fourier series as a function of time. The fourier order is the number of terms in the series. A higher order can fit more complex and quickly changing seasonality patterns, but it will also make overfitting more likely. You can use the seasonality components plots of this app to tune this parameter visually.")
            prior_scale_c=st.number_input("Prior_Scale", value=10.0, min_value=1.0, help="Determines the magnitude of seasonality effects on your predictions. Decrease this value if you observe that seasonality effects are overfitted.")

with st.sidebar.expander("Regressors"): # Expander inside sidebar for adding Regressors in Prophet Model.(For Multivariate Time Series Analysis)

    if upload is not None:

        regressors=st.multiselect("Add Regressors that affect y_variable", df.columns.to_list(), help="Add Numerical columns that have impact on value of y_variable.",default=num_cols)
        regressors_prior_scale=[st.number_input(f"Prior Scale for '{col}' regressor", value=10.0, min_value=1.0, help="Determines the magnitude of the regressor effect on your predictions.", key=f"{col} reg") for col in regressors]
        # regressors_prior_scale is a list of number_input objects for each regressor that have been added to 'regressors' multiselect object.

# Parameters that help in Model Evaluation.(Train test Splitting or Cross Validation)
if upload is not None:
    st.sidebar.header("3. MODEL EVALUATION")

with st.sidebar.expander("Train/Validation Splitting Or Cross Validation"):

    if upload is not None:

        cross_validation_=st.checkbox("Perform Cross Validation", value=False, help="Check to evaluate performance through a cross-validation, or uncheck to use a simple training/test split.") # Checkbox for performing Cross Validation or doing Simple Train-Test Split.

        if cross_validation_:
            cv=st.number_input("Number of CV Folds", value=5, min_value=2, help="Number of distinct training/validation pairs to include in the cross-validation.")
            horizon=st.number_input("Horizon of each Fold", value=10, min_value=2, help="Length of Validation period for each fold.")
            # This cross validation procedure can be done automatically for a range of historical cutoffs using the cross_validation function. We specify the forecast horizon (horizon), and then optionally the size of the initial training period (initial) and the spacing between cutoff dates (period). By default, the initial training period is set to three times the horizon, and cutoffs are made every half a horizon.
            # The output of cross_validation is a dataframe with the true values y and the out-of-sample forecast values yhat, at each simulated forecast date and for each cutoff date. In particular, a forecast is made for every observed point between cutoff and cutoff + horizon. This dataframe can then be used to compute error measures of yhat vs y.
            test_size=0

        else:   # If the user doesn't opts for Cross_Validation then he/she has to mention the test_size for the data Uploaded.
            test_size=st.number_input("Test Size in Percentage of Total Data", value=20.0, min_value=0.0, max_value=100.0)*0.01 # The user should Input the Test Size as percentage of size of CSV file Uploaded.

if upload is not None:
    forecast_button=st.sidebar.checkbox("Make Forecast for Future Dates", value=False)
    # If the User wants to get the forecast on Future dates (Which are not there in the initial Uploaded Dataset), then he/she has to check the 'forecast_button'.
    # If the 'forecast_button' is checked by the the user, then whole of the data uploaded by the user will be used for Training the Model and error/metrics will be calculated only on Training Data.

if upload is not None and forecast_button:
    st.sidebar.header("4. MAKE FORECAST")

with st.sidebar.expander("Forecasting on Future Dates",expanded=True):

    if upload is not None and forecast_button:

        if len(regressors)>0:

            st.write("Upload Test Data CSV file having the same Schema as the Training data CSV file.")
            test_upload=st.file_uploader("Upload Test Data", type={"csv"})

            if test_upload is not None:
                test_data=pd.read_csv(test_upload, index_col=0) # There should be one index like column in data other than Date and other important columns.
                test_data2=test_data.copy()

        else:

            upload_test_data=st.checkbox("Upload Test Data")

            if upload_test_data:

                st.write("Upload Test Data CSV file having the same Schema as the Training data CSV file.")
                test_upload=st.file_uploader("Upload Test Data", type={"csv"})

                if test_upload is not None:
                    test_data=pd.read_csv(test_upload,index_col=0) # There should be one 'index like column' in data other than Date and other important columns. 'index_col=0' makes the first column of data as the 'index'.
                    test_data2=test_data.copy()

            else:

                future_forecast_period=st.number_input("Future Forecast Period", help="Number of Future Dates on which Forecast is to be made", min_value=1, value=10)

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

# MODIFYING DATA AND TRAINING MODEL BASED ON USER INPUT.

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

## Modifying 'df' dataframe. All the filters and modifications that are specified in "Filtering Data" and "Info About Data"
if upload is not None:          # Checking if File is uploaded or not, If Something is Uploaded then only the "if" condition gets satisfied.
    if filter_button:           # If checkbox of Filter button is pressed, then only this 'if' condition gets satisfied and all filtering/processing of Uploaded dataset takes place.

        df[date_column]=pd.to_datetime(df[date_column], format=date_format) # Changing Data Type of Date Column to DateTime.
        dates=df[date_column].unique()

        for i in cat_cols:      # This loop iterates for all column names present in 'cat_cols' multiselect object and checks whether the checkbox corresponding to the particular column is checked or not.
            if not st.session_state[i] and len(st.session_state[i+'m'])>0:  # If the checkbox (present in 'col_list') corresponding to a column name (present in 'cat_cols') is unchecked and there are more than 0 values in the multiselect object (made as the checkbox for that column was unchecked) corresponding to that column, then we filter the dataset using the values mentioned in that multiselect object.
                df=df.loc[df[i].isin(st.session_state[i+'m'])]

        agg_funcs_for_num_cols=[st.session_state[col+" agg"] for col in num_cols] # Making a list of aggregation functions for each column mentioned in 'num_cols' object.
        diction=dict(zip(num_cols,agg_funcs_for_num_cols))                        # Making a dictionary that contains column name (present in 'num_cols') as key and its aggregation function (present in 'agg_funcs_for_num_cols') as its value.

        # Add changes here
        dates_present_after_filter=df[date_column].unique()

        if set(dates)!=set(dates_present_after_filter):

            filtered_out_dates=pd.DataFrame({date_column:[date for date in dates if date not in dates_present_after_filter]})
            if len(diction)>0:
                aggregations=df.agg(diction)
            df=pd.concat([df,filtered_out_dates], ignore_index=True)

            for col in num_cols:
                df[col].replace(np.nan, aggregations.loc[col], inplace=True)
            df[target_column].replace(np.nan, 0, inplace=True)

        diction[target_column]=y_variable_agg_func               # Adding a new key-value pair in the 'diction' dictionary, the new key-value pair is of y_variable and its aggregation function.

        df=df.groupby([date_column]).agg(diction).reset_index()  # This groups the data according to Date column and thus now we get one y value for one Date value, therefore only 1 Time Series.

        df.rename(columns={str(date_column): "ds", str(target_column):"y"}, inplace=True) # Just changing name of Date and target column to "ds" and "y" as its needed for Training of Prophet Model.

        if growth_pattern=="logistic": # If growth pattern is logistic then we need to mention 'cap' value for every row in dataset.
            df["cap"]=cap_value        # Here we are making new columns in 'df' named as 'cap' and 'floor' that has value contained in 'cap_value' and 'floor_value' respectively.
            df["floor"]=floor_value
        else:                          # On changing growth pattern from 'logistic' to any other growth pattern, we need to drop the 'cap','floor' column from the dataset 'df' therefore we have used df.drop() function. But considering the Case when we initially chose 'linear'/'flat' growth pattern then for that case df.drop() will raise a 'KeyError', thus we have used exception handelling.
            try:
                df.drop(["cap","floor"], axis=1, inplace=True)
            except KeyError:
                pass

        st.write(df2.head())

    else:  # This 'else' section is to restore the processed/filtered dataset to its original uploaded form when we uncheck the 'filter_button'.
        df=df2

if upload is not None:

    if forecast_button:

        if len(regressors)>0 or upload_test_data:

            if test_upload is not None:

                if filter_button:

                    test_data[date_column]=pd.to_datetime(test_data[date_column], format=date_format)
                    test_dates=test_data[date_column].unique()

                    for i in cat_cols:      # This loop iterates for all column names present in 'cat_cols' multiselect object and checks whether the checkbox corresponding to the particular column is checked or not.
                        if not st.session_state[i] and len(st.session_state[i+'m'])>0:  # If the checkbox (present in 'col_list') corresponding to a column name (present in 'cat_cols') is unchecked and there are more than 0 values in the multiselect object (made as the checkbox for that column was unchecked) corresponding to that column, then we filter the dataset using the values mentioned in that multiselect object.
                            test_data=test_data.loc[test_data[i].isin(st.session_state[i+'m'])]

                    agg_funcs_for_num_cols_forecast=[st.session_state[col+" agg"] for col in num_cols] # Making a list of aggregation functions for each column mentioned in 'num_cols' object.
                    diction_forecast=dict(zip(num_cols, agg_funcs_for_num_cols_forecast))              # Making a dictionary that contains column name (present in 'num_cols') as key and its aggregation function (present in 'agg_funcs_for_num_cols') as its value.

                    test_dates_present_after_filter=test_data[date_column].unique()

                    if set(test_dates)!=set(test_dates_present_after_filter):

                        filtered_out_test_dates=pd.DataFrame({date_column:[date for date in test_dates if date not in test_dates_present_after_filter]})
                        if len(diction_forecast)>0:
                            aggregations=test_data.agg(diction_forecast)
                        test_data=pd.concat([test_data, filtered_out_test_dates], ignore_index=True)

                        for col in num_cols:
                            test_data[col].replace(np.nan, aggregations.loc[col], inplace=True)

                    if len(diction_forecast)>0:
                        test_data=test_data.groupby([date_column]).agg(diction_forecast).reset_index()  # This groups the data according to Date column and thus now we get one y value for one Date value, therefore only 1 Time Series.
                    else: ## Adding this else statement ensures that even if len(diction_forecast)==0, the test_data will be finally having no repeated date values.
                        test_data.drop_duplicates(subset=date_column,inplace=True, ignore_index=True)

                    test_data.rename(columns={str(date_column): "ds"}, inplace=True) # Just changing name of Date and target column to "ds" and "y" as its needed for Training of Prophet Model.

                    if growth_pattern=="logistic": # If growth pattern is logistic then we need to mention 'cap' value for every row in dataset.
                        test_data["cap"]=cap_value        # Here we are making new columns in 'test_data' named as 'cap' and 'floor' that has value contained in 'cap_value' and 'floor_value' respectively.
                        test_data["floor"]=floor_value
                    else:                          # On changing growth pattern from 'logistic' to any other growth pattern, we need to drop the 'cap','floor' column from the dataset 'test_data' therefore we have used test_data.drop() function. But considering the Case when we initially chose 'linear'/'flat' growth pattern then for that case test_data.drop() will raise a 'KeyError', thus we have used exception handelling.
                        try:
                            test_data.drop(["cap","floor"], axis=1, inplace=True)
                        except KeyError:
                            pass

                else:
                    test_data=test_data2

## Fitting Data to the Model along with Hyperparameters mentioned in Above Sections.
if upload is not None:

    launch=st.checkbox("Launch Model",value=True if default_data else False, help="Check to launch forecast. A new forecast will be made each time some parameter is changed in the sidebar.")

    if launch and filter_button:

        model=Prophet(changepoint_prior_scale=change_point_prior_scale, changepoint_range=change_point_range, n_changepoints=n_change_points, growth=growth_pattern,
                       yearly_seasonality=yearly_seasonality_value, weekly_seasonality=weekly_seasonality_value, daily_seasonality=daily_seasonality)
        model_temp=model

        if weekly_seasonality=="Custom":
            model.add_seasonality(name="Weekly_Seasonality", fourier_order=fourier_order_w, period=7.0, prior_scale=prior_scale_w, mode=mode_w)
        else:
            model=model_temp

        if yearly_seasonality=="Custom":
            model.add_seasonality(name="Yearly_Seasonality", fourier_order=fourier_order_y, period=365.0, prior_scale=prior_scale_y, mode=mode_y)
        else:
            model=model_temp

        if add_custom_seasonality:
            model.add_seasonality(name=name_c, fourier_order=fourier_order_c, period=period_c, prior_scale=prior_scale_c, mode=mode_c)
        else:
            model=model_temp

        for col in regressors:
            model.add_regressor(name=col, prior_scale=st.session_state[col+" reg"])

        model_temp_2=model

        if forecast_button:

            model=model_temp_2
            model.fit(df)

            if cross_validation_:
                initial_size=len(df["ds"].values[0:-int(horizon*cv)-cv])  # [Total Data Size - (Horizon*cv)] = initial data size (Given Period = Horizon)
                df_cv = cross_validation(model, horizon=str(int(horizon*freq))+f" {freq_unit}", period=str(int(horizon*freq))+f" {freq_unit}", initial=str(int(initial_size*freq))+f" {freq_unit}")

            if len(regressors)>0 and test_upload is not None:

                future=model.make_future_dataframe(periods=test_data["ds"].count(), freq=pd.Timedelta(f"{float(freq)} " + freq_unit))

                for col in regressors:
                    future[col]=pd.concat([df[col], test_data[col]]).reset_index(drop=True)

                if growth_pattern=="logistic":
                    future["cap"]=cap_value
                    future["floor"]=floor_value
                else:
                    try:
                        future.drop(["cap","floor"], axis=1, inplace=True)
                    except KeyError:
                        pass

                forecast=model.predict(future)

            elif len(regressors)==0 and upload_test_data and test_upload is not None:

                future=model.make_future_dataframe(periods=test_data["ds"].count(), freq=pd.Timedelta(f"{float(freq)} " + freq_unit))

                if growth_pattern=="logistic":
                    future["cap"]=cap_value
                    future["floor"]=floor_value
                else:
                    try:
                        future.drop(["cap","floor"], axis=1, inplace=True)
                    except KeyError:
                        pass

                forecast=model.predict(future)

            elif len(regressors)==0 and not upload_test_data:

                future=model.make_future_dataframe(periods=future_forecast_period, freq=pd.Timedelta(f"{float(freq)} " + freq_unit))

                if growth_pattern=="logistic":
                    future["cap"]=cap_value
                    future["floor"]=floor_value
                else:
                    try:
                        future.drop(["cap","floor"], axis=1, inplace=True)
                    except KeyError:
                        pass

                forecast=model.predict(future)

        else:

            if cross_validation_:
                model=model_temp_2
                initial_size=len(df["ds"].values[0:-int(horizon*cv)-cv])  # [Total Data Size - (Horizon*cv)] = initial data size (Given Period = Horizon)
                model.fit(df)
                df_cv = cross_validation(model, horizon=str(int(horizon*freq))+f" {freq_unit}", period=str(int(horizon*freq))+f" {freq_unit}", initial=str(int(initial_size*freq))+f" {freq_unit}")
                future=model.make_future_dataframe(periods=0, freq=pd.Timedelta(f"{float(freq)} " + freq_unit))

                if growth_pattern=="logistic":
                    future["cap"]=cap_value
                    future["floor"]=floor_value
                else:
                    try:
                        future.drop(["cap","floor"], axis=1, inplace=True)
                    except KeyError:
                        pass

                for col in regressors:
                    future[col]=df[col]

                forecast=model.predict(future)

            else:
                model=model_temp_2
                model.fit(df.iloc[:int(df["ds"].count()*(1-test_size))])
                future=model.make_future_dataframe(periods=len(df.iloc[int(df["ds"].count()*(1-test_size)):,0]), freq=pd.Timedelta(f"{float(freq)} " + freq_unit))

                if growth_pattern=="logistic":
                    future["cap"]=cap_value
                    future["floor"]=floor_value
                else:
                    try:
                        future.drop(["cap","floor"], axis=1, inplace=True)
                    except KeyError:
                        pass

                for col in regressors:
                    future[col]=df[col]

                forecast=model.predict(future)

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

# OUTPUT DISPLAYED TO THE USER BASED ON HIS/HER INPUT. DISPLAYES THE FORESCAST/HOW THE MODEL PERFORMED.

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

if upload is not None:

    if launch and filter_button:

        st.subheader("1. Overview") # Adding Forecast Figure (Overview of Prophet Model)

        trace1=go.Scatter(
            x=df["ds"],
            y=df["y"],
            mode="lines",
            name="Actual Value",
            line=dict(color='rgb(244, 24, 231)', width=2)
        )

        if forecast_button and ( (len(regressors)>0 and test_upload is not None) or (len(regressors)==0 and not upload_test_data) or (len(regressors)==0 and upload_test_data and test_upload is not None) ):

            trace2=go.Scatter(
                x=df["ds"],
                y=forecast.iloc[:df["ds"].count()]["yhat"],
                mode="lines+markers",
                name="Predicted Values on Training Data",
                line=dict(color='rgb(151, 51, 30)', width=2)
            )
            trace3=go.Scatter(
                x=forecast.iloc[df["ds"].count():]["ds"],
                y=forecast.iloc[df["ds"].count():]["yhat"],
                mode="lines+markers",
                name="Forecasted Values on Future Dates",
                line=dict(color='rgb(20, 161, 10)', width=2)
            )

            fig1=go.Figure()
            fig1.add_trace(trace1)
            fig1.add_trace(trace2)
            fig1.add_trace(trace3)

            fig1['layout'].update(height = 500, width = 900, title = "Forecasted Vs Actual Values")
            st.plotly_chart(fig1)

        elif not forecast_button:

            trace2=go.Scatter(
                x=df["ds"],
                y=forecast["yhat"],
                mode="lines+markers",
                name="Predicted Values",
                line=dict(color='rgb(151, 51, 30)', width=2)
            )

            fig1=go.Figure()
            fig1.add_trace(trace1)
            fig1.add_trace(trace2)

            fig1['layout'].update(height = 500, width = 900, title = "Forecasted Vs Actual Values")
            st.plotly_chart(fig1)

        elif test_upload is None:
            st.error("NO TEST DATA UPLOADED.")

if upload is not None:

    if launch and filter_button:

        st.subheader("2. Metrics")

        if forecast_button and ( (len(regressors)>0 and test_upload is not None) or (len(regressors)==0 and not upload_test_data) or (len(regressors)==0 and upload_test_data and test_upload is not None) ):

            if cross_validation_:

                arr=df_cv["cutoff"].unique()
                metric_df=pd.DataFrame(columns=[f"cv_{j}" for j in range(1,cv+1)])

                for i,k in enumerate(arr):
                    temp=df_cv.loc[df_cv["cutoff"]==k]

                    mae_value=round(mean_absolute_error(temp["y"].values, temp["yhat"].values), 2)
                    rmse_value=round(mean_squared_error(temp["y"].values, temp["yhat"].values)**0.5, 2)
                    mdape_value=calculate_mdape(temp["y"].values, temp["yhat"].values)
                    smape_value=calculate_smape(temp["y"].values, temp["yhat"].values)

                    metric_df[f"cv_{i+1}"]=np.array([smape_value, mdape_value, rmse_value, mae_value])

                metric_df["METRIC"]=["SMAPE", "MDAPE", "RMSE", "MAE"]
                metric_df.set_index(keys="METRIC", inplace=True)

                st.dataframe(metric_df)

                smape_avg=round(metric_df.loc["SMAPE",:].mean(), 2)
                mdape_avg=round(metric_df.loc["MDAPE",:].mean(), 2)
                rmse_avg=round(metric_df.loc["RMSE",:].mean(), 2)
                mae_avg=round(metric_df.loc["MAE",:].mean(), 2)

                smape, mdape, rmse, mae=st.columns(4)

                smape.metric("**:red[AVG SMAPE]**", f"{smape_avg}%")
                mdape.metric("**:red[AVG MDAPE]**", f"{mdape_avg}%")
                rmse.metric("**:red[AVG RMSE]**", f"{rmse_avg}")
                mae.metric("**:red[AVG MAE]**", f"{mae_avg}")

            else:

                y_true=df["y"]
                y_pred=forecast.iloc[:df["ds"].count()]["yhat"]
                rmse_value=round(mean_squared_error(y_true, y_pred)**0.5, 2)
                mae_value=round(mean_absolute_error(y_true, y_pred), 2)
                mdape_value=calculate_mdape(y_true, y_pred)
                smape_value=calculate_smape(y_true, y_pred)

                smape,mdape,mae,rmse=st.columns(4,gap='small')

                smape.metric("**:red[SMAPE]**", f"{smape_value}%")
                mdape.metric(f"**:red[MDAPE]**", f"{mdape_value}%")
                rmse.metric(f"**:red[RMSE]**", f"{rmse_value}")
                mae.metric(f"**:red[MAE]**" , f"{mae_value}")

        elif not forecast_button:

            if cross_validation_:

                arr=df_cv["cutoff"].unique()
                metric_df=pd.DataFrame(columns=[f"cv_{j}" for j in range(1,cv+1)])

                for i,k in enumerate(arr):
                    temp=df_cv.loc[df_cv["cutoff"]==k]

                    mae_value=round(mean_absolute_error(temp["y"].values, temp["yhat"].values), 2)
                    rmse_value=round(mean_squared_error(temp["y"].values, temp["yhat"].values)**0.5, 2)
                    mdape_value=calculate_mdape(temp["y"].values, temp["yhat"].values)
                    smape_value=calculate_smape(temp["y"].values, temp["yhat"].values)

                    metric_df[f"cv_{i+1}"]=np.array([smape_value, mdape_value, rmse_value, mae_value])

                metric_df["METRIC"]=["SMAPE", "MDAPE", "RMSE", "MAE"]
                metric_df.set_index(keys="METRIC", inplace=True)

                st.dataframe(metric_df)

                smape_avg=round(metric_df.loc["SMAPE",:].mean(), 2)
                mdape_avg=round(metric_df.loc["MDAPE",:].mean(), 2)
                rmse_avg=round(metric_df.loc["RMSE",:].mean(), 2)
                mae_avg=round(metric_df.loc["MAE",:].mean(), 2)

                smape, mdape, rmse, mae=st.columns(4)

                smape.metric("**:red[AVG SMAPE]**", f"{smape_avg}%")
                mdape.metric("**:red[AVG MDAPE]**", f"{mdape_avg}%")
                rmse.metric("**:red[AVG RMSE]**", f"{rmse_avg}")
                mae.metric("**:red[AVG MAE]**", f"{mae_avg}")

            else:

                y_true_test=df.iloc[int(df["ds"].count()*(1-test_size)):]["y"].values
                y_pred_test=forecast.iloc[int(df["ds"].count()*(1-test_size)):]["yhat"].values
                y_true_train=df.iloc[:int(df["ds"].count()*(1-test_size))]["y"].values
                y_pred_train=forecast.iloc[:int(df["ds"].count()*(1-test_size))]["yhat"].values
                y_true_whole=df["y"].values
                y_pred_whole=forecast["yhat"].values

                if len(y_true_test)>0:
                    evaluation_type=st.radio("**How to Evaluate Metrics ?**", options=["Get Metrics Value only on Test/Validation Data", "Get Metrics Value only on Training Data", "Get Metrics Value on Whole Data"])
                else:
                    evaluation_type=st.radio("**How to Evaluate Metrics ?**", options=["Get Metrics Value only on Training Data", "Get Metrics Value on Whole Data"])

                smape,mdape,mae,rmse=st.columns(4,gap='small')

                if evaluation_type=="Get Metrics Value only on Test/Validation Data" and len(y_true_test)>0: # If test_size is 0 then y_true_test and y_pred_test will be empty array and mean_squared_error function will give error, so we are also checking size of y_true_test
                    rmse_value=round(mean_squared_error(y_true_test, y_pred_test)**0.5, 2)
                    mae_value=round(mean_absolute_error(y_true_test, y_pred_test), 2)
                    mdape_value=calculate_mdape(y_true_test, y_pred_test)
                    smape_value=calculate_smape(y_true_test, y_pred_test)

                elif evaluation_type=="Get Metrics Value on Whole Data":

                    rmse_value=round(mean_squared_error(y_true_whole, y_pred_whole)**0.5, 2)
                    mae_value=round(mean_absolute_error(y_true_whole, y_pred_whole), 2)
                    mdape_value=calculate_mdape(y_true_whole, y_pred_whole)
                    smape_value=calculate_smape(y_true_whole, y_pred_whole)

                elif evaluation_type=="Get Metrics Value only on Training Data":

                    rmse_value=round(mean_squared_error(y_true_train, y_pred_train)**0.5, 2)
                    mae_value=round(mean_absolute_error(y_true_train, y_pred_train), 2)
                    mdape_value=calculate_mdape(y_true_train, y_pred_train)
                    smape_value=calculate_smape(y_true_train, y_pred_train)

                smape.metric("**:red[SMAPE]**", f"{smape_value}%")
                mdape.metric(f"**:red[MDAPE]**", f"{mdape_value}%")
                rmse.metric(f"**:red[RMSE]**", f"{rmse_value}")
                mae.metric(f"**:red[MAE]**" , f"{mae_value}")

if upload is not None:

    if launch and filter_button:

        st.subheader("3. Components of the Forecast")

        if forecast_button and ( (len(regressors)>0 and test_upload is not None) or (len(regressors)==0 and not upload_test_data) or (len(regressors)==0 and upload_test_data and test_upload is not None) ):
            show_components=True
        elif not forecast_button:
            show_components=True
        else:
            show_components=False

        if show_components:

            # Trend Component of Model

            trace_trend_lower=go.Scatter(
                x=forecast["ds"],
                y=forecast["trend_lower"],
                mode="lines",
                name="Lower_Trend",
                line=dict(color='rgb(244, 24, 231)', width=2)
            )
            trace_trend_upper=go.Scatter(
                x=forecast["ds"],
                y=forecast["trend_upper"],
                mode="lines",
                name="Upper_Trend",
                fill='tonexty',
                line_color="indigo"
            )
            fig2=go.Figure()
            fig2.add_trace(trace_trend_lower)
            fig2.add_trace(trace_trend_upper)
            fig2['layout'].update(height = 350, width = 750, title = "Trend")
            st.plotly_chart(fig2)

            # Seasonality Component of Model

            if add_custom_seasonality:
                trace_custom_seasonality=go.Scatter(
                    x=forecast["ds"],
                    y=forecast[name_c],
                    mode="lines",
                    name=name_c,
                    fill="tozeroy"
                )
                fig3=go.Figure()
                fig3.add_trace(trace_custom_seasonality)
                fig3['layout'].update(height = 350, width = 750, title = name_c)
                st.plotly_chart(fig3)

            if weekly_seasonality=="Custom":
                trace_weekly_seasonality=go.Scatter(
                    x=forecast["ds"],
                    y=forecast["Weekly_Seasonality"],
                    mode="lines",
                    name="Weekly_Seasonality",
                    fill="tozeroy"
                )
                fig4=go.Figure()
                fig4.add_trace(trace_weekly_seasonality)
                fig4['layout'].update(height = 350, width = 750, title = "Weekly_Seasonality")
                st.plotly_chart(fig4)

            elif weekly_seasonality=="auto" and "weekly" in forecast.columns.to_list():
                trace_weekly_seasonality=go.Scatter(
                    x=forecast["ds"],
                    y=forecast["weekly"],
                    mode="lines",
                    name="Weekly_Seasonality",
                    fill="tozeroy"
                )
                fig4=go.Figure()
                fig4.add_trace(trace_weekly_seasonality)
                fig4['layout'].update(height = 350, width = 750, title = "Weekly_Seasonality")
                st.plotly_chart(fig4)

            if yearly_seasonality=="Custom":
                trace_yearly_seasonality=go.Scatter(
                    x=forecast["ds"],
                    y=forecast["Yearly_Seasonality"],
                    mode="lines",
                    name="Yearly_Seasonality",
                    fill="tozeroy"
                )
                fig5=go.Figure()
                fig5.add_trace(trace_yearly_seasonality)
                fig5['layout'].update(height = 350, width = 750, title = "Yearly_Seasonality")
                st.plotly_chart(fig5)

            elif yearly_seasonality=="auto" and "yearly" in forecast.columns.to_list():
                trace_yearly_seasonality=go.Scatter(
                    x=forecast["ds"],
                    y=forecast["yearly"],
                    mode="lines",
                    name="Yearly_Seasonality",
                    fill="tozeroy"
                )
                fig5=go.Figure()
                fig5.add_trace(trace_yearly_seasonality)
                fig5['layout'].update(height = 350, width = 750, title = "Yearly_Seasonality")
                st.plotly_chart(fig5)

            # Effect of Regressors.
            if len(regressors)>0:
                df_regressors_coef=regressor_coefficients(model)

                trace_regressors=go.Bar(
                    x=df_regressors_coef["regressor"],
                    y=df_regressors_coef["coef"],
                    name="Effect of Regressors added in Forecast"
                )

                fig_regressors=go.Figure()
                fig_regressors.add_trace(trace_regressors)
                st.plotly_chart(fig_regressors)

if upload is not None:

    if launch and filter_button:

        if forecast_button and ( (len(regressors)>0 and test_upload is not None) or (len(regressors)==0 and not upload_test_data) or (len(regressors)==0 and upload_test_data and test_upload is not None) ):
            download=True
        elif not forecast_button:
            download=True
        else:
            download=False

        if download:

            csv = convert_df(forecast[["ds","yhat","yhat_lower","yhat_upper"]])
            st.download_button("Download Forecasted Values", csv, file_name="forecasted_values.csv", mime='text/csv')

# Assumptions
## No Null value in Date column.


#!/usr/bin/env python3

import os
import json
import datetime
from PIL import Image
from collections import defaultdict

def translate_bbox(	image_path='./dataset/Train',
					ann_input='./dataset/Annotations',
					ann_output='./dataset/Augmented_Annotations',
					input_format='txt',
					output_format='txt'):
	''' Translating between different bounding box annotation formats '''
	if input_format == 'txt':
		BBOX = defaultdict(list)
		for filename in os.listdir(ann_input):
			with open('{}/{}'.format(ann_input, filename), 'r') as f:
				filename = filename.split('.')
				filename[-1] = '.jpg'
				filename = ''.join(filename)
				next(f)
				for line in f:
					line = line.split()
					label = line[4]
					x = int(line[0])
					y = int(line[1])
					w = int(line[2])
					h = int(line[3])
					BBOX[filename].append([x, y, w, h, label])
	elif input_format == 'csv':
		TheLines = []
		BBOX = defaultdict(list)
		with open(ann_input, 'r') as F:
			next(F)
			for line in F:
				line = line.strip().split(':')
				filename = line[0].split(',')[0]
				label = line[5].split('"')[4]
				x = int(line[2].split(',')[0])
				y = int(line[3].split(',')[0])
				w = int(line[4].split(',')[0])
				h = int(line[5].split(',')[0].split('}')[0])
				BBOX[filename].append([x, y, w, h, label])
	elif input_format == 'xml':
		TheLines = []
		BBOX = defaultdict(list)
		for item in os.listdir(ann_input):
			with open('{}/{}'.format(ann_input, item), 'r') as F:
				next(F)
				filename = F.readline().strip().split()[0].split('>')[1]\
				.split('<')[0]
				Xx = []
				Yy = []
				Ww = []
				Hh = []
				Llabel = []
				for line in F:
					line = line.strip().split()[0]
					if '<xmin>' in line:
						Xx.append(line.split('>')[1].split('<')[0])
					elif '<ymin>' in line:
						Yy.append(line.split('>')[1].split('<')[0])
					elif '<xmax>' in line:
						Ww.append(line.split('>')[1].split('<')[0])
					elif '<ymax>' in line:
						Hh.append(line.split('>')[1].split('<')[0])
					elif '<name>' in line:
						Llabel.append(line.split('>')[1].split('<')[0])
				for x, y, w, h, label in zip(Xx, Yy, Ww, Hh, Llabel):
					x = int(x)
					y = int(y)
					w = int(w)
					h = int(h)
					BBOX[filename].append([x, y, w, h, label])
	elif input_format == 'coco':
		print('Not Currently Implemented')
	if output_format == 'txt':
		for name in BBOX:
			filename = name.split('.')[0]
			output = '{}/{}.txt'.format(ann_output, filename)
			with open(output, 'w') as f:
				total = str(len(BBOX[name]))
				f.write(total+'\n')
				for line in BBOX[name]:
					line = ' '.join(str(v) for v in line)+'\n'
					f.write(line)
	elif output_format == 'csv':
		output = '{}/Translated.csv'.format(ann_output)
		for name in BBOX:
			with open(output, 'a+') as f:
				header = 'filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes\n'
				line = f.seek(0)
				if f.readline() != header:
					f.write(header)
				items = 0
				for line in BBOX[name]:
					line = [str(i) for i in line]
					x = line[0]
					y = line[1]
					w = line[2]
					h = line[3]
					label = line[4]
					total = str(len(BBOX[name]))
					size = os.stat('{}/{}'.format(image_path, name)).st_size
					items += 1
					TheLine = '{},{},"{{}}",{},{},"{{""name"":""rect"",""x"":{},""y"":{},""width"":{},""height"":{}}}","{{""{}"":""""}}"\n'\
					.format(name, size, total, items, x, y, w, h, label)
					f.write(TheLine)
	elif output_format == 'xml':
		for name in BBOX:
			filename = name[:-4]#name.split('.')[0]
			output = '{}/{}.xml'.format(ann_output, filename)
			with open(output, 'w') as f:
					source = 'https://github.com/sarisabban/SinfNet'
					total = str(len(BBOX[name]))
					W, H = Image.open('{}/{}'.format(image_path, name)).size
					f.write('<annotation>\n')
					f.write('\t<filename>{}.jpg</filename>\n'.format(filename))
					f.write('\t<source>{}</source>\n'.format(source))
					f.write('\t<path>../dataset/Train/{}.jpg</path>\n'\
					.format(filename))
					f.write('\t<size>\n')
					f.write('\t\t<width>{}</width>\n'.format(W))
					f.write('\t\t<height>{}</height>\n'.format(H))
					f.write('\t\t<depth>3</depth>\n')
					f.write('\t</size>\n')
					f.write('\t<segments>{}</segments>\n'.format(total))
					items = 0
					for line in BBOX[name]:
						line = [str(i) for i in line]
						x = line[0]
						y = line[1]
						w = line[2]
						h = line[3]
						label = line[4]
						items += 1
						f.write('\t<object>\n')
						f.write('\t\t<name>{}</name>\n'.format(label))
						f.write('\t\t<bndbox>\n')
						f.write('\t\t\t<xmin>{}</xmin>\n'.format(x))
						f.write('\t\t\t<ymin>{}</ymin>\n'.format(y))
						f.write('\t\t\t<xmax>{}</xmax>\n'.format(w))
						f.write('\t\t\t<ymax>{}</ymax>\n'.format(h))
						f.write('\t\t</bndbox>\n')
						f.write('\t</object>\n')
					f.write('</annotation>')
	elif output_format == 'coco':
		output = '{}/temp'.format(ann_output)
		with open(output, 'w') as f:
			year = datetime.datetime.now().strftime('%Y')
			time = datetime.datetime.now().strftime('%A %d %B %Y @ %H:%M')
			header = '{{"info":\n\t\t{{"year":{},\n\t\t"version":"1",\n\t\t"description":"Microorganism Annotation",\n\t\t"contributor":"",\n\t\t"url":"https://github.com/sarisabban/sinfnet",\n\t\t"date_created":"{}"}},\n\n"images":\n'.format(year, time)
			f.write(header)
			IMG_LIST = []
			img_count = 0
			for name in BBOX:
				W, H = Image.open('{}/{}'.format(image_path, name)).size
				The_img_Line = '{{"id":{},"width":{},"height":{},"file_name":"{}","license":1,"date_captured":""}}'\
				.format(img_count, W, H, name)
				IMG_LIST.append(The_img_Line)
				img_count += 1
			f.write('\t\t'+str(IMG_LIST))
			f.write(',')
			ANN_LIST=[]
			f.write('\n\t\t\n"annotations":\n')
			img_count = 0
			ann_count = 0
			labels = []
			for name in BBOX:
				for i in BBOX[name]: labels.append(i[-1])
			collect = set(labels)
			code = {}
			codeR={}
			for l, n in enumerate(collect):
				code[n] = l
				codeR[l]= n
			for name in BBOX:
				size=str(os.stat('{}/{}'.format(image_path, name)).st_size)
				ID = name+size
				for line in BBOX[name]:
					line = [str(i) for i in line]
					x = line[0]
					y = line[1]
					w = line[2]
					h = line[3]
					area = str(int(w)*int(h))
					label = line[4]
					The_ann_Line = '{{"id":{},"image_id":"{}","segmentation":[{},{},{},{},{},{},{},{}],"area":{},"bbox":[{},{},{},{}],"iscrowd":0, "categoty_id":{}}}'\
					.format(ann_count, img_count, x, y, str(int(x)+int(w)), y, str(int(x)+int(w)), str(int(y)+int(h)), x, str(int(y)+int(h)), area, x, y, w, h, code[label])
					ANN_LIST.append(The_ann_Line)
					ann_count += 1
				img_count += 1
			f.write('\t\t'+str(ANN_LIST))
			f.write(',')
			f.write('\n\t\t\n"categories":\n')
			CAT_LIST = []
			for labs in code:
				The_cat_Line = '{{"id":{}, "name":"{}"}}'\
				.format(code[labs], codeR[code[labs]])
				CAT_LIST.append(The_cat_Line)
			f.write('\t\t'+str(CAT_LIST))
			f.write(',')
			f.write('\n\n')
			f.write('"licenses":[{"id":1,"name":"MIT","url":"https://github.com/sarisabban/SinfNet/blob/master/LICENSE"}]}')
		with open('{}/temp'.format(ann_output), 'rt') as fin:
			with open('{}/Translated.json'.format(ann_output), 'wt') as fout:
				for line in fin:
					fout.write(line.replace("'", ""))
		os.remove('{}/temp'.format(ann_output))
	print('[+] Translate done')

def translate_poly(	image_path='./dataset/Train',
					ann_input='./dataset/Annotations',
					ann_output='./dataset/Augmented_Annotations',
					input_format='csv',
					output_format='json'):
	''' Translating between different polygon annotation formats '''
	if input_format == 'csv':
		TheLines = []
		POLY = defaultdict(list)
		with open(ann_input, 'r') as F:
			next(F)
			for line in F:
				line = line.strip().split(':')
				filename = line[0].split(',')[0]
				path = '{}/{}'.format(image_path, filename)
				W, H = Image.open(path).size
				lineColor = [0, 255, 0, 128]
				fillColor = [255, 255, 0, 128]
				shape_type = line[1].split('"')[2]
				line_color = 'null'
				fill_color = 'null'
				label = line[3].split('"')[-3]
				x_points = line[2].split('"')[0][:-2][1:].split(',')
				y_points = line[3].split('"')[0][:-2][1:].split(',')
				points = []
				for x, y in zip(x_points, y_points):
					try:
						x = int(x)
						y = int(y)
						point = [x, y]
						points.append(point)
					except:
						x = float(x)
						y = float(y)
						point = [x, y]
						points.append(point)

				POLY[filename,
					str(lineColor),
					str(fillColor),
					path, W, H].append([label,
										line_color,
										fill_color,
										points,
										shape_type])
	elif input_format == 'json':
		POLY = defaultdict(list)
		for TheFile in os.listdir(ann_input):
			with open('{}/{}'.format(ann_input, TheFile), 'r') as f:
				d = json.load(f)
				filename = filename = TheFile[:-4]+'jpg'#TheFile.split('.')[0]+'.jpg'
				W, H = d['imageWidth'], d['imageHeight']
				lineColor = d['lineColor']
				fillColor = d['fillColor']
				path = d['imagePath']
				size = os.stat('{}/{}'.format(image_path, filename)).st_size
				for tot, x in enumerate(d['shapes']): total = tot+1
				num = 0
				for item in d['shapes']:
					label = item['label']
					points = item['points']
					x = []
					y = []
					for i in points:
						x.append(i[0])
						y.append(i[1])
					shape_type = item['shape_type']
					line_color = item['line_color']
					fill_color = item['fill_color']
					POLY[filename,
						str(lineColor),
						str(fillColor),
						path, W, H].append([label,
											line_color,
											fill_color,
											points,
											shape_type])
					num += 1
	if output_format == 'csv':
		with open('{}/Translated.csv'.format(ann_output), 'w+') as f:
			header = 'filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes\n'
			line = f.seek(0)
			if f.readline() != header:
				f.write(header)
			for name in POLY:
				filename = name[0]
				size = os.stat('{}/{}'.format(image_path, filename)).st_size
				for tot, x in enumerate(POLY[name]): total = tot+1
				num = 0
				for item in POLY[name]:
					shapetype = item[4]
					label = item[0]
					x = []
					y = []
					for points in item[3]:
						x.append(points[0])
						y.append(points[1])
					TheLine = '{},{},"{{}}",{},{},"{{""name"":""{}"",""all_points_x"":{},""all_points_y"":{}}}","{{""{}"":""""}}"\n'\
					.format(filename, size, total, num, shape_type, x, y, label)
					f.write(TheLine)
					num += 1
	elif output_format == 'json':
		for name in POLY:
			filename = name[0][:-4]
			with open('{}/{}.json'.format(ann_output, filename), 'w+') as f:
				version = '3.11.2'
				flags = ''
				lineColor = name[1]
				fillColor = name[2]
				path = name[3]
				imageData = ''
				W, H = str(name[4]), str(name[5])
				# files have the 'imagePath' value incorrect, it should be '../Train/{}'.format('FILENAME.json')
				header = '{{"version": "{}",\n"flags": {{{}}},\n"lineColor": {},\n"fillColor": {},\n"imagePath": "{}",\n"imageData": "{}",\n"imageHeight": {},\n"imageWidth": {},\n"shapes": ['\
				.format(version, flags, lineColor, fillColor, path, imageData, W, H)
				f.write(header)
				for info in POLY[name]:
					shape_type = info[4]
					line_color = info[1]
					fill_color = info[2]
					label = info[0]
					points = info[3]
					body = '\n\t{{"label": "{}",\n\t\t"line_color": {},\n\t\t"fill_color": {},\n\t\t"points": {},\n\t\t"shape_type": "{}"}},'\
					.format(label, line_color, fill_color, points, shape_type)
					f.write(body)
				loc = f.seek(0, os.SEEK_END)
				f.seek(loc-1)
				f.write(']}')
	print('[+] Translate done')


#!/usr/bin/env python
import rospy
from std_msgs.msg import String
from nav_msgs.msg import Odometry
import tf
from tf.transformations import euler_from_quaternion
import numpy as np
from geometry_msgs.msg import Twist
import time
from gazebo_msgs.msg import ModelStates
from std_msgs.msg       import Float64
payload_state = None
d, s = 1, 1
global i, j, k

def payload(msg):
    global payload_state
    global i, j, k
    idx = msg.name.index('payload')
    payload_state = msg.pose[idx]
    orientation_q = payload_state.orientation
    orientation_list = [orientation_q.x, orientation_q.y, orientation_q.z, orientation_q.w]
    (roll, pitch, yaw) = euler_from_quaternion (orientation_list)

    print(np.rad2deg(roll), np.rad2deg(pitch))

    # # rolln = (10*np.tan(-roll))
    # # pitchn = (10*np.tan(pitch))
    # rolln = (10*np.tan(-roll))
    # pitchn = (10*np.tan(-pitch))
    # #A = np.array([[-d*0.5, -s*.5],[-d*.5, s*.5],[d*.5, -s*.5],[d*.5, s*.5]])
    # A = np.array([[-d*0.5, s*.5],[-d*.5, -s*.5],[d*.5, s*.5],[d*.5, -s*.5]])
    # state = np.array([[rolln], [pitchn]])
    # control = np.dot(A,state)

    # initialcontrol = np.array([[0.12], [0.12], [0.12], [0.12]])  #L0 = 10cm
    # controln = control + initialcontrol   # L = Delta(L) + L0
    # print(round(controln[0], 3))
    # print(round(controln[1], 3))
    # print(round(controln[2], 3))
    # print(round(controln[3], 3))
    # print('----------------')
    # # #try1 Working rolln = roll & pitchn = pitch
    # # l0_pub.publish(round(controln[2], 3))
    # # l1_pub.publish(round(controln[3], 3))
    # # l2_pub.publish(round(controln[0], 3))
    # # l3_pub.publish(round(controln[1], 3))
    # l0_pub.publish(round(controln[0], 3))
    # l1_pub.publish(round(controln[1], 3))
    # l2_pub.publish(round(controln[2], 3))
    # l3_pub.publish(round(controln[3], 3))

    theta_x = 10 * np.tan(-roll)
    theta_y = 10 * np.tan(pitch)
    x0, y0 = -0.5, 1
    x1, y1 = -0.5, -1
    x2, y2 = 1, 0.5

    A = np.array([[y0, x0],[y1, x1],[y2, x2]])
    state = np.array([[theta_x], [theta_y]])
    control = np.dot(A,state)
    initialcontrol = np.array([[0.12], [0.12], [0.12]])
    controln = control + initialcontrol
    print(round(controln[0], 3))
    print(round(controln[1], 3))
    print(round(controln[2], 3))

    print('----------------')
    l0_pub.publish(round(controln[0], 3))
    l1_pub.publish(round(controln[1], 3))
    l2_pub.publish(round(controln[2], 3))

    # Ainv = np.linalg.pinv(A)
    # controlnn = np.array([controln[0,0], controln[1,0],controln[2,0],controln[3,0]])
    # statenn = np.dot(Ainv,controlnn)
    # print(np.rad2deg(statenn[0])*0.1, np.rad2deg(statenn[1])*0.1)

if __name__ == '__main__':
    rospy.init_node('pub_sub', anonymous=True)
    rospy.Subscriber('/gazebo/model_states', ModelStates, payload)
    rate = rospy.Rate(10) # 10hz

    vel_pub0 = rospy.Publisher('/minion_0/diff_drive_controller/cmd_vel', Twist, queue_size=10)
    vel_pub1 = rospy.Publisher('/minion_1/diff_drive_controller/cmd_vel', Twist, queue_size=10)
    vel_pub2 = rospy.Publisher('/minion_2/diff_drive_controller/cmd_vel', Twist, queue_size=10)
    vel_pub3 = rospy.Publisher('/minion_3/diff_drive_controller/cmd_vel', Twist, queue_size=10)

    l0_pub = rospy.Publisher('/minion_0/effort_controllers/command', Float64, queue_size=10)
    l1_pub = rospy.Publisher('/minion_1/effort_controllers/command', Float64, queue_size=10)
    l2_pub = rospy.Publisher('/minion_2/effort_controllers/command', Float64, queue_size=10)
    l3_pub = rospy.Publisher('/minion_3/effort_controllers/command', Float64, queue_size=10)

    l0_pub.publish(0.12)
    l1_pub.publish(0.12)
    l2_pub.publish(0.12)
    while not rospy.is_shutdown():
        twist0 = Twist()
        twist0.linear.x = 0.08
        twist0.angular.z = 0
        vel_pub0.publish(twist0)
        twist1 = Twist()
        twist1.linear.x = 0.08
        twist1.angular.z = 0
        vel_pub1.publish(twist1)
        twist2 = Twist()
        twist2.linear.x = 0.08
        twist2.angular.z = 0
        vel_pub2.publish(twist2)
        twist3 = Twist()
        twist3.linear.x = 0.0
        twist3.angular.z = 0.0
        vel_pub3.publish(twist3)
        rate.sleep()
    rospy.spin()


from prettytable import PrettyTable
from DB_Desc_Accesibilidad_BE import Desc_AccesibilidadDB

class desc_AccesibilidadBE:
    def __init__(self):
        self.desc_accesibilidadbd=Desc_AccesibilidadDB()

    def getAllDescription(self):
        result = self.desc_accesibilidadbd.getDescription()

        table = PrettyTable()
        table.field_names = [
            "idDA",
            "Descripcion",
            "IdAccesibilidad"
        ]

        for residencia in result:
            table.add_row(
                [
                    residencia["idDA"],
                    residencia["Descripcion"],
                    residencia["IdAccesibilidad"]
                ]
            )
        print(table)
        table.clear()

    def addDescription(self):
        print("\nAñadiendo una nueva descripcion...")
        Descripcion = input("\nDescripción: ")
        idAccesibilidad = input("\nid de accesibilidad: ")

        self.desc_accesibilidadbd.insertDescription(Descripcion,idAccesibilidad)

        print("\nLa descripción se ha agregado con éxito.")
        self.getAllDescription()

    def updateDescription(self):
        print("\nActualizando la descripción...")
        id = int(input("\nID de la descripción a actualizar: "))

        descripcion = self.desc_accesibilidadbd.searchDesc_AccesibilidadById(id)
        update = int(input("¿Actualizar descripción? 0-No - 1-Yes: "))
        if update == 1:
            print(f"Descripción: {descripcion['Descripcion']}")
            descripcion = input("Nueva descripción: ")
        else:
            descripcion = descripcion["Descripcion"]

        self.desc_accesibilidadbd.updateDescriptionBD(id,descripcion)
        print("\nLos cambios se han efectuado con éxito.")
        self.getAllDescription()

    def deleteDescription(self):
        print("Borrando descripciónn...")
        id = int(input("ID de descripciónn a eliminar: "))

        self.desc_accesibilidadbd.deleteDescriptionBD(id)
        print("La residencia se ha removido con éxito.")
        self.getAllDescription()