import mysql.connector as connection
import pandas as pd
import numpy as np
from app import app 
from flask import jsonify
from flask import flash, request, json
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.ensemble import RandomForestRegressor
from sklearn.svm import SVR
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from werkzeug.exceptions import HTTPException
from joblib import Parallel, delayed
import joblib

def connectDB():
    try:
        mydb = connection.connect(host="localhost", database = 'mitra_panen_skripsi',user="root", passwd="",use_pure=True)
        return mydb
    except Exception as e:
        print(str(e))

# Define the function to return the MAPE values
def calculate_mape(actual, predicted) -> float:
    # Convert actual and predicted
    # to numpy array data type if not already
    if not all([isinstance(actual, np.ndarray),
                isinstance(predicted, np.ndarray)]):
        actual, predicted = np.array(actual), 
        np.array(predicted)
  
    # Calculate the MAPE value and return
    return round(np.mean(np.abs((
      actual - predicted) / actual)) * 100, 3)

@app.route('/training/full')
def training_full():
    try:
        # get data from database
        mydb = connectDB()
        query = "SELECT fish_type_id, seed_amount, seed_weight, survival_rate, average_weight, pond_volume, total_feed_spent, harvest_amount FROM harvest_fish"
        df = pd.read_sql(query,mydb)
        # print(df)
        
        # set data train
        x_train = df.iloc[:, :7].values
        y_train = df.iloc[:, 7].values
        
        # Training Model
        method = request.args.get('method')
        if(method == 'linear'):
            print("Linear Regression")
            # Linear Regression Model 
            model = LinearRegression()
            model.fit(x_train, y_train)
        elif(method == 'poly'):
            print("Polynomial Regression")
            # Polynomial Regression Model full data
            model_poly = PolynomialFeatures(degree = 3)
            x_poly = model_poly.fit_transform(x_train)
            model_poly.fit(x_poly, y_train)
            model = LinearRegression()
            model.fit(x_poly, y_train)
        elif(method == 'rf'):
            print("Random Forest Regression")
            # Random Forest Regression Model
            model = RandomForestRegressor(min_samples_leaf=1, n_estimators=100, random_state=0)
            model.fit(x_train, y_train)
        elif(method == 'svr'):
            print("SVR")
            model = make_pipeline(StandardScaler(), SVR(kernel="poly", C=100, gamma="scale", degree=3, coef0=2))
            model.fit(x_train, y_train)
        else:
            print("tidak ada request")
            model = LinearRegression()
            model.fit(x_train, y_train)
            
        # save regression model to file
        joblib.dump(model, 'regression_model.pkl')
        joblib.dump(method, 'method.pkl')
        if(method == 'poly'):
            joblib.dump(model_poly, 'regression_model_poly.pkl')

        # respon API
        respon = jsonify()
        respon.data = json.dumps({
            "code": 200,
            "status": 'Success',
            "message": "Berhasil menyimpan training model",
        })
        respon.status_code = 200
        return respon
    except Exception as e:
        print(str(e))

def get_total_feed_spent(fish_id, seed_amount, sr) -> float:
    # check seed amount
    if(seed_amount == 1000 or seed_amount == 1500 or seed_amount == 2000):
        temp_amount = seed_amount
        check = 0
    else:
        temp_amount = 1000
        check = 1
        
    # get data from database
    mydb = connectDB()
    query = "SELECT total_feed_spent FROM harvest_fish WHERE fish_type_id=" + fish_id + " AND seed_amount=" + str(temp_amount)
    df = pd.read_sql(query,mydb)
    df_values = df.values.flatten()
    
    # calculate total feed spent
    if(check == 1):
        for i in range(len(df_values)):
            df_values[i] = seed_amount / 1000 * df_values[i]
    total_feed_spent = sum(df_values) / len(df_values)
    total_feed_spent = sr / 100 * total_feed_spent
    return round(total_feed_spent, 1)

