import gymnasium as gym
from gymnasium import error, spaces, utils, Env
from gymnasium.spaces import MultiDiscrete, Box
from gymnasium.utils import seeding
import math
import pymunk
import pygame
from pymunk import pygame_util
screen_width = 1904
screen_height = 960
target = 350
class Robot():
    def __init__(self, space):

        self.tick = 0

        moment = 10
        friction = 0.6

        self.shape = pymunk.Poly.create_box(None, (50, 100))
        body_moment = pymunk.moment_for_poly(moment, self.shape.get_vertices())
        self.body = pymunk.Body(moment, body_moment)

        self.body.position = (200, 350)
        self.shape.body = self.body
        self.shape.color = (150, 150, 150, 0)

        head_moment = pymunk.moment_for_circle(moment, 0, 30)
        self.head_body = pymunk.Body(moment, head_moment)
        self.head_body.position = (self.body.position.x, self.body.position.y+80)
        self.head_shape = pymunk.Circle(self.head_body, 30)
        self.head_shape.friction = friction
        self.head_joint = pymunk.PivotJoint(self.head_body, self.body, (-5, -30), (-5, 50))
        self.head_joint2 = pymunk.PivotJoint(self.head_body, self.body, (5, -30), (5, 50))

        arm_size = (100, 20)
        self.left_arm_upper_shape = pymunk.Poly.create_box(None, arm_size)
        left_arm_upper_moment = pymunk.moment_for_poly(moment, self.left_arm_upper_shape.get_vertices())
        self.left_arm_upper_body = pymunk.Body(moment, left_arm_upper_moment)
        self.left_arm_upper_body.position = (self.body.position.x-70, self.body.position.y+30)
        self.left_arm_upper_shape.body = self.left_arm_upper_body
        self.left_arm_upper_joint = pymunk.PivotJoint(self.left_arm_upper_body, self.body, (arm_size[0] / 2, 0), (-25, 30))
        self.la_motor = pymunk.SimpleMotor(self.body, self.left_arm_upper_body, 0)

        self.right_arm_upper_shape = pymunk.Poly.create_box(None, arm_size)
        right_arm_upper_moment = pymunk.moment_for_poly(moment, self.right_arm_upper_shape.get_vertices())
        self.right_arm_upper_body = pymunk.Body(moment, right_arm_upper_moment)
        self.right_arm_upper_body.position = (self.body.position.x+70, self.body.position.y+30)
        self.right_arm_upper_shape.body = self.right_arm_upper_body
        self.right_arm_upper_joint = pymunk.PivotJoint(self.right_arm_upper_body, self.body, (-arm_size[0] / 2, 0), (25, 30))
        self.ra_motor = pymunk.SimpleMotor(self.body, self.right_arm_upper_body, 0)

        thigh_size = (30, 60)
        self.lu_shape = pymunk.Poly.create_box(None, thigh_size)
        lu_moment = pymunk.moment_for_poly(moment, self.lu_shape.get_vertices())
        self.lu_body = pymunk.Body(moment, lu_moment)
        self.lu_body.position = (self.body.position.x-20, self.body.position.y-75)
        self.lu_shape.body = self.lu_body
        self.lu_shape.friction = friction
        self.lu_joint = pymunk.PivotJoint(self.lu_body, self.body, (0, thigh_size[1] / 2), (-20, -50))
        self.lu_motor = pymunk.SimpleMotor(self.body, self.lu_body, 0)

        self.ru_shape = pymunk.Poly.create_box(None, thigh_size)
        ru_moment = pymunk.moment_for_poly(moment, self.ru_shape.get_vertices())
        self.ru_body = pymunk.Body(moment, ru_moment)
        self.ru_body.position = (self.body.position.x+20, self.body.position.y - 75)
        self.ru_shape.body = self.ru_body
        self.ru_shape.friction = friction
        self.ru_joint = pymunk.PivotJoint(self.ru_body, self.body, (0, thigh_size[1] / 2), (20, -50))
        self.ru_motor = pymunk.SimpleMotor(self.body, self.ru_body, 0)

        leg_size = (20, 70)
        self.ld_shape = pymunk.Poly.create_box(None, leg_size)
        ld_moment = pymunk.moment_for_poly(moment, self.ld_shape.get_vertices())
        self.ld_body = pymunk.Body(moment, ld_moment)
        self.ld_body.position = (self.lu_body.position.x, self.lu_body.position.y - 65)
        self.ld_shape.body = self.ld_body
        self.ld_shape.friction = friction
        self.ld_joint = pymunk.PivotJoint(self.ld_body, self.lu_body, (0, leg_size[1] / 2), (0, -thigh_size[1] / 2))
        self.ld_motor = pymunk.SimpleMotor(self.lu_body, self.ld_body, 0)

        self.rd_shape = pymunk.Poly.create_box(None, leg_size)
        rd_moment = pymunk.moment_for_poly(moment, self.rd_shape.get_vertices())
        self.rd_body = pymunk.Body(moment, rd_moment)
        self.rd_body.position = (self.ru_body.position.x, self.ru_body.position.y - 65)
        self.rd_shape.body = self.rd_body
        self.rd_shape.friction = friction
        self.rd_joint = pymunk.PivotJoint(self.rd_body, self.ru_body, (0, leg_size[1] / 2), (0, -thigh_size[1] / 2))
        self.rd_motor = pymunk.SimpleMotor(self.ru_body, self.rd_body, 0)

        foot_size = (45, 20)
        self.lf_shape = pymunk.Poly.create_box(None, foot_size)
        rd_moment = pymunk.moment_for_poly(moment, self.lf_shape.get_vertices())
        self.lf_body = pymunk.Body(moment, rd_moment)
        self.lf_body.position = (self.ld_body.position.x + foot_size[0]/6, self.ld_body.position.y - (foot_size[1]*2))
        self.lf_shape.body = self.lf_body
        self.lf_shape.friction = friction
        self.lf_shape.elasticity = 0.1
        self.lf_joint = pymunk.PivotJoint(self.ld_body, self.lf_body, (-5, -leg_size[1] / 2), (-foot_size[0]/2 + 10, foot_size[1]/2))
        self.lf_motor = pymunk.SimpleMotor(self.ld_body, self.lf_body, 0)

        self.rf_shape = pymunk.Poly.create_box(None, foot_size)
        rd_moment = pymunk.moment_for_poly(moment, self.rf_shape.get_vertices())
        self.rf_body = pymunk.Body(moment, rd_moment)
        self.rf_body.position = (self.rd_body.position.x + foot_size[0]/6, self.rd_body.position.y - (foot_size[1]*2))
        self.rf_shape.body = self.rf_body
        self.rf_shape.friction = friction
        self.rf_shape.elasticity = 0.1
        self.rf_joint = pymunk.PivotJoint(self.rd_body, self.rf_body, (-5, -leg_size[1] / 2), (-foot_size[0]/2 + 10, foot_size[1]/2))
        self.rf_motor = pymunk.SimpleMotor(self.rd_body, self.rf_body, 0)

        space.add(self.body, self.shape, self.head_body, self.head_shape, self.head_joint, self.head_joint2)
        space.add(self.left_arm_upper_body, self.left_arm_upper_shape, self.left_arm_upper_joint, self.la_motor)
        space.add(self.right_arm_upper_body, self.right_arm_upper_shape, self.right_arm_upper_joint, self.ra_motor)
        space.add(self.lu_body, self.lu_shape, self.lu_joint, self.lu_motor)
        space.add(self.ru_body, self.ru_shape, self.ru_joint, self.ru_motor)
        space.add(self.ld_body, self.ld_shape, self.ld_joint, self.ld_motor)
        space.add(self.rd_body, self.rd_shape, self.rd_joint, self.rd_motor)
        space.add(self.lf_body, self.lf_shape, self.lf_joint, self.lf_motor)
        space.add(self.rf_body, self.rf_shape, self.rf_joint, self.rf_motor)

        shape_filter = pymunk.ShapeFilter(group=1)
        self.shape.filter = shape_filter
        self.head_shape.filter = shape_filter
        self.left_arm_upper_shape.filter = shape_filter
        self.right_arm_upper_shape.filter = shape_filter
        self.lu_shape.filter = shape_filter
        self.ru_shape.filter = shape_filter
        self.ld_shape.filter = shape_filter
        self.rd_shape.filter = shape_filter
        self.lf_shape.filter = shape_filter
        self.rf_shape.filter = shape_filter

        self.lu_flag = False
        self.ld_flag = False
        self.ru_flag = False
        self.rd_flag = False
        self.la_flag = False
        self.ra_flag = False
        self.lf_flag = False
        self.rf_flag = False

    def get_data(self):
        lu = ((360 - math.degrees(self.lu_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        ld = ((360 - math.degrees(self.ld_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        lf = ((360 - math.degrees(self.lf_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        ru = ((360 - math.degrees(self.ru_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        rd = ((360 - math.degrees(self.rd_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        rf = ((360 - math.degrees(self.rf_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        la = ((360 - math.degrees(self.left_arm_upper_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        ra = ((360 - math.degrees(self.right_arm_upper_body.angle)) - (360 - math.degrees(self.body.angle))) / 360.0
        return ru, rd, lu, ld, la, ra, lf, rf
        #removed self.body,angle

    def update(self):
        #lu
        self.lu_flag = False
        if (360 - math.degrees(self.lu_body.angle)) - (360 - math.degrees(self.body.angle)) >= 90 and self.lu_motor.rate > 0:
            self.lu_motor.rate = 0
            self.lu_flag = True
        elif (360 - math.degrees(self.lu_body.angle)) - (360 - math.degrees(self.body.angle)) <= -90 and self.lu_motor.rate < 0:
            self.lu_motor.rate = 0
            self.lu_flag = True

        #ld
        self.ld_flag = False
        if (360 - math.degrees(self.ld_body.angle)) - (360 - math.degrees(self.lu_body.angle)) >= 90 and self.ld_motor.rate > 0:
            self.ld_motor.rate = 0
            self.ld_flag = True
        elif (360 - math.degrees(self.ld_body.angle)) - (360 - math.degrees(self.lu_body.angle)) <= -90 and self.ld_motor.rate < 0:
            self.ld_motor.rate = 0
            self.ld_flag = True

        #ru
        self.ru_flag = False
        if (360 - math.degrees(self.ru_body.angle)) - (360 - math.degrees(self.body.angle)) >= 90 and self.ru_motor.rate > 0:
            self.ru_motor.rate = 0
            self.ru_flag = True
        elif (360 - math.degrees(self.ru_body.angle)) - (360 - math.degrees(self.body.angle)) <= -90 and self.ru_motor.rate < 0:
            self.ru_motor.rate = 0
            self.ru_flag = True

        #rd
        self.rd_flag = False
        if (360 - math.degrees(self.rd_body.angle)) - (360 - math.degrees(self.ru_body.angle)) >= 90 and self.rd_motor.rate > 0:
            self.rd_motor.rate = 0
            self.rd_flag = True
        elif (360 - math.degrees(self.rd_body.angle)) - (360 - math.degrees(self.ru_body.angle)) <= -90 and self.rd_motor.rate < 0:
            self.rd_motor.rate = 0
            self.rd_flag = True

        #lf
        self.lf_flag = False
        if (360 - math.degrees(self.lf_body.angle)) - (360 - math.degrees(self.ld_body.angle)) >= 90 and self.lf_motor.rate > 0:
            self.lf_motor.rate = 0
            self.lf_flag = True
        elif (360 - math.degrees(self.lf_body.angle)) - (360 - math.degrees(self.ld_body.angle)) <= -45 and self.lf_motor.rate < 0:
            self.lf_motor.rate = 0
            self.lf_flag = True

        #rf
        self.rf_flag = False
        if (360 - math.degrees(self.rf_body.angle)) - (360 - math.degrees(self.rd_body.angle)) >= 90 and self.rf_motor.rate > 0:
            self.rf_motor.rate = 0
            self.rf_flag = True
        elif (360 - math.degrees(self.rf_body.angle)) - (360 - math.degrees(self.rd_body.angle)) <= -45 and self.rf_motor.rate < 0:
            self.rf_motor.rate = 0
            self.rf_flag = True

    def add_land(self,space):
        body = pymunk.Body(body_type=pymunk.Body.STATIC)
        body.position = (0, 100)
        land = pymunk.Segment(body, (0, 50), (99999, 50), 10)
        land.friction = 1.0
        land.elasticity = 0.1
        space.add(body, land)

        body_2 = pymunk.Body(body_type=pymunk.Body.STATIC)
        body_2.position = (target, -50)
        t_block = pymunk.Segment(body_2, (0, 100), (20, 100), 10)
        space.add(body_2, t_block)

class Walker(Env):
    metadata = {'render.modes': ['human']}

    def __init__(self):
        self.action_space = MultiDiscrete([3]*8)
        self.observation_space = Box(-20,20,[8])
        self.viewer = None
        self.last_horizontal_pos = 0
        self.last_vertical_pos = 0
        self.step_nr = 0
        self.max_step = 900

    def check_fall(self):

      if self.robot.body.position[1] < self.initial_height-50:
            return True
      if self.robot.body.position[0] < 0 or self.robot.body.position[0] > screen_width:
            return True
      return False
    def calculate_reward(self):
        shape = self.space.shapes[-2]
        contact_lf = len(self.robot.lf_shape.shapes_collide(b=shape).points)
        contact_rf = len(self.robot.rf_shape.shapes_collide(b=shape).points)
        if (self.robot.body.position[0] - self.last_horizontal_pos) > 1:
            reward = 10
        elif 1 > (self.robot.body.position[0] - self.last_horizontal_pos) > -1:
            reward = 9
        elif (self.robot.body.position[0] - self.last_horizontal_pos) < -1:
            reward = 8
        if not contact_lf and not contact_rf:
                reward -= 6
        return reward

    def check_complete(self):
        if self.robot.body.position[0] >= target: # 500 is the position of the target
            return True

    def step(self, actions):
        self.step_nr += 1
        actions = [(a-1)*2 for a in actions]
        self.robot.ru_motor.rate = actions[0]
        self.robot.rd_motor.rate = actions[1]
        self.robot.lu_motor.rate = actions[2]
        self.robot.ld_motor.rate = actions[3]
        self.robot.la_motor.rate = actions[4]
        self.robot.ra_motor.rate = actions[5]
        self.robot.lf_motor.rate = actions[6]
        self.robot.rf_motor.rate = actions[7]

        self.robot.update()
        self.space.step(1/50)

        done = False
        reward = self.calculate_reward()

        if self.check_fall():
            done = True
            reward = 0
        if self.check_complete():
            done = True
            reward = 16
        if self.step_nr >= self.max_step:
            done = True

        info = {}
        observation = self.robot.get_data()

        self.last_horizontal_pos = self.robot.body.position[0]
        self.last_vertical_pos = self.robot.body.position[1]

        truncated = False

        return(
        observation,
        reward,
        done,
        truncated,
        info)

    def render(self, mode='human', close=False):
        if self.viewer is None:
            self.viewer = pygame.init()
            pygame_util.positive_y_is_up = True
            self.clock = pygame.time.Clock()
            self.screen = pygame.display.set_mode((screen_width, screen_height))
        self.draw_options = pygame_util.DrawOptions(self.screen)
        self.screen.fill((255, 255, 255))
        self.space.debug_draw(self.draw_options)
        pygame.display.flip()
        self.clock.tick(25)
        return pygame.surfarray.array3d(self.screen)

    def reset(self):
        self.step_nr = 0
        self.space = pymunk.Space()
        self.space.gravity = (0.0, -990)
        self.robot = Robot(self.space)
        self.robot.add_land(self.space)
        self.initial_height = self.robot.body.position[1]
        self.initial_horizontal = self.robot.body.position[0]
        observation = self.robot.get_data()
        return(observation,None)


from heapq import heapify, heappop, heappush
import collections

'''
Time: O(n) + O(nlogk)
Space: O(n)
'''
class Solution:

    def __init__(self, nums, k):
        self.heap = []
        self.k = k
        self.nums = nums

    def topk(self):
        freq = collections.defaultdict(int)
        for num in self.nums:
            freq[num] += 1
        print(freq)
        for key, val in freq.items():
            heappush(self.heap, (val, key))
            if len(self.heap) > self.k:
                heappop(self.heap)
        return [x[1] for x in self.heap]

if __name__ == '__main__':
    s = Solution([1,1,2,2,2,2,5,5,5,5,5,5,3,3,3], 2)
    print(s.topk())


import numpy as np
import optuna
import pandas as pd
import xgboost as xgb
from sklearn.metrics import f1_score, precision_score
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import recall_score
from sklearn.metrics import accuracy_score

def objective(trial, df, y):
    params = {
        'max_depth': trial.suggest_int('max_depth', 3, 10),
        'subsample': trial.suggest_uniform('subsample', 0.5, 1),
        'learning_rate': trial.suggest_uniform('learning_rate', 1e-5, 1),
        'gamma': trial.suggest_loguniform('gamma', 1e-8, 1e2),
        'lambda': trial.suggest_loguniform('lambda', 1e-8, 1e2),
        'alpha': trial.suggest_loguniform('alpha', 1e-8, 1e2)
    }
    kf = StratifiedKFold(n_splits=5, random_state=15, shuffle=True)

    y_hats = []
    y_tests = []

    for train_index, test_index in kf.split(df, y):
        X_train, X_test = df.iloc[train_index], df.iloc[test_index]
        y_train, y_test = y.iloc[train_index], y.iloc[test_index]
        model = xgb.XGBClassifier(**params)
        model.fit(X_train, y_train)
        y_hats += model.predict(X_test).tolist()
        y_tests += y_test.tolist()

    return f1_score(y_tests, y_hats)

X = pd.read_csv("X.csv")
X = X.set_index("token_address")
labels = pd.read_csv("Labelling/labeled_list.csv", index_col="token_address")
X = X.merge(labels['label'], left_index=True, right_index=True)
X = X.reset_index()
df = X.drop_duplicates(subset=['token_address'])
X = X.set_index("token_address")
lock_features = pd.read_csv("../data/token_lock_features.csv", index_col="token_address")
X = X.merge(lock_features, how='left', left_index=True, right_index=True)
optuna.logging.set_verbosity(optuna.logging.WARNING)

ids = []
total_probs = []
total_targets = []

skfolds = StratifiedKFold(n_splits=5, shuffle=True, random_state=2)
for fold, (t, v) in enumerate(skfolds.split(df['token_address'], df['label'])):

    ids_train = df['token_address'].iloc[t]
    df_train = X.loc[ids_train]
    ids_test = df['token_address'].iloc[v]
    df_test = X.loc[ids_test]

    X_train, y_train = df_train.drop(["label", "eval_block"], axis=1), df_train['label']
    X_test, y_test = df_test.drop(["label", "eval_block"], axis=1), df_test['label']

    columns = X_train.columns

    func = lambda trial: objective(trial, X_train.copy(), y_train.copy())
    study = optuna.create_study(direction='maximize')
    study.optimize(func, n_trials=100)

    model = xgb.XGBClassifier(**study.best_params)
    model.fit(X_train, y_train)
    preds_scorings = model.predict_proba(X_test)[:, 1]
    preds = model.predict(X_test)
    f1 = f1_score(y_test, preds)
    sensibilitat = recall_score(y_test, preds)
    precisio = precision_score(y_test, preds)
    accuracy = accuracy_score(y_test, preds)
    print("{},{},{},{},{}".format(accuracy, sensibilitat, precisio, f1, fold))
    ids += X_test.index.tolist()
    total_probs += preds.tolist()
    total_targets += y_test.tolist()

final_df = pd.DataFrame({'ids': ids, 'Pred': total_probs, 'Label': total_targets})\
    .to_csv("Results_XGBoost.csv", index=False)


# script to create scatter plot for mean intensity ranking in three emotion
# categories. refer to readme for more information about survey and ranking
# task.

# 18 November 2018, Pulkit Singh

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

#----------------------------------------------------------------------------#

# Sadness Category

sad_labels = ['displeased', 'unhappy', 'sad', 'dejected', 'miserable',
			  'heartbroken', 'depressed']
sad_means  = [1.38, 1.94, 2.56, 3.44, 4.22, 4.33, 4.38]
sad_x = list(range(1, len(sad_means) + 1))

# plotting means
sad_plt, sad_ax = plt.subplots(figsize=(9, 9))
sad_ax = sns.scatterplot(sad_x, sad_means, marker="")

# annotating data points
for i in range(len(sad_means)):
	sad_ax.annotate(sad_labels[i], (sad_x[i], sad_means[i]))

plt.xlabel("Words in 'sadness' category")
plt.ylabel("Average intensity ranking \n")
plt.title("Average intensity ranking of 'sadness' category words")
plt.xlim((0, 10))
plt.ylim((1, 5))
plt.xticks([], [])
plt.savefig("IR_mean_sadness.png")

#----------------------------------------------------------------------------#

# Anger Category
anger_labels = ['irked', 'annoyed', 'irritated', 'mad', 'incensed',
			  'angry', 'infuriated', 'enraged']
anger_means  = [1.83, 1.94, 2.44, 2.88, 3.22, 3.38, 4.55, 4.72]
anger_x = list(range(1, len(anger_means) + 1))

# plotting means
anger_plt, anger_ax = plt.subplots(figsize=(9, 9))
anger_ax = sns.scatterplot(anger_x, anger_means, marker="")

# annotating data points
for i in range(len(anger_means)):
	anger_ax.annotate(anger_labels[i], (anger_x[i], anger_means[i]))

plt.xlabel("Words in 'anger' category")
plt.ylabel("Average intensity ranking \n")
plt.title("Average intensity ranking of 'anger' category words")
plt.xlim((0, 10))
plt.ylim((1, 5))
plt.xticks([], [])
plt.savefig("IR_mean_anger.png")

#----------------------------------------------------------------------------#

# Fear Category

fear_labels = ['afraid', 'scared', 'intimidated', 'alarmed', 'distressed',
			   'frightened', 'horrified', 'terrified']
fear_means  = [2.27, 2.27, 2.33, 2.38, 2.66, 3.05, 4.38, 4.55]
fear_x = list(range(1, len(fear_means) + 1))

# plotting means
fear_plt, fear_ax = plt.subplots(figsize=(9, 9))
fear_ax = sns.scatterplot(fear_x, fear_means, marker="")

# annotating data points
for i in range(len(fear_means)):
	fear_ax.annotate(fear_labels[i], (fear_x[i], fear_means[i]))

plt.xlabel("Words in 'fear' category")
plt.ylabel("Average intensity ranking \n")
plt.title("Average intensity ranking of 'fear' category words")
plt.xlim((0, 10))
plt.ylim((1, 5))
plt.xticks([], [])
plt.savefig("IR_mean_fear.png")

#----------------------------------------------------------------------------#

Emotion Word Survey/Word Intensity Ranking/Scatter plots mean/intensityRanking_mean.py


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
ServiceInterval
Application implementation classes.
"""
from copy import copy
from datetime import date, timedelta
from numbers import Number
import os
import pickle
import re
import warnings

__author__ = 'Don D.S.'

# Version of ServiceInterval.
VERSION = (1, 0)

class Operation(object):
    """ Represents service operation.

    Examples of using:
    # Create an operation type.
    >>> oil_change = Operation("Changing the oil: engine",
    ...                        interval_km=10000,
    ...                        interval_year=1)

    # Create done-operation copy from current operation type.
    >>> oil_changed = oil_change.done(
    ...     km=9842,
    ...     date=date(2015, 12, 5),
    ...     comment="Price: 4000 RUR")

    # Create readable form.
    >>> print(oil_changed)
    2015-12-05 / 9842.0 km
    Changing the oil: engine
    Every 1.0 year(s) or 10000.0 km
    Price: 4000 RUR
    >>> print(oil_change)
    Changing the oil: engine.
    Every 1.0 year(s) or 10000.0 km

    # Create representative form.
    >>> repr(oil_change)
    'Operation(Changing the oil: engine, interval_km=10000.0, interval_year=1.0)'

    """

    def __init__(self, label, interval_km=0, interval_year=0, interval_month=0):
        """ Create service operation type.

        Default intervals value is 0. It means that operation is non-periodic.

        :param label:          operation label or description
        :param interval_km:    operation interval by vehicle haul, km
        :param interval_year:  operation interval time, years
        :param interval_month: operation interval time, months
        """
        super().__init__()
        # Initialize default values.
        self._label = ""
        self._interval_time = timedelta()
        self._interval_km = 0
        # For done copy of this operation type.
        self._done_at_km = 0
        self._done_at_date = None
        # Additional information (price, parts item numbers).
        self.comment = ""
        # Initialize private flag.
        self._is_done = False   # default operation state: not done.
        # Set up values for current operation instance.
        self.label = label
        self.interval_time = timedelta(
            days=365 * interval_year + 30.4 * interval_month)
        self.interval_km = interval_km

    def done(self, km=0, date=None, comment=""):
        # Create a copy of this operation, that has been done and return it.
        done = copy(self)
        done.done_at_km = km
        done.done_at_date = date
        done.comment = comment
        done._is_done = True
        return done

    def undo(self):
        # Clear information about operation completion
        self.done_at_km = 0
        self._done_at_date = None
        self.comment = ""
        self._is_done = False

    @property
    def is_done(self):
        # Flag: is operation has been done?
        return self._is_done

    @property
    def is_periodic(self):
        return self.interval_km != 0

    @property
    def label(self):
        return self._label

    @label.setter
    def label(self, new_title):
        if isinstance(new_title, str):
            self._label = new_title
        else:
            raise TypeError("OperationType title must be a text string.")

    @property
    def interval_time(self):
        return self._interval_time

    @interval_time.setter
    def interval_time(self, interval):
        if not isinstance(interval, timedelta):
            raise TypeError("Time must be represented as <datetime.timedelta>"
                            " class instance.")
        self._interval_time = interval

    @property
    def interval_km(self):
        return self._interval_km

    @interval_km.setter
    def interval_km(self, new_interval):
        try:
            new_interval = float(new_interval)
        except ValueError:
            raise TypeError("Interval must be a numeric type or string number.")
        if new_interval < 0:
            raise ValueError("Operation interval must be positive. "
                             "Received value " + str(new_interval))
        self._interval_km = new_interval

    @property
    def done_at_km(self):

        return self._done_at_km

    @done_at_km.setter
    def done_at_km(self, new_km):
        try:
            new_km = float(new_km)
        except ValueError:
            raise TypeError(
                "Haul value must be a numeric type or string number.")
        # Haul can be negative if this field used to show relative distance
        # from planned maintenance.
        # if new_km < 0 and not relative:
        #     raise ValueError("Haul value must be positive. "
        #                      "Received value " + str(new_km))
        self._done_at_km = new_km

    @property
    def done_at_date(self):
        return self._done_at_date

    @done_at_date.setter
    def done_at_date(self, new_date):
        if isinstance(new_date, date):
            self._done_at_date = new_date
        else:
            raise TypeError("Date must be a <datetime.date> class instance.")

    def __eq__(self, other):
        return self.label == other.label and self.done_at_km == other.done_at_km

    def __ne__(self, other):
        return not self == other

    def __lt__(self, other):
        if self.label != other.label:
            TypeError("unorderable operations with different labels")
        return self.done_at_km < other.done_at_km

    def __le__(self, other):
        if self.label != other.label:
            TypeError("unorderable operations with different labels")
        return self.done_at_km <= other.done_at_km

    def __gt__(self, other):
        if self.label != other.label:
            TypeError("unorderable operations with different labels")
        return self.done_at_km >= other.done_at_km

    def __ge__(self, other):
        if self.label != other.label:
            TypeError("unorderable operations with different labels")
        return self.done_at_km > other.done_at_km

    def __repr__(self):
        if self.is_done:
            return "Operation({0}, interval_km={1}, interval_year={2}).done("\
                   "km={3}, date={4}, comment={5})".format(
                       self.label, self.interval_km, self.interval_time.days/365,
                       self.done_at_km, self.done_at_date, self.comment)
        else:
            return "Operation({0}, interval_km={1}, interval_year={2})".format(
                self.label, self.interval_km, self.interval_time.days/365)

    def __str__(self):
        """ !!! ATTENTION !!!
        If you change this method, you also need to change OperationList.load()
        parsing method. This is bad idea.
        """
        interval_months = round(self.interval_time.days/(365/12))

        if self.is_done:
            return "{date} / {km} km\n" \
                   "{label}\n" \
                   "Every {prd_time} or {prd_km} km\n"\
                   "{comment}".format(
                       label=self.label,
                       date=self.done_at_date.isoformat(),
                       km=self.done_at_km,
                       comment=self.comment,
                       prd_time=
                       str(interval_months) + " month(s)" if interval_months < 12
                       else str(round(interval_months/12, 1)) + " year(s)",
                       prd_km=self.interval_km)
        else:

            return "{label}.\nEvery {prd_time} or {prd_km} km".format(
                label=self.label,
                prd_time=str(interval_months) + " month(s)" if interval_months < 12
                else
                str(round(interval_months/12, 1)) + " year(s)",
                prd_km=self.interval_km)

class OperationsList(list):
    """ List inheritance with additional methods.
    Added save(), load() methods.

    Example of using:
    >>> operations = OperationsList([
    ...     Operation("Changing the oil: engine", 1, 10000),
    ...     Operation("Changing the oil: gearbox", 3, 45000)])

    >>> operations.save("doctest.txt")
    """
    def __init__(self, seq=()):
        super().__init__(seq)

    def save(self, file):
        """ Create human-readable text file from list
        """
        with open(file, 'w') as fh:
            for operation in self:
                comm = operation.comment
                # Remove empty string to prevent parsing errors on import
                comm.replace('\n\n', '\n')
                operation.comment = comm
                print(operation, end="\n\n", file=fh)

    @staticmethod
    def load(file):
        """ Create <OperationList> class instance from file previously created
        by self.save() or created manually with the same formatting.

        # Create test operation type.
        >>> oil_change = Operation("Changing the oil: engine",
        ...                        interval_km=10000,
        ...                        interval_year=1)

        # Create done-operation copy from current test operation type.
        >>> oil_changed = oil_change.done(
        ...     km=9842,
        ...     date=date(2015, 12, 5),
        ...     comment="Price: 4000 RUR")

        # Format for operation that has been done:
        >>> print(oil_changed)
        2015-12-05 / 9842.0 km
        Changing the oil: engine
        Every 1.0 year(s) or 10000.0 km
        Price: 4000 RUR
        >>> OperationsList([oil_changed]).save('doctest.txt')

        # Doctest for reading and parsing operation that has been done:
        >>> print(OperationsList.load('doctest.txt'))
        [Operation(Changing the oil: engine, interval_km=10000.0, interval_year=1.0).done(km=9842.0, date=2015-12-05, comment=)]

        # Format for operation that hasn't been done:
        >>> print(oil_change)
        Changing the oil: engine.
        Every 1.0 year(s) or 10000.0 km
        >>> OperationsList([oil_change]).save('doctest.txt')

        # Doctest for reading and parsing operation that hasn't been done:
        >>> print(OperationsList.load('doctest.txt'))
        [Operation(Changing the oil: engine., interval_km=10000.0, interval_year=1.0)]
        """
        # Regular expression that can detect, that operation has been done
        re_done = re.compile(
            r"(?P<yyyy>[0-9]{4})-(?P<mm>[0-9]{2})-(?P<dd>[0-9]{2})\s/\s(?P<km>[0-9.]+)\skm")
        # Regular expression that can detect operation intervals line
        re_interval = re.compile(
            r"Every\s(?P<time>[0-9.]+)\s(?P<year_or_mon>[a-z()]+)\sor\s(?P<km>[0-9.]+)\skm")
        # Output variable
        ops = OperationsList()
        # Operation arguments
        label = None
        interval_km = None
        interval_year = None
        interval_month = None
        done_at_km = None
        done_at_date = None
        comment = ""
        # Operation done flag
        is_done = False
        # Control line numbers
        nline_done_first = None
        # Initialize storage
        line_previous = ""
        with open(file, 'r') as fh:
            for num, line in enumerate(fh):
                line = line.strip('\n')
                # At first line and after every empty line...
                if line == "":
                    # ...append previous operation to list (if exist)
                    if label:  # (check by label - it is necessary argument)
                        op = Operation(label,
                                       interval_km,
                                       interval_year,
                                       interval_month)
                        if is_done:
                            op = op.done(done_at_km,
                                         done_at_date,
                                         comment)
                        ops.append(op)
                    # ... and reset operation args, flag, nlines - anyway
                    # Operation arguments
                    label = None
                    interval_km = None
                    interval_year = None
                    interval_month = None
                    done_at_km = None
                    done_at_date = None
                    comment = ""
                    # Operation done flag
                    is_done = False
                    # Control line numbers
                    nline_done_first = None
                # Match with done-type operation
                match_done = re_done.search(line)
                if match_done:
                    is_done = True
                    done_at_km = int(float(match_done.group('km')))
                    done_at_date = date(int(match_done.group('yyyy')),
                                        int(match_done.group('mm')),
                                        int(match_done.group('dd')))
                    nline_done_first = num
                # Next line after match_done line - is label
                if is_done and num - 1 == nline_done_first:
                    label = line
                # Check for intervals line
                match_interval = re_interval.search(line)
                if match_interval:
                    year_or_mon = match_interval.group('year_or_mon')
                    if year_or_mon == "year(s)":
                        interval_year = float(match_interval.group('time'))
                        interval_month = 0
                    elif year_or_mon == "month(s)":
                        interval_year = 0
                        interval_month = float(match_interval.group('time'))
                    else:
                        raise ValueError("Unable to parse line: \n" + line)
                    interval_km = int(float(match_interval.group('km')))

                    if not is_done:
                        label = line_previous
                # Next line after label - is intervals. Already parsed.
                # Next line after intervals - is comment
                if is_done and num - 3 == nline_done_first:
                    if comment:
                        comment += "\n" + line
                    else:
                        comment = line
                    # Comment was the last part.
                    # For multiline comments...
                    nline_done_first += 1
                # Keep previous line. We can detect operation that hasn't been
                # done only from second string. In this case previous line will
                # be used as label.
                line_previous = line
        return ops

class VehicleLogBook(object):
    """ Represents storage of service operations for vehicle

    Vehicle identified by text label and production date

    WARNING!!! If you add some methods, do not forget to update
    self._changed field, that shows that object contains unsaved changes!

    Examples of using:
    # Without periodical operations catalogue.
    >>> car = VehicleLogBook(
    ...     "Hyundai Getz",
    ...     date(year=2006, month=11, day=30))

    # Or with catalogue.
    >>> catalogue = OperationsList([
    ...     Operation("Changing the oil: engine", 1, 10000),])
    >>> car = VehicleLogBook(
    ...     "Hyundai Getz",
    ...     date(year=2006, month=11, day=30),
    ...     catalogue)

    # Add complete operation.
    # ...Prepare operation type.
    >>> oil_change = Operation("Changing the oil: engine",
    ...                        interval_km=10000,
    ...                        interval_year=1)

    # ...Prepare operation instance.
    >>> oil_changed = oil_change.done(
    ...     km=98042,
    ...     date=date(2015, 12, 5),
    ...     comment="Price: 4000 RUR")

    # ...Add operation to log.
    >>> car.add_operation_to_log(oil_changed)

    # Make maintenance plan.
    >>> car.make_maintenance_plan()
    [Operation(Changing the oil: engine, interval_km=10000.0, interval_year=1.0).done(km=108042.0, date=2016-12-04, comment=)]

    # Add new periodic operation to catalogue.
    # ...already exist in catalogue
    >>> car.add_operation_to_cat(oil_change)

    # ...new operation
    >>> oil_change_gb = Operation("Changing the oil: gearbox",
    ...                        interval_km=45000,
    ...                        interval_year=3)

    >>> car.add_operation_to_cat(oil_change_gb)

    # Serialize (save) class instance to file.
    >>> car.save("doctest")

    # Deserialize (load) class instance from file
    >>> print(VehicleLogBook.load("doctest"))
    [Operation(Changing the oil: engine, interval_km=10000.0, interval_year=1.0).done(km=98042.0, date=2015-12-05, comment=Price: 4000 RUR)]

    """
    # Extension for files of class serialization
    _extension = ".sif"

    def __init__(self, label, production_date, operations_cat=tuple()):
        """
        :param label:            vehicle text identifier
        :param production_date:  vehicle production date as <datetime.date>
                                 class instance
        :param operations_cat:   catalogue of all periodical operations types
                                 (iterable with items - instances of <Operation>
                                 class)
        """
        super().__init__()
        # Version identifier
        self._version = VERSION

        self._production_date = None
        self._filename = ""  # filename where object saved
        # Car label
        self._label = label
        self.production_date = production_date
        # Car haul today
        self._haul = 0
        # List of all done operations for keeping history.
        self._operations_log = OperationsList()
        # Catalogue of all periodical operations types.
        # keys - operation labels; values - <Operation> class instances.
        self._operations_cat = dict()
        for op in operations_cat:
            if op.interval_time == 0 and op.interval_km == 0:
                raise TypeError(
                    "Operation <{}> is not periodic.".format(op.label) +
                    "\nUnable to add non-periodic operation to the catalogue "
                    "of periodic operations.")
            self._operations_cat[op.label] = op
        self._modified = False  # WARNING!!! False in spite of assignation
        # label and production_date during call __init___(). Becomes True after
        # assignment this fields through properties.

    @property
    def operations_log(self):
        return self._operations_log

    @property
    def operations_cat(self):
        return self._operations_cat

    @property
    def haul(self):
        return self._haul

    @haul.setter
    def haul(self, new_haul):
        if isinstance(new_haul, str) and new_haul.isdigit():
            new_haul = float(new_haul)
        if isinstance(new_haul, Number):
            self._haul = new_haul
            self._modified = True
        else:
            raise TypeError(
                "Haul value must be a Number (int, float, ...) or digit-string")

    @property
    def extension(self):
        return self._extension

    @classmethod
    def get_extension(cls):
        return cls._extension

    @property
    def filename(self):
        return self._filename

    @property
    def is_modified(self):
        return self._modified

    @property
    def label(self):
        return self._label

    @label.setter
    def label(self, new_label):
        if self._label != new_label:
            self._modified = True
            self._label = new_label

    @property
    def production_date(self):
        return self._production_date

    @production_date.setter
    def production_date(self, new_prod_date):
        # Car production date.
        if isinstance(new_prod_date, date):
            if new_prod_date != self._production_date:
                self._modified = True
                self._production_date = new_prod_date
        else:
            raise TypeError("Argument <new_prod_date> must be an instance "
                            "of <datetime.date> type.")

    def op_label_replace(self, old, new):
        """Rename operation
        - reAdd periodic operation to catalogue with new label
        - and rename old operations in log with label same as old label

        :param old:  old label string, that must be replaced by new
        :param new:  new label of operation
         """
        if old == new:
            return

        self._modified = True
        for op in self._operations_log:
            # Rename operations with  old name to new
            if op.label == old:
                op.label = new
        if old in self._operations_cat:
            # ReAdd with new label under new label-keyword
            op = self._operations_cat[old]
            self._operations_cat.pop(old)
            op.label = new
            self.add_operation_to_cat(op)

    def get_all_oper_labels(self):
        """ Get set of all known operation labels
        :return: list of strings
        """
        labels = set()
        labels = labels.union([x.label for x in self._operations_log])
        labels = labels.union([x for x in self._operations_cat.keys()])
        labels = list(labels)
        labels.sort()
        return labels

    def get_periodic(self, label):
        """ Find periodic operation with the same label in periodic
        operations catalogue

        :param label:  String of operation label
        :return:       Operation instance or None (if no same label)
        """
        if label in self._operations_cat:
            return self._operations_cat[label]
        else:
            return None

    def add_operation_to_log(self, operation):
        if not isinstance(operation, Operation):
            raise TypeError("Argument <operation> must be an instance "
                            "of <Operation> type.")
        if not operation.is_done:
            # It matter that operation has never been done.
            raise ValueError("Operation date and haul not specified. "
                             "Unable to add operation that has never been "
                             "done.")
        self._modified = True
        # Put operation to the log-list.
        self._operations_log.append(operation)
        self._operations_log.sort(key=lambda x: x.done_at_km)
        # If it is periodical operation
        if operation.is_periodic:
            if operation.label in self._operations_cat:
                # Update last completion time for this operation
                # if that is newer than last.
                operation_last = self._operations_cat[operation.label]
                if operation > operation_last:
                    self._operations_cat[operation.label] = operation
            else:
                # Add operation to periodic operations catalogue
                self.add_operation_to_cat(operation)

    def add_operation_to_cat(self, operation):
        if operation.is_periodic \
                and operation.label not in self._operations_cat.keys():
            self._modified = True
            # Default operation last completion date/haul
            last_date = self._production_date
            last_km = 0
            # Lookup operations log for a last operation with the same label
            same_operations = list(filter(lambda x: x.label == operation.label,
                                          self._operations_log))
            if len(same_operations) > 0:
                last_operation = max(same_operations)
                last_date = last_operation.done_at_date
                last_km = last_operation.done_at_km
            # Set operation last completion
            operation = operation.done(last_km, last_date)
            # Add operation to periodic operations catalogue
            self._operations_cat[operation.label] = operation

    def clear_log(self):
        self._modified = True
        # Clear log of produced operations.
        self._operations_log.clear()
        # Clear information about last operation completion
        for operation in self._operations_cat.values():
            operation.undo()

    def clear_all(self):
        self._modified = True
        # Clear operations log and peridic operations catalogue.
        self._operations_log.clear()
        self._operations_cat.clear()

    def remove_from_log(self, operations):
        """ Remove specified operation from oeprations list
        :param operations: list of operations
        """
        for op in operations:
            self._operations_log.remove(op)
        self._modified = True

    def remove_from_cat(self, operations):
        for op in operations:

            # Remove all operations in log with the same labels.
            # ... get all indexes of items with the same lables
            inds = [ind for ind, op_in_log
                    in enumerate(self._operations_log)
                    if op_in_log.label == op.label]
            # we need to start removing from the end to prevent shift of indexes
            # after removing elements
            inds.reverse()
            # ... pop all items with this indexes
            for ind in inds:
                self._operations_log.pop(ind)

            # Also remove operation from catalogue.
            del self._operations_cat[op.label]

        self._modified = True

    def make_maintenance_plan(self, haul=None, relative=True):
        """ Make plan of periodic operations that must be performed.

        :param haul:  current vehicle haul, km. If you specify it here, than
                      this value will be saved in class property <haul>
        :param relative: If True, than the plan with operations planned with
                         haul relative to current.
                         Otherwise - with absolute haul values
        :return:  list of operations, that represents plan of periodic
                  operations that must be performed.
        """
        plan = list()
        if haul:
            self.haul = haul
        for operation in self._operations_cat.values():
            # Planned operation date.
            last_date = operation.done_at_date
            interval_date = operation.interval_time
            plan_date = last_date + interval_date

            # Planned operation haul.
            last_km = operation.done_at_km
            interval_km = operation.interval_km
            plan_km = last_km + interval_km

            # Make planned operation haul relative to current.
            if relative:
                plan_km -= self.haul
            plan.append(operation.done(plan_km, plan_date))
        plan.sort(key=lambda x: x.done_at_km)
        return plan

    def export_log(self, file):
        # Export operations history to txt file.
        self._operations_log.save(file)

    def export_cat(self, file):
        # Export periodic operations catalogue to txt file.
        cat = self._operations_cat.values()
        # Clear last operation info and convert it to <OperationsList> type.
        cat = OperationsList([x for x in cat])
        for x in cat:
            x.undo()
        cat.save(file)

    def export_plan(self, file, haul=None):
        # Export maintenance plan to txt file.
        plan = self.make_maintenance_plan(haul)
        plan = OperationsList([x for x in plan])
        plan.save(file)

    def import_log(self, file):
        self._modified = True
        # Import operations history from txt file.
        ops = OperationsList.load(file)
        for op in ops:
            self.add_operation_to_log(op)

    def import_cat(self, file):
        self._modified = True
        # Import periodic operations catalogue to txt file.
        ops = OperationsList.load(file)
        for op in ops:
            self.add_operation_to_cat(op)

    def save(self, file=None):
        """ Serialize current class instance.

        Saving using pickle as compressed file
        """
        # Make filename correct.
        if not file and not self._filename:
            raise ValueError("File name argument missed.")
        elif not file:
            file = self._filename
        # Add extension (if missed).
        ext = os.path.splitext(file)[-1]
        if not ext or ext != self._extension:
            file += VehicleLogBook._extension
        # Serialize.
        with open(file, 'wb') as fh:
            pickle.dump(self, fh, pickle.HIGHEST_PROTOCOL)
        self._modified = False
        self._filename = file

    @staticmethod
    def load(file):
        """ Create class instance from previously saved instance.

        Using pickle module.

        Warning
        -------
        The pickle module is not secure against erroneous or maliciously
        constructed data. Never unpickle data received from an untrusted or
        unauthenticated source.
        """
        # Add extension (if missed).
        ext = os.path.splitext(file)[-1]
        if not ext:
            file += VehicleLogBook._extension
        # Deserialize.
        with open(file, 'rb') as fh:
            vehice_log_book = pickle.load(fh)
        vehice_log_book._changed = False
        # Check type.
        if not isinstance(vehice_log_book, VehicleLogBook):
            raise TypeError("File {0} has unexpected type: {1}".format(
                file,
                type(vehice_log_book)))
        # Check version.
        if vehice_log_book._version != VERSION:
            warnings.warn("File {0} created by another version "
                          "of class <VehicleLogBook>".format(file), Warning)
        vehice_log_book._modified = False
        vehice_log_book._filename = file
        return vehice_log_book

    def __str__(self):
        return self._operations_log.__str__()

if __name__ == "__main__":
    # If running that module as the main program - do doctests.
    import doctest
    doctest.testmod()


# External Packages
from fastapi import APIRouter
from fastapi import Request
from fastapi.responses import HTMLResponse, FileResponse
from fastapi.templating import Jinja2Templates
from khoj.utils.rawconfig import TextContentConfig, OpenAIProcessorConfig, FullConfig

# Internal Packages
from khoj.utils import constants, state

import json

# Initialize Router
web_client = APIRouter()
templates = Jinja2Templates(directory=constants.web_directory)

VALID_TEXT_CONTENT_TYPES = ["org", "markdown", "pdf", "plaintext"]

# Create Routes
@web_client.get("/", response_class=FileResponse)
def index(request: Request):
    return templates.TemplateResponse("index.html", context={"request": request, "demo": state.demo})

@web_client.get("/chat", response_class=FileResponse)
def chat_page(request: Request):
    return templates.TemplateResponse("chat.html", context={"request": request, "demo": state.demo})

if not state.demo:

    @web_client.get("/config", response_class=HTMLResponse)
    def config_page(request: Request):
        default_full_config = FullConfig(
            content_type=None,
            search_type=None,
            processor=None,
        )
        current_config = state.config or json.loads(default_full_config.json())

        successfully_configured = {
            "pdf": False,
            "markdown": False,
            "org": False,
            "image": False,
            "github": False,
            "notion": False,
            "plaintext": False,
            "enable_offline_model": False,
            "conversation_openai": False,
            "conversation_gpt4all": False,
        }

        if state.content_index:
            successfully_configured.update(
                {
                    "pdf": state.content_index.pdf is not None,
                    "markdown": state.content_index.markdown is not None,
                    "org": state.content_index.org is not None,
                    "image": state.content_index.image is not None,
                    "github": state.content_index.github is not None,
                    "notion": state.content_index.notion is not None,
                    "plaintext": state.content_index.plaintext is not None,
                }
            )

        if state.processor_config and state.processor_config.conversation:
            successfully_configured.update(
                {
                    "conversation_openai": state.processor_config.conversation.openai_model is not None,
                    "conversation_gpt4all": state.processor_config.conversation.gpt4all_model.loaded_model is not None,
                }
            )

        return templates.TemplateResponse(
            "config.html",
            context={
                "request": request,
                "current_config": current_config,
                "current_model_state": successfully_configured,
            },
        )

    @web_client.get("/config/content_type/github", response_class=HTMLResponse)
    def github_config_page(request: Request):
        default_copy = constants.default_config.copy()
        default_github = default_copy["content-type"]["github"]  # type: ignore

        default_config = TextContentConfig(
            compressed_jsonl=default_github["compressed-jsonl"],
            embeddings_file=default_github["embeddings-file"],
        )

        current_config = (
            state.config.content_type.github
            if state.config and state.config.content_type and state.config.content_type.github
            else default_config
        )

        current_config = json.loads(current_config.json())

        return templates.TemplateResponse(
            "content_type_github_input.html", context={"request": request, "current_config": current_config}
        )

    @web_client.get("/config/content_type/notion", response_class=HTMLResponse)
    def notion_config_page(request: Request):
        default_copy = constants.default_config.copy()
        default_notion = default_copy["content-type"]["notion"]  # type: ignore

        default_config = TextContentConfig(
            compressed_jsonl=default_notion["compressed-jsonl"],
            embeddings_file=default_notion["embeddings-file"],
        )

        current_config = (
            state.config.content_type.notion
            if state.config and state.config.content_type and state.config.content_type.notion
            else default_config
        )

        current_config = json.loads(current_config.json())

        return templates.TemplateResponse(
            "content_type_notion_input.html", context={"request": request, "current_config": current_config}
        )

    @web_client.get("/config/content_type/{content_type}", response_class=HTMLResponse)
    def content_config_page(request: Request, content_type: str):
        if content_type not in VALID_TEXT_CONTENT_TYPES:
            return templates.TemplateResponse("config.html", context={"request": request})

        default_copy = constants.default_config.copy()
        default_content_type = default_copy["content-type"][content_type]  # type: ignore

        default_config = TextContentConfig(
            compressed_jsonl=default_content_type["compressed-jsonl"],
            embeddings_file=default_content_type["embeddings-file"],
        )

        current_config = (
            state.config.content_type[content_type]
            if state.config and state.config.content_type and state.config.content_type[content_type]  # type: ignore
            else default_config
        )
        current_config = json.loads(current_config.json())

        return templates.TemplateResponse(
            "content_type_input.html",
            context={
                "request": request,
                "current_config": current_config,
                "content_type": content_type,
            },
        )

    @web_client.get("/config/processor/conversation/openai", response_class=HTMLResponse)
    def conversation_processor_config_page(request: Request):
        default_copy = constants.default_config.copy()
        default_processor_config = default_copy["processor"]["conversation"]["openai"]  # type: ignore
        default_openai_config = OpenAIProcessorConfig(
            api_key="",
            chat_model=default_processor_config["chat-model"],
        )

        current_processor_openai_config = (
            state.config.processor.conversation.openai
            if state.config
            and state.config.processor
            and state.config.processor.conversation
            and state.config.processor.conversation.openai
            else default_openai_config
        )
        current_processor_openai_config = json.loads(current_processor_openai_config.json())

        return templates.TemplateResponse(
            "processor_conversation_input.html",
            context={
                "request": request,
                "current_config": current_processor_openai_config,
            },
        )


"""
-*- coding: utf-8 -*-
File    : basepage.py
Version : 0.1
Author  : usrpi
Date    :2021/1/4
"""
import logging
import datetime
import os

from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 封装基本函数 -- 执行日志、处理异常、失败截图
# 所有的页面公共部分，不涉及业务
class BasePage:

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

    # 等待元素可见
    def wait_eleVisible(self, locator, times=30 ,poll_frequency=0.5, doc=""):
        """
        :param locator: 元素定位。 元素形式（元素定位类型、元素定位方式）
        :param times:
        :param poll_frequency:
        :param doc:
        :return:
        """
        logging.info("等待元素 s% 可见", locator)
        try:
            # 开始等待时间
            start = datetime.datetime.now()
            return WebDriverWait(self.driver, times=30 ,poll_frequency=0.5).until(EC.visibility_of_element_located(locator))
            # 结束等待时间
            end = datetime.datetime.now()
            # 求一个插值，写在日志中，就是等待了多久
            logging.info("等待结束，等待时长为：s%", (end-start))
        except:
            logging.exception("等待元素可见失败！！！")
            # 截图
            self.save_screenshot(doc)
            raise

    # 等待元素可见
    def wait_eleExist(self, locator):
        pass
    # 查找元素
    def get_ele(self, locator, doc = ""):
        logging.info("查找元素: s%", locator)
        try:
            return self.driver.find_element(*locator)
        except:
            logging.exception("查找元素失败！！！")
            # 截图
            self.save_screenshot(doc)
            raise

    # 点击操作
    def click_ele(self, locator, doc = ""):
        # 找元素
        ele = self.get_ele(locator, doc)
        # 元素操作
        logging.info("点击元素：s%", locator)
        try:
           ele.click()
        except:
            logging.exception("元素点击操作失败！！！")
            # 截图
            self.save_screenshot(doc)
            raise
    # 输入操作
    def input_text(self, locator, text , doc = ""):
        # 找元素
        ele = self.get_ele(locator,doc)
        # 输入操作
        logging.info("元素输入：s%",locator)
        try:
           ele.send_keys(text)
        except:
            logging.exception("数据输入失败！！！")
            # 截图
            self.save_screenshot(doc)
            raise

    # 获取元素的文本内容
    def get_text(self, locator, doc = ""):
        # 找元素
        ele = self.get_ele(locator, doc)
        # 获取文本
        try:
            return ele.text
        except:
            logging.exception("获取文本失败！！！")
            # 截图
            self.save_screenshot(doc)
            raise

    # 获取元素的属性
    def get_attr(self, locator,attr, doc = ""):
        # 找元素
        ele = self.get_ele(locator, doc)
        # 获取文本
        try:
            return ele.get_attribute(attr)
        except:
            logging.exception("获取元素属性失败！！！")
            # 截图
            self.save_screenshot(doc)
            raise

    # alter弹窗处理

    # iframe切换

    # 上传操作

    # 滚动条处理
    # 窗口切换

    # 失败截图
    def save_screenshot(self, name):
        # 图片名称：模块名_页面名称_操作名称_时间.png
        path = os.path.dirname(os.path.dirname(__file__)) + '\\Output\screenshots'
        t = datetime.datetime.now()
        file_name = path + t + ".png"
        self.driver.save_screenshot(file_name)
        logging.info("接取网页成功，文件路径为：s%" , file_name)


import sys
import os
from django.conf import settings
from django.core.management import execute_from_command_line
from django.conf.urls import url
from django.http import HttpResponse
from django.core.wsgi import get_wsgi_application

DEBUG = os.environ.get('DEBUG', 'on') == 'on'

print(DEBUG)

SECRET_KEY = os.environ.get('SECRET_KEY', os.urandom(32))

print(SECRET_KEY)

settings.configure(
    DEBUG = DEBUG,
    SECRET_KEY = SECRET_KEY,
    ROOT_URLCONF  = __name__,
    MIDDLEWARE_CLASSES = (
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ),
)

def index(request):
    return HttpResponse('Hello World')

urlpatterns = (
    url(r'^$', placeholder, name='homepage'),
)

application = get_wsgi_application()

if __name__ == '__main__':

    execute_from_command_line(sys.argv)


from __future__ import annotations

import os
import platform
import re
import subprocess
import typing
from pathlib import Path

from PySide6 import QtCore, QtGui, QtWidgets

import randovania
from randovania import get_data_path

if typing.TYPE_CHECKING:
    from collections.abc import Iterator

def map_set_checked(iterable: Iterator[QtWidgets.QCheckBox], new_status: bool):
    for checkbox in iterable:
        checkbox.setChecked(new_status)

def lock_application(value: bool):
    QtWidgets.QApplication.instance().main_window.setEnabled(value)

def _prompt_user_for_file(
    window: QtWidgets.QWidget, caption: str, filter: str, dir: str | None = None, new_file: bool = False
) -> Path | None:
    """
    Helper function for all `prompt_user_for_*` functions.
    :param window:
    :param caption:
    :param filter:
    :param new_file: If false, prompt for an existing file.
    :return: A string if the user selected a file, None otherwise
    """
    if new_file:
        method = QtWidgets.QFileDialog.getSaveFileName
    else:
        method = QtWidgets.QFileDialog.getOpenFileName
    open_result = method(window, caption=caption, dir=dir, filter=filter)
    if not open_result or open_result == ("", ""):
        return None
    return Path(open_result[0])

def _prompt_user_for_directory(
    window: QtWidgets.QWidget, caption: str, dir: str | None = None, new_file: bool = False
) -> Path | None:
    if new_file:
        dialog = QtWidgets.QFileDialog(window)
        dialog.setFileMode(QtWidgets.QFileDialog.FileMode.DirectoryOnly)
        dialog.setOption(QtWidgets.QFileDialog.ShowDirsOnly)
        dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
        dialog.setDirectory(dir)
        if dialog.exec_():
            open_result = dialog.selectedFiles()
            if not open_result:
                return None
            return Path(open_result[0])
        return None

    else:
        open_result = QtWidgets.QFileDialog.getExistingDirectory(
            window, caption, dir, QtWidgets.QFileDialog.ShowDirsOnly
        )
        if not open_result or open_result == ("", ""):
            return None
        return Path(open_result)

def prompt_user_for_vanilla_input_file(
    window: QtWidgets.QWidget, extensions: list[str], existing_file: Path | None = None
) -> Path | None:
    """
    Shows an QFileDialog asking the user for a vanilla game file
    :param window:
    :param extensions:
    :param existing_file: An existing file to pre-fill with.
    :return: A string if the user selected a file, None otherwise
    """
    if extensions and extensions == [""]:
        return _prompt_user_for_directory(
            window, "Select the vanilla game folder", dir=str(existing_file) if existing_file is not None else None
        )
    return _prompt_user_for_file(
        window,
        caption="Select the vanilla game {}.".format("/".join(extensions)),
        dir=str(existing_file) if existing_file is not None else None,
        filter=";".join(f"*.{ext}" for ext in extensions),
    )

def prompt_user_for_output_file(window: QtWidgets.QWidget, default_name: str, extensions: list[str]) -> Path | None:
    """
    Shows an QFileDialog asking the user where to place the output file
    :param window:
    :param default_name: Name of a file that will be offered by default in the UI.
    :param extensions:
    :return: A string if the user selected a file, None otherwise
    """
    if extensions and extensions == [""]:
        return _prompt_user_for_directory(
            window, "Where to place the Randomized game directory", dir=default_name, new_file=False
        )

    return _prompt_user_for_file(
        window,
        caption="Where to place the Randomized game file.",
        dir=default_name,
        filter=";".join(f"*.{ext}" for ext in extensions),
        new_file=True,
    )

def prompt_user_for_output_game_log(window: QtWidgets.QWidget, default_name: str) -> Path | None:
    """
    Shows an QFileDialog asking the user for a Randovania seed log
    :param window:
    :param default_name:
    :return: A string if the user selected a file, None otherwise
    """
    from randovania.layout.layout_description import LayoutDescription

    return _prompt_user_for_file(
        window,
        caption="Select a Randovania seed log.",
        dir=default_name,
        filter=f"Randovania Game, *.{LayoutDescription.file_extension()}",
        new_file=True,
    )

def prompt_user_for_input_game_log(window: QtWidgets.QWidget) -> Path | None:
    """
    Shows an QFileDialog asking the user for a Randovania seed log
    :param window:
    :return: A string if the user selected a file, None otherwise
    """
    from randovania.layout.layout_description import LayoutDescription

    return _prompt_user_for_file(
        window,
        caption="Select a Randovania seed log.",
        filter=f"Randovania Game, *.{LayoutDescription.file_extension()}",
        new_file=False,
    )

def prompt_user_for_database_file(window: QtWidgets.QWidget) -> Path | None:
    """
    Shows an QFileDialog asking the user for a Randovania database file
    :param window:
    :return: A string if the user selected a file, None otherwise
    """
    return _prompt_user_for_file(window, caption="Select a Randovania database file.", filter="*.json")

def prompt_user_for_preset_file(window: QtWidgets.QWidget, new_file: bool, name: str | None = None) -> None | (Path):
    """
    Shows an QFileDialog asking the user for a Randovania preset file
    :param window:
    :param new_file: If it should be an existing file (False) or not.
    :return: A path if the user selected a file, None otherwise
    """
    from randovania.layout.versioned_preset import VersionedPreset

    return _prompt_user_for_file(
        window,
        caption="Select a Randovania Preset file.",
        filter=f"Randovania Preset, *.{VersionedPreset.file_extension()};;All Files (*.*)",
        dir=name,
        new_file=new_file,
    )

def set_default_window_icon(window: QtWidgets.QWidget):
    """
    Sets the window icon for the given widget to the default icon
    :param window:
    :return:
    """
    window.setWindowIcon(QtGui.QIcon(os.fspath(randovania.get_icon_path())))

def set_error_border_stylesheet(edit: QtWidgets.QWidget, has_error: bool):
    edit.has_error = has_error
    if has_error:
        edit.setStyleSheet(":enabled { border: 1px solid red; }:disabled { border: 1px solid red; background: #CCC }")
    else:
        edit.setStyleSheet("")

def set_edit_if_different(edit: QtWidgets.QLineEdit, new_text: str):
    """
    Sets the text of the given QLineEdit only if it differs from the current value.
    Prevents snapping the user's cursor to the end unnecessarily.
    :param edit:
    :param new_text:
    :return:
    """
    if edit.text() != new_text:
        edit.setText(new_text)

def set_edit_if_different_text(edit: QtWidgets.QTextEdit, new_text: str):
    if edit.toPlainText() != new_text:
        edit.setPlainText(new_text)

def get_network_client():
    from randovania.gui.lib.qt_network_client import QtNetworkClient

    return typing.cast(QtNetworkClient, QtWidgets.QApplication.instance().network_client)

def get_game_connection():
    from randovania.game_connection.game_connection import GameConnection

    return typing.cast(GameConnection, QtWidgets.QApplication.instance().game_connection)

def show_install_visual_cpp_redist(details: str):
    from PySide6 import QtWidgets

    download_url = "https://aka.ms/vs/16/release/vc_redist.x64.exe"
    support_url = "https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads"

    box = QtWidgets.QMessageBox(
        QtWidgets.QMessageBox.Critical,
        "Unable to load Dolphin backend",
        "Please install the latest "
        f"Microsoft Visual C++ Redistributable.

"
        f"For more details, see Microsoft's webpage.",
        QtWidgets.QMessageBox.Ok,
    )
    set_default_window_icon(box)
    box.setDetailedText(details)
    box.exec_()

def set_clipboard(text: str):
    from PySide6 import QtWidgets

    QtWidgets.QApplication.clipboard().setText(text)

class FallbackDialog(typing.NamedTuple):
    title: str
    text: str
    parent: QtWidgets.QWidget

def open_directory_in_explorer(path: Path, fallback_dialog: FallbackDialog | None = None):
    try:
        if platform.system() == "Windows":
            os.startfile(path)
        elif platform.system() == "Darwin":
            subprocess.run(["open", path], check=False)
        else:
            subprocess.run(["xdg-open", path], check=False)

    except OSError:
        if fallback_dialog is None:
            raise
        else:
            box = QtWidgets.QMessageBox(
                QtWidgets.QMessageBox.Icon.Information,
                fallback_dialog.title,
                fallback_dialog.text,
                QtWidgets.QMessageBox.StandardButton.Ok,
                fallback_dialog.parent,
            )
            box.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
            box.show()

def set_icon_data_paths(label: QtWidgets.QLabel):
    image_pattern = re.compile('')

    repl = rf'<img src="{get_data_path().as_posix()}/\g<1>"/>'
    new_text = image_pattern.sub(repl, label.text())
    label.setText(new_text)


import logging
from datetime import timedelta

import requests
from django.db import models
from django.utils.six import string_types
from django.utils import timezone
from requests_oauthlib import OAuth2Session

from killboard import app_settings
from killboard.errors import TokenError, IncompleteResponseError

logger = logging.getLogger(__name__)

def _process_scopes(scopes):
    if scopes is None:
        # support filtering by no scopes with None passed
        scopes = []
    if not isinstance(scopes, models.QuerySet) and len(scopes) == 1:
        # support a single space-delimited string inside a list because :users:
        scopes = scopes[0]
    # support space-delimited string scopes or lists
    if isinstance(scopes, string_types):
        scopes = set(scopes.split())
    return set(str(s) for s in scopes)

class TokenQueryset(models.QuerySet):
    def get_expired(self):
        """
        Get all tokens which have expired.
        :return: All expired tokens.
        :rtype: :class:`esi.managers.TokenQueryset`
        """
        max_age = timezone.now() - timedelta(seconds=app_settings.ESI_TOKEN_VALID_DURATION)
        return self.filter(created__lte=max_age)

    def bulk_refresh(self):
        """
        Refreshes all refreshable tokens in the queryset.
        Deletes any tokens which fail to refresh.
        Deletes any tokens which are expired and cannot refresh.
        Excludes tokens for which the refresh was incomplete for other reasons.
        """
        session = OAuth2Session(app_settings.ESI_SSO_CLIENT_ID)
        auth = requests.auth.HTTPBasicAuth(app_settings.ESI_SSO_CLIENT_ID, app_settings.ESI_SSO_CLIENT_SECRET)
        incomplete = []
        for model in self.filter(refresh_token__isnull=False):
            try:
                model.refresh(session=session, auth=auth)
                logging.debug("Successfully refreshed {0}".format(repr(model)))
            except TokenError:
                logger.info("Refresh failed for {0}. Deleting.".format(repr(model)))
                model.delete()
            except IncompleteResponseError:
                incomplete.append(model.pk)
        self.filter(refresh_token__isnull=True).get_expired().delete()
        return self.exclude(pk__in=incomplete)

    def require_valid(self):
        """
        Ensures all tokens are still valid. If expired, attempts to refresh.
        Deletes those which fail to refresh or cannot be refreshed.
        :return: All tokens which are still valid.
        :rtype: :class:`esi.managers.TokenQueryset`
        """
        expired = self.get_expired()
        valid = self.exclude(pk__in=expired)
        valid_expired = expired.bulk_refresh()
        return valid_expired | valid

    def require_scopes(self, scope_string):
        """
        :param scope_string: The required scopes.
        :type scope_string: Union[str, list]
        :return: The tokens with all requested scopes.
        :rtype: :class:`esi.managers.TokenQueryset`
        """
        scopes = _process_scopes(scope_string)
        if not scopes:
            # asking for tokens with no scopes
            return self.filter(scopes__isnull=True)
        from .models import Scope
        scope_pks = Scope.objects.filter(name__in=scopes).values_list('pk', flat=True)
        if not len(scopes) == len(scope_pks):
            # there's a scope we don't recognize, so we can't have any tokens for it
            return self.none()
        tokens = self.all()
        for pk in scope_pks:
            tokens = tokens.filter(scopes__pk=pk)
        return tokens

    def require_scopes_exact(self, scope_string):
        """
        :param scope_string: The required scopes.
        :type scope_string: Union[str, list]
        :return: The tokens with only the requested scopes.
        :rtype: :class:`esi.managers.TokenQueryset`
        """
        num_scopes = len(_process_scopes(scope_string))
        pks = [v['pk'] for v in self.annotate(models.Count('scopes')).require_scopes(scope_string).filter(
            scopes__count=num_scopes).values('pk', 'scopes__id')]
        return self.filter(pk__in=pks)

    def equivalent_to(self, token):
        """
        Gets all tokens which match the character and scopes of a reference token
        :param token: :class:`esi.models.Token`
        :return: :class:`esi.managers.TokenQueryset`
        """
        return self.filter(character_id=token.character_id).require_scopes_exact(token.scopes.all()).filter(
            models.Q(user=token.user) | models.Q(user__isnull=True)).exclude(pk=token.pk)

class TokenManager(models.Manager):
    def get_queryset(self):
        """
        Replace base queryset model with custom TokenQueryset
        :rtype: :class:`esi.managers.TokenQueryset`
        """
        return TokenQueryset(self.model, using=self._db)

class EVEClassManager(models.Manager):
    def get_or_create_from_code(self, i_id, json_data, api):
        try:
            return self.get(id=i_id), False
        except self.model.DoesNotExist:
            item = self.model(id=i_id)
            item.process(json_data, api)
            item.save()
            return item, True


import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
from plotly.subplots import make_subplots
import os
import plotly.io as pio
pio.renderers.default = "browser"

num_of_interviews = [0, 1, 2, 3]

y_list = 3
x_list = 6

current = False
trend = False

# example: communication = 0
specific_aspect = 1

# categories set for the radar plots
categories = ["Value for the customer",
              "Value for the producer",
              "Risks for the customer",
              "Risks for the producer",
              "Cost of development",
              "Cost of implementation",
              "Return of investment",
              "Market establishment"
              ]

# aspect names
aspects = ["Communication",
           "Data handling",
           "Data driven problem solving",
           "Situational context awareness",
           "Adaption to different contexts",
           "Remote Operation",
           "Recommendation and decision support",
           "Self-organization and control",
           "Predictive acting",
           "Continuous improvement",
           "Task automation"]

def add_figure_to_plot(values, _categories, name):
    """
    This function adds another trace to a plotly figure.
    You need to provide values, categories and a plot name.
    """
    fig.add_trace(go.Scatterpolar(
        r=values,
        theta=_categories,
        fill=None,
        name=name
    ))

if __name__ == "__main__":
    # df gets loaded from csv
    df = pd.read_csv("interview0/interview0_current.csv")

    # 0 = "Value for the customer"
    # 1 = "Value for the producer"
    # 2 = "Risks for the customer"
    # 3 = "Risks for the producer"
    # 4 = "Cost of development"
    # 5 = "Cost of implementation"
    # 6 = "Return of investment"
    # 7 = "Market establishment"
    # first criterion row gets selected.
    lst0 = list(df.iloc[y_list])
    lst0.pop(0)
    # print(lst0)

    # second criterion row gets selected.
    lst1 = list(df.iloc[x_list])
    lst1.pop(0)
    # print(lst1)

    # initial lists for the heatmap
    # hl / hm / hh
    # ml / mm / mh
    # ll / lm / lh
    ll = []
    lm = []
    lh = []
    ml = []
    mm = []
    mh = []
    hl = []
    hm = []
    hh = []

    # first and second list get sorted into the initial lists for the heatmap
    for i in range(len(lst0)):
        value = "input"
        if lst0[i] == "l" and lst1[i] == "l":
            ll.append(value)
        elif lst0[i] == "l" and lst1[i] == "m":
            lm.append(value)
        elif lst0[i] == "l" and lst1[i] == "h":
            lh.append(value)
        elif lst0[i] == "m" and lst1[i] == "l":
            ml.append(value)
        elif lst0[i] == "m" and lst1[i] == "m":
            mm.append(value)
        elif lst0[i] == "m" and lst1[i] == "h":
            mh.append(value)
        elif lst0[i] == "h" and lst1[i] == "l":
            hl.append(value)
        elif lst0[i] == "h" and lst1[i] == "m":
            hm.append(value)
        elif lst0[i] == "h" and lst1[i] == "h":
            hh.append(value)
        else:
            print(f"Cannot sort value {lst0[i], lst1[i]}, wrong input")

    # data list gets created
    data = [[len(hl), len(hm), len(hh)],
            [len(ml), len(mm), len(mh)],
            [len(ll), len(lm), len(lh)]]

    # figure gets created
    fig = px.imshow(data,
                    labels=dict(x=categories[x_list], y=categories[y_list], color="Occurrence"),
                    x=['low', 'medium', 'high'],
                    y=['high', 'medium', 'low']
                    )
    fig.update_xaxes(side="bottom")
    # fig.show()
    fig.write_html(f"test_heatmap.html")

    # # new figure class gets created
    # fig = make_subplots()
    #
    # interview_count = 0
    #
    # if current:
    #     for i in current_data:
    #         add_figure_to_plot(i[specific_aspect], categories, f"Interview{interview_count}: Current")
    #         interview_count = interview_count + 1
    #
    # interview_count = 0
    # if trend:
    #     for i in trend_data:
    #         add_figure_to_plot(i[specific_aspect], categories, f"Interview{interview_count}: Trend")
    #         interview_count = interview_count + 1
    #
    # fig.update_traces(opacity=0.9)
    #
    # # Plot styling happens here
    # fig.update_layout(font_family="Arial", legend=dict(
    #     title=f"Aspect: {aspects[specific_aspect]}",
    #     font=dict(size=20),
    #     orientation="h",
    #     y=1.1,
    #     yanchor="bottom",
    #     x=0.5,
    #     xanchor="center"
    #     ),
    #     template='plotly',
    #     showlegend=True)
    #
    # fig.update_layout(
    #     polar=dict(
    #         radialaxis=dict(
    #             visible=True,
    #             range=[0, 3]
    #         )),
    # )
    #
    # fig.add_annotation(text='Criterion rating information:
high = value 3
medium = value 2
low = value 1',
    #                    x=0.9,
    #                    y=0.8,
    #                    bordercolor='black',
    #                    borderwidth=1,
    #                    showarrow=False,
    #                    font=dict(size=15,
    #                              family="Arial"))
    #
    # # savings html in specific folders
    # if current:
    #     naming = "current.html"
    # if trend:
    #     naming = "trend.html"
    # if trend and current:
    #     naming = "both.html"
    # interview_interpretation = "multigraph_interpretation"
    # path = os.path.join(os.getcwd(), interview_interpretation)
    # if not os.path.isdir(path):
    #     os.mkdir(path)
    # try:
    #     filename = f"{aspects[specific_aspect] + '_' + str(num_of_interviews) + '_' + naming}"
    #     # fig.write_image(f"{path}/{filename}.png")
    #     fig.write_html(f"{path}/{filename}")
    # except:
    #     print(f"{naming} not defined")


import os

# os.environ['CUDA_VISIBLE_DEVICES'] = '-1'

import tensorflow as tf
import keras
# from keras import layers
from sempler import Dataset, DatasetSide, DatasetSoft
from wave_u_net import wave_u_net
from loss import combined_loss, ScatterLoss, RegulatedLoss
from call_back import CustomCallback
from keras.callbacks import ModelCheckpoint
# import random
# from matplotlib import pyplot as plt
import json
# import numpy as np
import math

s_size = 16384 * (24 // 2)
steps_per_epoch = 100
steps = 20 # 40
noise_ratio = 0.21
batch_size=3
side = False
cycles = 10

# model = wave_u_net(num_initial_filters = 12, num_layers = 6, kernel_size = 10, input_size = s_size, output_type = "single")

# model = wave_u_net(num_initial_filters = 24, num_layers = 12, kernel_size = 15, input_size = s_size, output_type = "single")
# model = wave_u_net(num_initial_filters = 32, num_layers = 16, kernel_size = 30, input_size = s_size, output_type = "single")
# model = wave_u_net(num_initial_filters = 32, num_layers = 16, kernel_size = 50, input_size = s_size, output_type = "single")

# model = wave_u_net(num_initial_filters = 24, num_layers = 12, kernel_size = 15, input_size = s_size, output_type = "single", attention = "Gate", attention_res = False, dropout = "False", dropout_rate = 0.2, sub=True, side_chanel=True, side_chanel_cycles=10)
# model = wave_u_net(num_initial_filters = 32, num_layers = 16, kernel_size = 30, input_size = s_size, output_type = "single", attention = "Gate", attention_res = False, dropout = "False", dropout_rate = 0.2, sub=True, side_chanel=side, side_chanel_cycles=cycles)
model = wave_u_net(num_initial_filters = 32, num_layers = 16, kernel_size = 50, input_size = s_size, output_type = "single", attention = "Gate", attention_res = False, dropout = "Last", dropout_rate = 0.2, sub=True, side_chanel=side, side_chanel_cycles=cycles)

if os.path.exists('model.h5'): model.load_weights('model.h5')

initial_epoch = 0

if os.path.exists('epoch.txt'):
    with open("epoch.txt", "r") as f:
        initial_epoch = int(f.read())

opt = keras.optimizers.Adam(learning_rate=0.000_01) #0.000_1 - 0.000_01

loss = RegulatedLoss(s_size, steps, noise_ratio)
# loss = ScatterLoss(s_size, steps, noise_ratio)
# loss = "MSE"

model.compile(loss=loss, optimizer=opt)

model.summary()

c1 = CustomCallback(chackpoint=True)
c2 = ModelCheckpoint(filepath='model.h5', save_best_only=False, save_weights_only=True, save_freq='epoch')

# 90
if side:
    dataset = DatasetSide(list(range(90)), s_size=s_size, steps=steps, batch_size=batch_size, noise_ratio=noise_ratio, orig=True, info=True, side_cysles=cycles)
else:
    # dataset = Dataset(list(range(900, 1044)), s_size=s_size, steps=steps, batch_size=batch_size, noise_ratio=noise_ratio, orig=True, info=True)
    dataset = DatasetSoft(list(range(0, 250)), s_size=s_size, steps=steps, batch_size=batch_size, noise_ratio=noise_ratio, orig=True, info=True)

epochs = len(dataset) // steps_per_epoch
print(f"data: {(len(dataset) * batch_size):_}")

model.fit(dataset, epochs=epochs, steps_per_epoch=steps_per_epoch, initial_epoch=initial_epoch, shuffle=False, callbacks=[c1, c2])


from numpy import mean
from numpy import std
from matplotlib import pyplot as plt
from sklearn.model_selection import KFold
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten
from tensorflow.keras.optimizers import SGD
from scipy.ndimage.interpolation import shift
from tensorflow.keras.models import load_model
import numpy as np

def getDataset():
 	(Xtrain, Ytrain), (Xtest, Ytest) = mnist.load_data()
 	Xtrain = Xtrain.reshape((Xtrain.shape[0], 28, 28, 1))
 	Xtest = Xtest.reshape((Xtest.shape[0], 28, 28, 1))
 	Ytest = to_categorical(Ytest)
 	Ytrain = to_categorical(Ytrain)
 	return Xtrain, Ytrain, Xtest, Ytest

def processData(train, test):
 	train = train.astype('float32')
 	test = test.astype('float32')
 	train = train / 255.0
 	test = test / 255.0
 	return train, test

def constructModel():
 	model = Sequential()
 	model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', input_shape=(28, 28, 1)))
 	model.add(MaxPooling2D((2, 2)))
 	model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform'))
 	model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform'))
 	model.add(MaxPooling2D((2, 2)))
 	model.add(Flatten())
 	model.add(Dense(100, activation='relu', kernel_initializer='he_uniform'))
 	model.add(Dense(10, activation='softmax'))
 	opt = SGD(learning_rate=0.01, momentum=0.9)
 	model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
 	return model

def modelEvaluation(dataX, dataY, n_folds=5):
 	scores, histories = list(), list()
 	kfold = KFold(n_folds, shuffle=True, random_state=1)
 	for train_ix, test_ix in kfold.split(dataX):
          model = constructModel()
          trainX, trainY, testX, testY = dataX[train_ix], dataY[train_ix], dataX[test_ix], dataY[test_ix]
          history = model.fit(trainX, trainY, epochs=10, batch_size=32, validation_data=(testX, testY), verbose=0)
          _, acc = model.evaluate(testX, testY, verbose=0)
          print('> %.3f' % (acc * 100.0))
          scores.append(acc)
          histories.append(history)
 	return scores, histories

def getDiagnostics(histories):
 	for i in range(len(histories)):
         plt.subplot(2, 1, 1)
         plt.title('Cross Entropy Loss')
         plt.plot(histories[i].history['loss'], color='blue', label='train')
         plt.plot(histories[i].history['val_loss'], color='orange', label='test')
         plt.subplot(2, 1, 2)
         plt.title('Classification Accuracy')
         plt.plot(histories[i].history['acc'], color='blue', label='train')
         plt.plot(histories[i].history['val_acc'], color='orange', label='test')
 	plt.show()

def getPerformance(scores):
 	print('Accuracy: mean=%.3f std=%.3f, n=%d' % (mean(scores)*100, std(scores)*100, len(scores)))
 	plt.boxplot(scores)
 	plt.show()

def startTraining():
    trainX, trainY, testX, testY = getDataset()
    trainX, testX = processData(trainX, testX)
    scores, histories = modelEvaluation(trainX, trainY)
    getDiagnostics(histories)
    getPerformance(scores)
    model = constructModel()
    model.fit(trainX, trainY, epochs=10, batch_size=32, verbose=0)
    model.save('DigitClassifier.h5')

startTraining()

def startTesting():
 	trainX, trainY, testX, testY = getDataset()
 	trainX, testX = processData(trainX, testX)
 	model = load_model('DigitClassifier.h5')
 	_, acc = model.evaluate(testX, testY, verbose=0)
 	print('> %.3f' % (acc * 100.0))

startTesting()


# coding: utf-8

from flask import jsonify
from app import app

class Error():
    '''
    HTTP Response Error
    '''

    def __init__(self):
        self.status = None
        self.code = None
        self.message = None
        self.errors = None

    def _ready(self, log_level='info'):
        if log_level == 'critical':
            app.logger.critical(str(self.status) + ' ' + self.message)
        else:
            app.logger.info(str(self.status) + ' ' + self.message)

        error = {
            'status': self.status,
            'code': self.code,
            'message': self.message
        }

        if self.errors:
            error['errors'] = self.errors

        return jsonify(error), self.status

    def bad_request(self, message):
        self.status = 400
        if 'errors' in message:
            self.code = message['code']
            self.message = message['message']
            self.errors = message['errors']
        else:
            self.code = message['code']
            self.message = message['message']

        return self._ready()

    def unauthorized(self, message):
        self.status = 403
        self.code = 'unauthorized'
        self.message = message

        return self._ready()

    def forbidden(self, message):
        self.status = 403
        self.code = 'forbidden'
        self.message = message

        return self._ready()

    def not_found(self, message):
        self.status = 404
        self.code = 'not_found'
        self.message = str(message)

        return self._ready()

    def method_not_allowed(self, message):
        self.status = 405
        self.code = 'method_not_allowed'
        self.message = message

        return self._ready()

    def request_timeout(self, message):
        self.status = 408
        self.code = 'request_timeout'
        self.message = message

        return self._ready()

    def conflict(self, message):
        self.status = 409
        self.code = 'conflict'
        self.message = message

        return self._ready()

    def internal_server_error(self, message):
        self.status = 500
        self.code = 'internal_server_error'
        self.message = str(message)

        return self._ready('critical')