@app.route('/implements')
def implements_model():
    try:
        # get data
        fish_id = request.args.get('fish_id')
        seed_amount = request.args.get('seed_amount')
        seed_weight = request.args.get('seed_weight')
        pond_volume = request.args.get('pond_volume')
        total_fish_count = request.args.get('total_fish_count') 
        
        # processing data
        average_weight = 1000 / int(total_fish_count)
        average_weight = round(average_weight, 2)
        if(fish_id == '1'):
            sr_arr = [85,87,89,91,93]
        elif(fish_id == '2'):
            sr_arr = [84,86,88,90,92]
        else:
            sr_arr = [65,70,75,80,85]
        
        # load model
        method = joblib.load('method.pkl')
        model = joblib.load('regression_model.pkl')
            
        # predict data with regression model by survival rate
        result_predict = []
        table_result = []
        graph_result = []
        tfs = []
        for i in range(len(sr_arr)):
            sr = sr_arr[i]
            total_feed_spent = get_total_feed_spent(fish_id, int(seed_amount), sr)
            tfs.append(total_feed_spent)
            data = np.array([[fish_id, seed_amount, seed_weight, sr, average_weight, pond_volume, total_feed_spent]])
            data = np.array(data, dtype=float)
            if(method == 'poly'):
                model_poly = joblib.load('regression_model_poly.pkl')
                predict = model.predict(model_poly.fit_transform(data))
            else:
                predict = model.predict(data)

            # control value of prediction
            max_harvest = average_weight * int(seed_amount) / 1000
            if(predict[0] > max_harvest):
                predict[0] = max_harvest
            elif(predict[0] < 0):
                predict[0] = 0
                
            result_predict.append(round(predict[0],3))
            table_result.append({"sr":sr, "predict":round(predict[0],3)})
            graph_result.append({"x":sr, "y":round(predict[0],3)})
        
        # save implements result to file
        implements_result = {
            "fish_id":fish_id, "seed_amount":seed_amount, "seed_weight":seed_weight,
            "average_weight":average_weight, "total_feed_spent":tfs, "sr_arr":sr_arr, 
            "result_predict":result_predict, "table_result":table_result, "graph_result":graph_result
        }
        joblib.dump(implements_result, "implements_result.pkl")
        
        # response API
        respon = jsonify()
        respon.data = json.dumps({
            "code": 200,
            "status": 'Success',
            "message": "Berhasil implements model",
            "method": method,
            "predict": result_predict,
        })
        respon.status_code = 200
        return respon
    except Exception as e:
        print(str(e))

@app.route('/implements/prediction')
def implements_prediction():
    try:
        # get data
        fish_id = request.args.get('fish_id')
        seed_amount = request.args.get('seed_amount')
        seed_weight = request.args.get('seed_weight')
        pond_volume = request.args.get('pond_volume')
        total_fish_count = request.args.get('total_fish_count') 
        sr = request.args.get('survival_rate')
        sr = float(sr)
        
        # processing data
        average_weight = 1000 / int(total_fish_count)
        average_weight = round(average_weight, 2)
        
        # load model
        method = joblib.load('method.pkl')
        model = joblib.load('regression_model.pkl')
            
        # predict data with regression model
        total_feed_spent = get_total_feed_spent(fish_id, int(seed_amount), sr)
        data = np.array([[fish_id, seed_amount, seed_weight, sr, average_weight, pond_volume, total_feed_spent]])
        data = np.array(data, dtype=float)
        if(method == 'poly'):
            model_poly = joblib.load('regression_model_poly.pkl')
            predict = model.predict(model_poly.fit_transform(data))
        else:
            predict = model.predict(data)

        # control value of prediction
        max_harvest = average_weight * int(seed_amount) / 1000
        if(predict[0] > max_harvest):
            predict[0] = max_harvest
        elif(predict[0] < 0):
            predict[0] = 0
        
        # duration fish
        if(fish_id == '1'):
            day = list(range(1, 91))
        else:
            day = list(range(1,181))
          
        # cultivation data  
        total_fish = predict[0] * 1000 / average_weight
        growth = (average_weight - float(seed_weight)) / (len(day)-1)
        weight = []
        feed_spent = []
        simulation_table = []
        current_feed = 0.0
        current_total_feed = 0.0
        current_weight = 0.0
        temp_total_feed = 0.0
        rule_feed = [0.0] * 4
        for j in range(len(day)):
            # simulation weight
            if(day[j] == 1):
                current_weight += float(seed_weight)
            else:
                current_weight = float(current_weight) + float(growth)
            weight.append({"x":day[j], "y":round(current_weight,3)})
            
            # make rule feed using percentage above day 30
            if(day[j] == 31):
                temp_total_feed = total_feed_spent - current_total_feed
                if(fish_id == '1'):
                    rule_feed[0] = (temp_total_feed * 0.4) / 30 # day 31-60 feed spent 40%
                    rule_feed[1] = (temp_total_feed * 0.6) / 30 # day 61-90 feed spent 60%
                else:
                    rule_feed[0] = (temp_total_feed * 0.1) / 30 # day 31-60 feed spent 10%
                    rule_feed[1] = (temp_total_feed * 0.15) / 30 # day 61-90 feed spent 15%
                    rule_feed[2] = (temp_total_feed * 0.45) / 60 # day 91-150 feed spent 45%
                    rule_feed[3] = (temp_total_feed * 0.3) / 30 # day 151-180 feed spent 30% 
                    
            # simulation feed spent
            if(day[j] == 1 or day[j] == 2): # day 1-2 feed spent 0
                current_feed = 0.0
            elif(day[j] >= 3 and day[j] <= 5): # day 3-5 feed spent 0.15kg
                current_feed = 0.15 * float(seed_amount) / 1000
            elif(day[j] == 6 or day[j] == 7): # day 6-7 feed spent 0.3kg
                current_feed = 0.3 * float(seed_amount) / 1000
            elif(day[j] == 8 or day[j] == 9): # day 8-9 feed spent 0.4kg
                current_feed = 0.4 * float(seed_amount) / 1000
            elif(day[j] >= 10 and day[j] <= 30): # day 10-30 feed spent 0.6kg
                current_feed = 0.6 * float(seed_amount) / 1000
            elif(day[j] >= 31 and day[j] <= 60): # day 31-60 feed spent rule[0]
                current_feed = rule_feed[0]
            elif(day[j] >= 61 and day[j] <= 90): # day 61-90 feed spent rule[1]
                current_feed = rule_feed[1]
            elif(day[j] >= 91 and day[j] <= 150): # day 91-150 feed spent rule[2]
                current_feed = rule_feed[2]
            elif(day[j] >= 151 and day[j] <= 180): # day 151-180 feed spetn rule[3]
                current_feed = rule_feed[3]
            else:
                current_feed = 1

            current_total_feed += float(current_feed)
            feed_spent.append({"x":day[j], "y":round(current_feed,1)})
            total_weight = total_fish * current_weight / 1000
            simulation_table.append({"day":day[j], "weight":round(current_weight,1), "feed_spent":round(current_feed,1), "total_feed_spent":round(current_total_feed,1), "total_weight":round(total_weight,1)})
        
        # response API
        respon = jsonify()
        respon.data = json.dumps({
            "code": 200,
            "status": 'Success',
            "message": "Berhasil implements prediction model",
            "method": method,
            "predict": round(predict[0],1),
            "total_feed_spent": round(total_feed_spent,1),
            "simulation_table": simulation_table
        })
        respon.status_code = 200
        return respon
    except Exception as e:
        print(str(e))

@app.route('/implements/result')
def get_implements_result():
    try:
        # get implements result data
        implements_result = joblib.load("implements_result.pkl")
        fish_id = implements_result["fish_id"]
        seed_amount = implements_result["seed_amount"]
        seed_weight = implements_result["seed_weight"]
        average_weight = implements_result["average_weight"]
        total_feed_spent = implements_result["total_feed_spent"]
        sr_arr = implements_result["sr_arr"]
        result_predict = implements_result["result_predict"]
        table_result = implements_result["table_result"]
        graph_result = implements_result["graph_result"]
        
        # duration fish
        if(fish_id == '1'):
            day = list(range(1, 91))
            day_range = ['1-2', '3-5', '6-7', '8-9', '10-30', '31-60', '61-90']
        else:
            day = list(range(1,181))
            day_range = ['1-2', '3-5', '6-7', '8-9', '10-30', '31-60', '61-90', '91-150', '151-180']
            
        # processing simulation weight and feed spent
        graph_weight = []
        graph_feed_spent = []
        feed_spent_arr = []
        total_weight_arr = []
        simulation_table_arr = []
        simple_feed_arr = []
        for i in range(len(sr_arr)):
            total_fish = result_predict[i] * 1000 / average_weight
            growth = (average_weight - float(seed_weight)) / (len(day)-1)
            weight = []
            feed_spent = []
            simulation_table = []
            simple_feed = []
            current_feed = 0.0
            current_total_feed = 0.0
            current_weight = 0.0
            temp_total_feed = 0.0
            rule_feed = [0.0] * 4
            for j in range(len(day)):
                # simulation weight
                if(day[j] == 1):
                    current_weight += float(seed_weight)
                else:
                    current_weight = float(current_weight) + float(growth)
                weight.append({"x":day[j], "y":round(current_weight,3)})
                
                # make rule feed using percentage above day 30
                if(day[j] == 31):
                    temp_total_feed = total_feed_spent[i] - current_total_feed
                    if(fish_id == '1'):
                        rule_feed[0] = (temp_total_feed * 0.4) / 30 # day 31-60 feed spent 40%
                        rule_feed[1] = (temp_total_feed * 0.6) / 30 # day 61-90 feed spent 60%
                    else:
                        rule_feed[0] = (temp_total_feed * 0.1) / 30 # day 31-60 feed spent 10%
                        rule_feed[1] = (temp_total_feed * 0.15) / 30 # day 61-90 feed spent 15%
                        rule_feed[2] = (temp_total_feed * 0.45) / 60 # day 91-150 feed spent 45%
                        rule_feed[3] = (temp_total_feed * 0.3) / 30 # day 151-180 feed spent 30% 
                        
                # simulation feed spent
                if(day[j] == 1 or day[j] == 2): # day 1-2 feed spent 0
                    current_feed = 0.0
                elif(day[j] >= 3 and day[j] <= 5): # day 3-5 feed spent 0.15kg
                    current_feed = 0.15 * float(seed_amount) / 1000
                elif(day[j] == 6 or day[j] == 7): # day 6-7 feed spent 0.3kg
                    current_feed = 0.3 * float(seed_amount) / 1000
                elif(day[j] == 8 or day[j] == 9): # day 8-9 feed spent 0.4kg
                    current_feed = 0.4 * float(seed_amount) / 1000
                elif(day[j] >= 10 and day[j] <= 30): # day 10-30 feed spent 0.6kg
                    current_feed = 0.6 * float(seed_amount) / 1000
                elif(day[j] >= 31 and day[j] <= 60): # day 31-60 feed spent rule[0]
                    current_feed = rule_feed[0]
                elif(day[j] >= 61 and day[j] <= 90): # day 61-90 feed spent rule[1]
                    current_feed = rule_feed[1]
                elif(day[j] >= 91 and day[j] <= 150): # day 91-150 feed spent rule[2]
                    current_feed = rule_feed[2]
                elif(day[j] >= 151 and day[j] <= 180): # day 151-180 feed spetn rule[3]
                    current_feed = rule_feed[3]
                else:
                    current_feed = 1

                current_total_feed += float(current_feed)
                feed_spent.append({"x":day[j], "y":round(current_feed,1)})
                total_weight = total_fish * current_weight / 1000
                simulation_table.append({"day":day[j], "weight":round(current_weight,3), "feed_spent":round(current_feed,1), "total_weight":round(total_weight,3)})
                simple_feed.append(round(current_feed,1))
            total_weight_arr.append(round(total_weight,3))
            feed_spent_arr.append(round(current_total_feed,1))
            simulation_table_arr.append(simulation_table)
            graph_weight.append(weight)
            graph_feed_spent.append(feed_spent)
            simple_feed = list(set(simple_feed))
            simple_feed_arr.append(simple_feed)
                
        # mapping simple simulation feed
        simple_table_arr = []
        for i in range(len(simple_feed_arr)):
            simple_table = []
            for j in range(len(day_range)):
                simple_table.append({'day_range':day_range[j], 'feed_spent':simple_feed_arr[i][j]})
            simple_table_arr.append(simple_table)
        
        # response API
        respon = jsonify()
        respon.data = json.dumps({
            "code": 200,
            "status": 'Success',
            "message": "Berhasil mendapatkan data hasil implementasi",
            "sr": sr_arr,
            "total_feed_spent": total_feed_spent,
            "predict": result_predict,
            "table_result": table_result,
            "graph_result": graph_result,
            "table_simulation_1": simulation_table_arr[0],
            "table_simulation_2": simulation_table_arr[1],
            "table_simulation_3": simulation_table_arr[2],
            "table_simulation_4": simulation_table_arr[3],
            "table_simulation_5": simulation_table_arr[4],
            "graph_weight": graph_weight,
            "graph_feed": graph_feed_spent,
            "table_simple_1": simple_table_arr[0],
            "table_simple_2": simple_table_arr[1],
            "table_simple_3": simple_table_arr[2],
            "table_simple_4": simple_table_arr[3],
            "table_simple_5": simple_table_arr[4],
            "total_feed_spent_arr": feed_spent_arr,
            "total_weight": total_weight_arr
        })
        respon.status_code = 200
        return respon
    except Exception as e:
        print(str(e))

@app.route('/best/params')
def get_best_param():
    try:
        # get data from database
        mydb = connectDB()
        query = "SELECT fish_type_id, seed_amount, seed_weight, survival_rate, average_weight, pond_volume, total_feed_spent, harvest_amount FROM harvest_fish"
        df = pd.read_sql(query,mydb)
        x = df.iloc[:, :7].values
        y = df.iloc[:, 7].values
        
        # make pipeline for polynomial regression
        pipeline_poly = Pipeline([
            ('poly', PolynomialFeatures()),
            ('regression', LinearRegression())
        ])
        
        # make pipeline for SVR
        pipeline_svr = Pipeline([
            ('scaler', StandardScaler()),
            ('svr', SVR())
        ])
        
        model_params = {
            'linear_regression': {
                'model': LinearRegression(),
                'params' : {
                    # No Parameter
                }  
            },
            'polynomial_regression': {
                'model': pipeline_poly,
                'params' : {
                    'poly__degree' : [2,3,4]
                }
            },
            'random_forest': {
                'model': RandomForestRegressor(),
                'params' : {
                    'n_estimators': [1,10,100],
                    'min_samples_leaf' : [1,2,3],
                    'random_state': [0]
                }
            }, 
            'svr': {
                'model': pipeline_svr,
                'params': {
                    'svr__kernel': ["linear", "poly", "rbf", "sigmoid", "precomputed"],
                    'svr__degree': [2,3,4],
                    'svr__gamma': ['scale', 'auto'],
                    'svr__coef0': [0,1,2],
                    'svr__C': [1,10,100],
                }
            }   
        }
        
        # get best parameter model
        scores = []
        for model_name, mp in model_params.items():
            clf = GridSearchCV(mp['model'], mp['params'], scoring='neg_root_mean_squared_error', cv=19)
            clf.fit(x, y)
            scores.append({
                'model': model_name,
                'best_score': clf.best_score_,
                'best_params': clf.best_params_
            })
        score_df = pd.DataFrame(scores,columns=['model','best_score','best_params'])
        table_scores = score_df.to_html(index=False)
        
        # response API
        respon = jsonify()
        respon.data = json.dumps({
            "code": 200,
            "status": 'Success',
            "message": "Berhasil mendapatkan parameter terbaik setiap model",
            "scores": scores,
            "table_scores": table_scores
        })
        respon.status_code = 200
        return respon  
    except Exception as e:
        print(str(e))

@app.route('/split/data')
def split_data():
    try:
        # get data from database
        mydb = connectDB()
        test_size = request.args.get('test_size')
        test_size = float(test_size)
        query = "SELECT fish_type_id, seed_amount, seed_weight, survival_rate, average_weight, pond_volume, total_feed_spent, harvest_amount FROM harvest_fish"
        df = pd.read_sql(query,mydb)
        
        # split data to train and test
        x = df.iloc[:, :7].values
        y = df.iloc[:, 7].values
        x_train, x_test, y_train, y_test = train_test_split(x,y,test_size=test_size,random_state=0)
        
        # save split data
        split_data = {"x_train":x_train, "x_test":x_test, "y_train":y_train, "y_test":y_test}
        joblib.dump(split_data, "split_data.pkl")
        
        # setting tuning hyperparameter
        setting_param = request.args.get('setting')
        if(setting_param == "default"):
            # Linear Regression Model 
            lin = LinearRegression()
            lin.fit(x_train, y_train)
            
            # Polynomial Regression Model
            poly = PolynomialFeatures()
            x_poly = poly.fit_transform(x_train)
            poly.fit(x_poly, y_train)
            lin2 = LinearRegression()
            lin2.fit(x_poly, y_train)
            
            # Random Forest Regression Model
            regressor = RandomForestRegressor(random_state=0)
            regressor.fit(x_train, y_train)
            
            # SVR Model
            # model dengan akurasi terbaik : SVR(kernel="poly", C=100, gamma="auto", degree=2, coef0=1)
            regr = make_pipeline(StandardScaler(), SVR())
            regr.fit(x_train, y_train)
        else :
            # Linear Regression Model 
            lin = LinearRegression()
            lin.fit(x_train, y_train)
            
            # Polynomial Regression Model
            poly = PolynomialFeatures(degree = 3)
            x_poly = poly.fit_transform(x_train)
            poly.fit(x_poly, y_train)
            lin2 = LinearRegression()
            lin2.fit(x_poly, y_train)
            
            # Random Forest Regression Model
            regressor = RandomForestRegressor(min_samples_leaf=1, n_estimators=100, random_state=0)
            regressor.fit(x_train, y_train)
            
            # SVR Model
            regr = make_pipeline(StandardScaler(), SVR(kernel="poly", C=100, gamma="scale", degree=3, coef0=2))
            regr.fit(x_train, y_train)
        
        # save all model
        all_model = {"linear":lin, "poly":poly, "lin2":lin2, "rf":regressor, "svr":regr}
        joblib.dump(all_model, "all_model.pkl")
        
        respon = jsonify()
        respon.data = json.dumps({
            "code": 200,
            "status": 'Success',
            "message": "Berhasil simpan split data",
        })
        respon.status_code = 200
        return respon
    except Exception as e:
        print(str(e))

@app.route('/testing/model')
def testing_model():
    try:
        # get split data
        split_data = joblib.load("split_data.pkl")
        x_train = split_data["x_train"]
        y_train = split_data["y_train"]
        x_test = split_data["x_test"]
        y_test = split_data["y_test"]
         
        # set data train
        data_train = []
        for i in range(len(y_train)):
            data_train.append({"fish_type_id":x_train[i][0], "seed_amount":x_train[i][1], "seed_weight":x_train[i][2], "survival_rate":x_train[i][3], "average_weight":x_train[i][4], "pond_volume":x_train[i][5], "total_feed_spent":x_train[i][6], "harvest_amount":y_train[i]})
        
        # set data test
        data_test = []
        for i in range(len(y_test)):
            data_test.append({"fish_type_id":x_test[i][0], "seed_amount":x_test[i][1], "seed_weight":x_test[i][2], "survival_rate":x_test[i][3], "average_weight":x_test[i][4], "pond_volume":x_test[i][5], "total_feed_spent":x_test[i][6], "harvest_amount":y_test[i]})
        
        # load all regression model
        all_model = joblib.load("all_model.pkl")
        lin = all_model["linear"]
        poly = all_model["poly"]
        lin2 = all_model["lin2"]
        regressor = all_model["rf"]
        regr = all_model["svr"]
        
        # predict with data test
        y_linear = lin.predict(x_test)
        y_poly = lin2.predict(poly.fit_transform(x_test))
        y_rf = regressor.predict(x_test) 
        y_svr = regr.predict(x_test)
        
        # set data comparison between harvest_amount actual (data test) with predict
        data_compare = []
        for i in range(len(y_test)):
            data_compare.append({"actual":y_test[i], "y_linear":round(y_linear[i],3), "y_poly":round(y_poly[i],3), "y_rf":round(y_rf[i],3), "y_svr":round(y_svr[i],3)})
            
        #setup data graph
        actual_graph = []
        linear_graph = []
        poly_graph   = []
        rf_graph     = []
        svr_graph    = []
        data_compare = sorted(data_compare, key=lambda data:data["actual"])
        for i in range(len(y_test)):
            actual_graph.append({"x":i+1, "y":data_compare[i]["actual"]})
            linear_graph.append({"x":i+1, "y":data_compare[i]["y_linear"]})
            poly_graph.append({"x":i+1, "y":data_compare[i]["y_poly"]})
            rf_graph.append({"x":i+1, "y":data_compare[i]["y_rf"]})
            svr_graph.append({"x":i+1, "y":data_compare[i]["y_svr"]})
        
        # test accuracy model with RMSE and MAPE
        rmse = [0] * 4
        rmse[0] = round(mean_squared_error(y_test, y_linear, squared=False),3)
        rmse[1] = round(mean_squared_error(y_test, y_poly, squared=False),3)
        rmse[2] = round(mean_squared_error(y_test, y_rf, squared=False),3)
        rmse[3] =  round(mean_squared_error(y_test, y_svr, squared=False),3)
        mape = [0] * 4  
        mape[0] = str(calculate_mape(y_test, y_linear)) + ' %'
        mape[1] = str(calculate_mape(y_test, y_poly)) + ' %'
        mape[2] = str(calculate_mape(y_test, y_rf)) + ' %'
        mape[3] = str(calculate_mape(y_test, y_svr)) + ' %'
        mape_num = [0] * 4
        mape_num[0] = calculate_mape(y_test, y_linear)
        mape_num[1] = calculate_mape(y_test, y_poly)
        mape_num[2] = calculate_mape(y_test, y_rf)
        mape_num[3] = calculate_mape(y_test, y_svr)
        metode = ['Linear Regression', 'Polynomial Regression', 'Random Forest Regression', 'SVR']
        _method = ['linear', 'poly', 'rf', 'svr']
        test_result = []
        result = []
        for i in range(len(metode)):
            test_result.append({"metode":metode[i], "rmse":rmse[i], "mape":mape[i]})
            result.append({"method":_method[i], "rmse":rmse[i], "mape":mape_num[i]})
            
        # check if minimal rmse not same with minimal mape so use minimal rmse for best method
        minimal_rmse = min(result, key=lambda x: x["rmse"])
        minimal_mape = min(result, key=lambda x: x["mape"])
        if(minimal_rmse["method"] != minimal_mape["method"]):
            best_method = minimal_rmse["method"]
        else:
            best_method = minimal_mape["method"]
        
        # Save model with high accuracy
        joblib.dump(best_method, 'method.pkl')
        if(best_method == "linear"):
            joblib.dump(lin, 'regression_model.pkl')
        elif(best_method == "poly"):
            joblib.dump(lin2, 'regression_model.pkl')
            joblib.dump(poly, 'regression_model_poly.pkl')
        elif(best_method == "rf"):
            joblib.dump(regressor, 'regression_model.pkl')
        else:
            joblib.dump(regr, 'regression_model.pkl')
            
        # response API  
        respon = jsonify()
        respon.data = json.dumps({
            "code": 200,
            "status": 'Success',
            "message": "Berhasil testing model",
            "data_train": data_train,
            "data_test": data_test,
            "data_compare": data_compare,
            "test_result": test_result,
            "minimal_rmse": minimal_rmse,
            "minimal_mape": minimal_mape,
            "best_method": best_method,
            "actual_graph": actual_graph,
            "linear_graph": linear_graph,
            "poly_graph": poly_graph,
            "rf_graph": rf_graph,
            "svr_graph": svr_graph
        })
        respon.status_code = 200
        return respon
        
    except Exception as e:
        print(str(e))

@app.errorhandler(HTTPException)
def handle_exception(e):
    """Return JSON instead of HTML for HTTP errors."""
    # start with the correct headers and status code from the error
    response = e.get_response()
    # replace the body with JSON
    response.data = json.dumps({
        "code": e.code,
        "name": e.name,
        "description": e.description,
    })
    response.content_type = "application/json"
    return response
        
if __name__ == "__main__":
    app.run(host="0.0.0.0",port=4000)