import copy
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import glob
import climind.data_manager.processing as dm
import climind.plotters.plot_types as pt
import climind.stats.utils as utils

import climind.data_types.timeseries as ts

from climind.config.config import DATA_DIR
from climind.definitions import METADATA_DIR

def get_jra55():
    jra_dir = DATA_DIR / 'JRA_temp'

    files = jra_dir.glob('tas_*.csv')

    all_df = []

    for file in files:
        df = pd.read_csv(file)
        all_df.append(df)

    df = pd.concat(all_df, axis=0, ignore_index=True)

    out_ts = ts.TimeSeriesIrregular(
        df.year.tolist(),
        df.month.tolist(),
        df.day.tolist(),
        df.data.tolist()
    )

    return out_ts

final_year = 2023

project_dir = DATA_DIR / "ManagedData"
metadata_dir = METADATA_DIR

data_dir = project_dir / "Data"
fdata_dir = project_dir / "Formatted_Data"
figure_dir = project_dir / 'Figures'
log_dir = project_dir / 'Logs'
report_dir = project_dir / 'Reports'
report_dir.mkdir(exist_ok=True)

# Read in the whole archive then select the various subsets needed here
archive = dm.DataArchive.from_directory(metadata_dir)

ts_archive = archive.select({'variable': 'tas',
                             'type': 'timeseries',
                             'name': ['ERA5'],
                             'time_resolution': 'irregular'})

ds = ts_archive.read_datasets(data_dir)[0]

ts_archive = archive.select({'variable': 'tas',
                             'type': 'timeseries',
                             'name': ['Climate Reanalyzer'],
                             'time_resolution': 'irregular'})

cr = ts_archive.read_datasets(data_dir)[0]

jr = get_jra55()

import seaborn as sns
import pandas as pd

STANDARD_PARAMETER_SET = {
    'axes.axisbelow': False,
    'axes.labelsize': 20,
    'xtick.labelsize': 15,
    'ytick.labelsize': 15,
    'axes.edgecolor': 'lightgrey',
    'axes.facecolor': 'None',

    'axes.grid.axis': 'y',
    'grid.color': 'lightgrey',
    'grid.alpha': 0.5,

    'axes.labelcolor': 'dimgrey',

    'axes.spines.left': False,
    'axes.spines.right': False,
    'axes.spines.top': False,

    'figure.facecolor': 'white',
    'lines.solid_capstyle': 'round',
    'patch.edgecolor': 'w',
    'patch.force_edgecolor': True,
    'text.color': 'dimgrey',

    'xtick.bottom': True,
    'xtick.color': 'dimgrey',
    'xtick.direction': 'out',
    'xtick.top': False,
    'xtick.labelbottom': True,

    'ytick.major.width': 0.4,
    'ytick.color': 'dimgrey',
    'ytick.direction': 'out',
    'ytick.left': False,
    'ytick.right': False
}
sns.set(font='Franklin Gothic Book', rc=STANDARD_PARAMETER_SET)
plt.figure(figsize=(16, 9))

d = 0.12
plt.fill_between([0, 365], [1.5 + d, 1.5 + d], [1.5 - d, 1.5 - d], color='lightblue', alpha=0.5, zorder=0)
plt.fill_between([0, 365], [2 + d, 2 + d], [2 - d, 2 - d], color='lightblue', alpha=0.5, zorder=0)

for year in range(1940, 2024):
    ds2 = copy.deepcopy(ds)
    sub = ds2.select_year_range(year, year)

    zord = 99
    linewidth = 1
    if year == 2016:
        color = 'firebrick'
        linewidth = 3
    elif year == 2020:
        color = 'dodgerblue'
        linewidth = 3
    elif year == 2023:
        color = 'black'
        zord = 100
        linewidth = 3
    else:
        color = 'lightgrey'
        zord = 1

    plt.plot(sub.df.data, color=color, zorder=zord, linewidth=linewidth)

plt.gca().set_xlabel('Day in year (0-364')
plt.gca().set_ylabel('Daily anomaly (degC)')
plt.gca().set_title('Daily anomalies from ERA5 wrt 1850-1900', loc='left', fontsize=24)

plt.savefig(figure_dir / 'daily_anomalies_with_limits.png')
plt.savefig(figure_dir / 'daily_anomalies_with_limits.svg')
plt.close('all')

print(np.count_nonzero(ds.df.data > 1.5))
print(np.count_nonzero(ds.df.data > 1.5 + d))
print(np.count_nonzero(ds.df.data > 1.5 - d))

print(np.count_nonzero(ds.df.data > 2))
print(np.count_nonzero(ds.df.data > 2 + d))
print(np.count_nonzero(ds.df.data > 2 - d))

ds.rebaseline(1981, 2010)
ds = ds.select_year_range(1980, 2023)
cr.rebaseline(1981, 2010)
cr = cr.select_year_range(1980, 2023)
jr.rebaseline(1981, 2010)
jr = jr.select_year_range(1980, 2023)

plt.figure(figsize=(16, 9))
plt.plot(cr.df.date, ds.df.data - cr.df.data)
for y in range(1980, 2025):
    times = pd.date_range(start=f'{y}-01-01', freq='1D', periods=1)
    plt.plot([times[0], times[0]], [-0.3, 0.4], color='lightgrey', alpha=0.5)
plt.gca().set_xlabel('Date')
plt.gca().set_ylabel('Anomaly difference (degC)')
plt.gca().set_ylim(-0.3, 0.4)
plt.gca().set_title('Difference between daily global temperature anomalies from ERA5 and CFSR', loc='left', fontsize=24)

plt.savefig(figure_dir / 'daily_anomalies_diff.png')
plt.savefig(figure_dir / 'daily_anomalies_diff.svg')
plt.close('all')

plt.figure(figsize=(16, 9))
plt.plot(ds.df.date, ds.df.data - jr.df.data)
for y in range(1980, 2025):
    times = pd.date_range(start=f'{y}-01-01', freq='1D', periods=1)
    plt.plot([times[0], times[0]], [-0.3, 0.4], color='lightgrey', alpha=0.5)
plt.gca().set_xlabel('Date')
plt.gca().set_ylabel('Anomaly difference (degC)')
plt.gca().set_ylim(-0.3, 0.4)
plt.gca().set_title('Difference between daily global temperature anomalies from ERA5 and JRA55', loc='left', fontsize=24)

plt.savefig(figure_dir / 'daily_anomalies_diff_jra.png')
plt.savefig(figure_dir / 'daily_anomalies_diff_jra.svg')
plt.close('all')

plt.figure(figsize=(16, 9))

for year in range(1980, 2024):
    ds2 = copy.deepcopy(ds)
    sub = ds2.select_year_range(year, year)

    jr2 = copy.deepcopy(jr)
    subjr = jr2.select_year_range(year, year)

    color = '#dddddd'
    zord = 1
    if year % 10 == 0:
        color = 'firebrick'
        zord = 99

    plt.plot((sub.df.data-subjr.df.data)-np.mean(sub.df.data-subjr.df.data), color=color, zorder=zord)

plt.gca().set_xlabel('Day in year (0-364')
plt.gca().set_ylabel('Daily anomaly difference (degC)')
plt.gca().set_title('Daily anomalies difference between JRA55 and ERA5', loc='left', fontsize=24)

plt.savefig(figure_dir / 'daily_anomalies_diffs.png')
plt.savefig(figure_dir / 'daily_anomalies_diffs.svg')
plt.close('all')


# -*- coding: utf-8 -*-
"""
@ Description:
-------------

-------------
@ Time    : 2019/5/21 9:49
@ Author  : Yaoming Cai
@ FileName: GRegConvAE_Base.py
@ Software: PyCharm
@ Blog    ：https://github.com/AngryCai
@ Email   : caiyaomxc@outlook.com
"""
import os
import sys

from munkres import Munkres
from scipy.sparse import csgraph
from scipy.sparse.linalg import svds
from sklearn import cluster
from sklearn.metrics import pairwise_kernels
from sklearn.neighbors import kneighbors_graph
from sklearn.preprocessing import normalize
from sklearn.metrics.cluster import normalized_mutual_info_score
from sklearn.metrics.classification import cohen_kappa_score, accuracy_score

sys.path.append('/home/caiyaom/python_codes/')
import numpy as np
import tensorflow as tf

class GRegConvAE:

    def __init__(self, epoch, n_cluster, image_name, lr=0.01, reg_lap=1., reg_latent=1., weight_decay=0.,
                 verb_per_iter=None, random_state=None):
        """
        :param epoch: the maximum iteration for updating parameters
        :param n_cluster: the number of clusters
        :param image_name: HSI data name, see demo.py
        :param lr: learning rate (float)
        :param reg_lap: laplasian regularization coefficient
        :param reg_latent: self-expression term regularization coefficient
        :param weight_decay: self-expression coef. regularization coefficient (L2 reg.)
        :param verb_per_iter: print clustering accuracy after n iterations (default None means print nothing)
        :param random_state: graph-level random seed (default is None)
        """
        tf.reset_default_graph()
        self.epoch = epoch
        self.reg_lap = reg_lap
        self.image_name = image_name
        self.reg_latent = reg_latent
        self.weight_decay = weight_decay
        self.n_cluster = n_cluster
        self.lr = lr
        self.verb_per_iter = verb_per_iter
        if random_state is not None:
            tf.set_random_seed(random_state)
        if not os.path.exists(self.image_name):
            os.mkdir(self.image_name)
        self.model_root_dir = self.image_name
        self.model_path = self.model_root_dir + '/' + self.image_name + '-model'

    def net(self, x, n_batch, is_training):
        # X = tf.layers.batch_normalization(X, training=is_training)

        # ============ encoder =================
        embed_1, embed_2, code = self.encoder(x, is_training, 'encoder', reuse=tf.AUTO_REUSE)

        # ============ self expression =============
        Z, Z_hat, C, latent_z = self.self_expression(code, n_batch, 'self-expression', reuse=tf.AUTO_REUSE)

        # ============ encoder =================
        decode = self.decoder(latent_z, [embed_1, embed_2, code], x.get_shape().as_list()[-1], is_training, 'decoder', reuse=tf.AUTO_REUSE)

        return code, decode, Z, Z_hat, C

    def self_expression(self, x, n_batch, name, reuse):
        with tf.variable_scope(name, reuse=reuse):
            Z = tf.layers.flatten(x)  # tf.reshape(X, (tf.shape(X)[0], -1))
            C = tf.Variable(1.0e-8 * tf.ones([n_batch, n_batch], tf.float32), name='Coef')
            Z_hat = tf.matmul(C, Z)
            latent_z = tf.reshape(Z_hat, tf.shape(x))
        return Z, Z_hat, C, latent_z

    def encoder(self, x, is_training, name, reuse):
        with tf.variable_scope(name, reuse=reuse):
            """========= Conv 1 ============"""
            hidden = tf.layers.conv2d(x, 24, (3, 3), strides=(1, 1), padding='same',
                                      kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                      bias_initializer=tf.initializers.zeros())
            embed_1 = tf.nn.relu(tf.layers.batch_normalization(hidden, training=is_training))

            """========= Conv 2 ============"""
            hidden = tf.layers.conv2d(embed_1, 24, (3, 3), strides=(1, 1), padding='same',
                                      kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                      bias_initializer=tf.initializers.zeros())
            embed_2 = tf.nn.relu(tf.layers.batch_normalization(hidden, training=is_training))

            """========= Conv 3 ============"""
            hidden = tf.layers.conv2d(embed_2, 32, (3, 3), strides=(1, 1), padding='same',
                                      kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                      bias_initializer=tf.initializers.zeros())
            code = tf.nn.relu(tf.layers.batch_normalization(hidden, training=is_training))
        return embed_1, embed_2, code

    def decoder(self, latent_z, x, out_channel, is_training, name, reuse):
        with tf.variable_scope(name, reuse=reuse):
            """========= Conv 1 ============"""
            hidden = tf.layers.conv2d_transpose(latent_z, 32, (3, 3), strides=(1, 1), padding='same',
                                                kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                                bias_initializer=tf.initializers.zeros())
            embed_1 = tf.layers.batch_normalization(hidden, training=is_training)
            res_1 = tf.nn.relu(tf.add(x[2], embed_1))
            # embed_1 = tf.nn.relu(tf.layers.batch_normalization(hidden, training=is_training))
            # concat_1 = tf.concat([X[0], embed_1], axis=4)

            """========= Conv 2 ============"""
            hidden = tf.layers.conv2d_transpose(res_1, 24, (3, 3), strides=(1, 1), padding='same',
                                                kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                                bias_initializer=tf.initializers.zeros())
            embed_2 = tf.layers.batch_normalization(hidden, training=is_training)
            res_2 = tf.nn.relu(tf.add(x[1], embed_2))
            # embed_2 = tf.nn.relu(tf.layers.batch_normalization(hidden, training=is_training))
            # concat_2 = tf.concat([X[1], embed_2], axis=4)

            """========= Conv 3 ============"""
            hidden = tf.layers.conv2d_transpose(res_2, 24, (3, 3), strides=(1, 1), padding='same',
                                                kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                                bias_initializer=tf.initializers.zeros())
            embed_3 = tf.layers.batch_normalization(hidden, training=is_training)
            res_3 = tf.nn.relu(tf.add(x[0], embed_3))
            # embed_3 = tf.nn.relu(tf.layers.batch_normalization(hidden, training=is_training))
            # concat_3 = tf.concat([X[2], embed_3], axis=4)

            """========= Conv output ============"""
            decode = tf.layers.conv2d(res_3, out_channel, (1, 1), strides=(1, 1), padding='same',
                                      kernel_initializer=tf.contrib.layers.xavier_initializer_conv2d(),
                                      bias_initializer=tf.initializers.zeros())
            # decode = tf.nn.relu(tf.layers.batch_normalization(hidden, training=is_training))
        return decode

    def __loss(self, x_true, x_predict, z, z_hat, C, L):
        """
        compute loss
        :param x_true: training labels
        :param x_predict: all samples' prediction
        :param C: adjacent matrix
        :return:
        """
        # =========== model reconstruction loss ==============
        loss_recon = tf.reduce_mean(tf.losses.mean_squared_error(x_true, x_predict))
        tf.summary.scalar('loss-recon', loss_recon)

        # =========== coefficient L2 loss ==============
        loss_l2 = tf.nn.l2_loss(C)
        tf.summary.scalar('loss-l2', loss_l2)

        # =========== latent reconstruction loss ==============
        loss_recon_latent = tf.reduce_mean(tf.losses.mean_squared_error(z, z_hat))
        tf.summary.scalar('loss-latent', loss_recon_latent)

        # =========== laplacian loss ==============
        loss_lap = tf.trace(
            tf.matmul(tf.matmul(tf.transpose(C), tf.constant(L, dtype=tf.float32)), C))
        # loss_lap = tf.trace(
        #     tf.matmul(tf.matmul(tf.transpose(z), tf.constant(L, dtype=tf.float32)), z))
        tf.summary.scalar('loss-lap', loss_lap)

        loss = loss_recon + self.reg_lap * loss_lap + self.reg_latent * loss_recon_latent + self.weight_decay * loss_l2
        tf.summary.scalar('loss-total', loss)
        return loss

    def lap_matrix(self, x):
        # A = kneighbors_graph(X.reshape(X.shape[0], -1), n_neighbors=10, include_self=True, n_jobs=3).toarray()
        # A_ = kneighbors_graph(X.reshape(X.shape[0], -1), n_neighbors=5, include_self=True, n_jobs=8).toarray()
        # A_ = 0.5 * (A_ + A_.T)
        A_ = pairwise_kernels(x.reshape(x.shape[0], -1), metric='rbf', gamma=1., n_jobs=8)
        A = 0.5 * (A_ + A_.T)
        # A_[np.nonzero(A_)] = A[np.nonzero(A_)]
        L = csgraph.laplacian(A, normed=True)
        return L

    def __init_net__(self, X):
        x_placeholder = tf.placeholder(tf.float32, shape=(None, X.shape[1], X.shape[2], X.shape[3]))
        is_training = tf.placeholder(tf.bool)
        n_batch = X.shape[0]
        code, decode, Z, Z_hat, C = self.net(x_placeholder, n_batch, is_training)
        L = self.lap_matrix(X)
        loss_p = self.__loss(x_placeholder, decode, Z, Z_hat, C, L)
        tf.summary.histogram('C', C)
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
        with tf.control_dependencies(update_ops):
            train_op = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(loss_p)
        sess = tf.InteractiveSession()
        sess.run(tf.global_variables_initializer())
        self.train_op = train_op
        self.C = C
        self.x_placeholder = x_placeholder
        self.is_training = is_training
        self.prediction_p = decode
        self.loss_p = loss_p
        self.sess = sess

    def fit(self, X, y):
        self.__init_net__(X)
        merged = tf.summary.merge_all()
        writer = tf.summary.FileWriter(self.model_root_dir + '/logs', self.sess.graph)
        saver = tf.train.Saver()
        loss_his = []
        acc_his = {'oa':[], 'nmi':[], 'kappa':[], 'ca':[]} #[]
        for step_i in range(self.epoch):
            train_feed_dict = {self.x_placeholder: X, self.is_training: True}
            _, loss, summary = self.sess.run([self.train_op, self.loss_p, merged], feed_dict=train_feed_dict)
            print('epoch %s ==> loss=%s' % (step_i, loss))
            loss_his.append(loss)
            writer.add_summary(summary, step_i)
            # =============== test ==================
            # loss_eval, C_eval = self.sess.run([loss_p, C], feed_dict={self.x_placeholder: X, self.is_training: False})
            # # print logs after self.verb_per_iter iterations
            if self.verb_per_iter is not None and (step_i + 1) % self.verb_per_iter == 0:
                loss_test, y_pre = self.predict(X)
                acc, nmi, kappa, ca = self.cluster_accuracy(y, y_pre)
                print('epoch %s ==> loss=%s, acc=%s' % (step_i, loss_test, (acc, nmi, kappa, ca)))
                acc_his['oa'].append(acc)
                acc_his['nmi'].append(nmi)
                acc_his['kappa'].append(kappa)
                acc_his['ca'].append(ca)
                saver.save(self.sess, self.model_path, write_meta_graph=False)
        np.savez(self.model_root_dir + '/history.npz', loss=loss_his, acc=acc_his)
        saver.save(self.sess, self.model_path)
        if self.verb_per_iter is not None:
            return acc_his

    def predict(self, X, alpha=0.25):
        loss, Coef = self.sess.run([self.loss_p, self.C], feed_dict={self.x_placeholder: X, self.is_training: False})
        Coef = self.thrC(Coef, alpha)
        y_pre, C = self.post_proC(Coef, self.n_cluster, 8, 18)
        np.savez(self.model_root_dir + '/Affinity.npz', coef=C)
        np.savez(self.model_root_dir + '/y_pre.npz', y_pre=y_pre)
        # missrate_x = self.err_rate(y, y_x)
        # acc = 1 - missrate_x
        return loss, y_pre

    def predict_from_model(self, X, y):
        if not os.path.exists(self.model_root_dir + '/checkpoint'):
            raise Exception('model cannot be found !')
        else:
            self.__init_net__(X)
            # saver = tf.train.import_meta_graph('./checkpoint_dir/MyModel-1000.meta')
            saver = tf.train.Saver()
            saver.restore(self.sess, self.model_path)
            Coef = self.sess.run(self.C, feed_dict={self.x_placeholder: X, self.is_training: False})
            Coef = self.thrC(Coef, 0.25)
            y_pre, C = self.post_proC(Coef, self.n_cluster, 8, 18)
            np.savez(self.model_root_dir + '/Affinity.npz', coef=C)
            acc = self.cluster_accuracy(y, y_pre)
            return y_pre, acc

    def thrC(self, C, ro):
        if ro < 1:
            N = C.shape[1]
            Cp = np.zeros((N, N))
            S = np.abs(np.sort(-np.abs(C), axis=0))
            Ind = np.argsort(-np.abs(C), axis=0)
            for i in range(N):
                cL1 = np.sum(S[:, i]).astype(float)
                stop = False
                csum = 0
                t = 0
                while (stop == False):
                    csum = csum + S[t, i]
                    if csum > ro * cL1:
                        stop = True
                        Cp[Ind[0:t + 1, i], i] = C[Ind[0:t + 1, i], i]
                    t = t + 1
        else:
            Cp = C
        return Cp

    def build_aff(self, C):
        N = C.shape[0]
        Cabs = np.abs(C)
        ind = np.argsort(-Cabs, 0)
        for i in range(N):
            Cabs[:, i] = Cabs[:, i] / (Cabs[ind[0, i], i] + 1e-6)
        Cksym = Cabs + Cabs.T
        return Cksym

    def post_proC(self, C, K, d, alpha):
        # C: coefficient matrix, K: number of clusters, d: dimension of each subspace
        C = 0.5 * (C + C.T)
        r = d * K + 1
        U, S, _ = svds(C, r, v0=np.ones(C.shape[0]))
        U = U[:, ::-1]
        S = np.sqrt(S[::-1])
        S = np.diag(S)
        U = U.dot(S)
        U = normalize(U, norm='l2', axis=1)
        Z = U.dot(U.T)
        Z = Z * (Z > 0)
        L = np.abs(Z ** alpha)
        L = L / L.max()
        L = 0.5 * (L + L.T)
        spectral = cluster.SpectralClustering(n_clusters=K, eigen_solver='arpack', affinity='precomputed',
                                              assign_labels='discretize')
        spectral.fit(L)
        grp = spectral.fit_predict(L) + 1
        return grp, L

    def cluster_accuracy(self, y_true, y_pre):
        Label1 = np.unique(y_true)
        nClass1 = len(Label1)
        Label2 = np.unique(y_pre)
        nClass2 = len(Label2)
        nClass = np.maximum(nClass1, nClass2)
        G = np.zeros((nClass, nClass))
        for i in range(nClass1):
            ind_cla1 = y_true == Label1[i]
            ind_cla1 = ind_cla1.astype(float)
            for j in range(nClass2):
                ind_cla2 = y_pre == Label2[j]
                ind_cla2 = ind_cla2.astype(float)
                G[i, j] = np.sum(ind_cla2 * ind_cla1)
        m = Munkres()
        index = m.compute(-G.T)
        index = np.array(index)
        c = index[:, 1]
        y_best = np.zeros(y_pre.shape)
        for i in range(nClass2):
            y_best[y_pre == Label2[i]] = Label1[c[i]]

        # # calculate accuracy
        err_x = np.sum(y_true[:] != y_best[:])
        missrate = err_x.astype(float) / (y_true.shape[0])
        acc = 1. - missrate
        nmi = normalized_mutual_info_score(y_true, y_pre)
        kappa = cohen_kappa_score(y_true, y_best)
        ca = self.class_acc(y_true, y_best)
        return acc, nmi, kappa, ca

    def class_acc(self, y_true, y_pre):
        """
        calculate each classes's acc
        :param y_true:
        :param y_pre:
        :return:
        """
        ca = []
        for c in np.unique(y_true):
            y_c = y_true[np.nonzero(y_true == c)]  # find indices of each classes
            y_c_p = y_pre[np.nonzero(y_true == c)]
            acurracy = accuracy_score(y_c, y_c_p)
            ca.append(acurracy)
        ca = np.array(ca)
        return ca

    @staticmethod
    def build_laplacian(C):
        C = 0.5 * (np.abs(C) + np.abs(C.T))
        W = np.sum(C, axis=0)
        W = np.diag(1.0 / W)
        L = W.dot(C)
        return L


"""Add value to energyprice.

Revision ID: 668759e73b80
Revises: 46adf5114de1
Create Date: 2017-08-22 13:34:08.196691

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '668759e73b80'
down_revision = '46adf5114de1'

def upgrade():
    for column in ('value', 'bill', 'usage_hauptzaehler',
                   'usage_members', 'leakage_current'):
        op.add_column('energyprice',
                      sa.Column(column, sa.Integer(), nullable=True))
    op.execute("""
        UPDATE energyprice
            SET value = 1489550,
                bill = 114392300
            WHERE year = 2016""")

def downgrade():
    op.drop_column('energyprice', 'value')


from django.contrib import admin
from .models import *

# Register your models here.
admin.site.register([TCamera, TPictureUser])

@admin.register(TPictureCamera)
class TPictureCameraAdmin(admin.ModelAdmin):
    list_display = ('id', 'path', 'size')
    fk_fields = ('user')


"""Basic RNN model
"""
from typing import Literal

import torch
import torch.nn as nn
import torch.nn.functional as F

class Attention(nn.Module):
    """Self Attention Module"""

    def __init__(
        self,
        hidden_size: int,
        proj_hidden_size: int,
    ):
        """Self Attention Module

        Args:
            hidden_size (int): Hidden Size of Encoder Outputs
            proj_hidden_size (int): Hidden Size of Self Attention hidden layer
        """
        super(Attention, self).__init__()
        self.projection = nn.Sequential(
            nn.Linear(hidden_size, proj_hidden_size),
            nn.ReLU(True),
            nn.Linear(proj_hidden_size, 1),
        )

    def forward(self, encoder_outputs: torch.tensor) -> torch.tensor:
        """Forward Propagation

        Args:
            encoder_outputs (torch.tensor): Output hidden states for all encoder timestamps. Dimension: bs, sequence_length, hidden_size

        Returns:
            torch.tensor: Weighted tensor for all encoder hidden states
        """
        att_energy = self.projection(encoder_outputs)  # (bs, seq_len, 1)
        att_weights = F.softmax(att_energy.squeeze(2), dim=1)  # (bs, seq_len)
        outputs = (encoder_outputs * att_weights.unsqueeze(2)).sum(
            dim=1
        )  # (bs, hidden_size)
        return outputs

class RNNModel(nn.Module):
    """RNN Model"""

    def __init__(
        self,
        in_channels: int,
        num_classes: int,
        sequence_len: int,
        hidden_size: int = 64,
        num_layers: int = 2,
        rnn_type: Literal["RNN", "LSTM", "GRU"] = "GRU",
        bidirectional: bool = True,
        dropout: float = 0.0,
        use_attention: bool = True,
        proj_size: int = 64,
    ):
        """RNN Model

        Args:
            in_channels (int): Number of input channels for each timestep.
            num_classes (int): Sequence Length
            sequence_len (int): Number of output class labels
            hidden_size (int, optional): RNN hidden size to pass to next time step. Defaults to 64.
            num_layers (int, optional): Number of RNN layers. Defaults to 2.
            rnn_type (str, "LSTM", "RNN" or "GRU"): Type of RNN architecture. Defaults to "LSTM". Need to be UPPERCASE.
            bidirectional (bool, optional): Use bidirectional RNN. Defaults to True.
            dropout (float, optional): Dropout Rate. Defaults to 0.25.
            use_attention (bool, optional): Use self-attention. Defaults to False.
            proj_size (int, optional): Hidden size for self-attention network. Default to 64.
        """

        super(RNNModel, self).__init__()

        self.bidirectional = bidirectional
        self.sequence_len = sequence_len
        self.use_attention = use_attention

        self.rnn_type = rnn_type
        rnn_obj = getattr(nn, rnn_type)
        self.rnn = rnn_obj(
            input_size=in_channels,
            hidden_size=hidden_size,
            num_layers=num_layers,
            bias=False,
            batch_first=True,
            dropout=dropout,
            bidirectional=bidirectional,
        )

        self.dropout = nn.Dropout(p=dropout)

        if use_attention:
            if self.bidirectional:
                self.linear = nn.Linear(4 * hidden_size, num_classes)
                self.self_attention = Attention(2 * hidden_size, proj_size)
            else:
                self.linear = nn.Linear(2 * hidden_size, num_classes)
                self.self_attention = Attention(hidden_size, proj_size)
        else:
            self.self_attention = None
            if self.bidirectional:
                self.linear = nn.Linear(2 * hidden_size, num_classes)
            else:
                self.linear = nn.Linear(hidden_size, num_classes)

    def forward(self, inputs: torch.tensor) -> torch.tensor:
        """Forward Pass

        Args:
            inputs (torch.tensor): Input Mini Batch

        Returns:
            torch.tensor: Output Probability
        """
        ## Input Dimension = (bs, input_size, sequence_len)
        x = inputs.permute(0, 2, 1)  # Dimension = (bs, sequence_len, input_size)
        # x = self.dropout(inputs)
        outs, _ = self.rnn(x)  # (bs, sequence_len, hidden_size)
        rnn_out = outs[:, -1, :]  # (bs, hidden_size)
        att_out = self.self_attention(outs)  # (bs, hidden_size)
        final_out = torch.cat((rnn_out, att_out), dim=1)

        probs = self.linear(final_out)  # (bs, num_classes)

        return probs

    def __str__(self):
        """Model Name"""
        if self.use_attention:
            return "RNN_ATT"
        else:
            return "RNN"

if __name__ == "__main__":
    model = RNNModel(
        in_channels=128, num_classes=31, sequence_len=126, use_attention=True
    )
    sample_batch = torch.rand(5, 128, 126)
    out = model(sample_batch)
    print(out.size())


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 03 12:18:59 2022

@author: osso73
"""

import sys
from datetime import datetime, time, timedelta

import pytest
from scripts import alarm

def time_to_delta(timeobj: time) -> timedelta:
    """Return time as a timedelta"""
    return timedelta(hours=timeobj.hour, minutes=timeobj.minute, seconds=timeobj.second)

class TestCommandLine:
    """
    All tests for command_line function:
    - message is well parsed
    - wait is well parsed: test with different lists, etc.
    - at is well parsed
    """

    @pytest.mark.parametrize("option", ["-m", "--message"])
    @pytest.mark.parametrize("msg", ["Hola", "S'ha acabat el temps", "Time!!"])
    def test_command_line_message(self, option: str, msg: str) -> None:
        """Test command_line function message argument"""
        sys.argv = sys.argv[:1]
        sys.argv.append(option)
        sys.argv.append(msg)

        args = alarm.command_line()
        assert args.message == msg

    @pytest.mark.parametrize("option", ["-w", "--wait"])
    @pytest.mark.parametrize(
        "wait",
        [
            ["13:51"],
            ["15m", "30s"],
            ["Oriol", "Pujol", "Romanyà"],
        ],
    )
    def test_command_line_wait(self, option: str, wait: list[str]) -> None:
        """Test command_line function wait argument"""
        sys.argv = sys.argv[:1]
        sys.argv.append(option)
        for element in wait:
            sys.argv.append(element)

        args = alarm.command_line()
        assert args.wait == wait

    @pytest.mark.parametrize("option", ["-m", "--message"])
    @pytest.mark.parametrize("at", ["24:58", "14:23:56", "any time!!"])
    def test_command_line_at(self, option: str, at: str) -> None:
        """Test command_line function at argument"""
        sys.argv = sys.argv[:1]
        sys.argv.append(option)
        sys.argv.append(at)

        args = alarm.command_line()
        assert args.message == at

class TestExitError:
    """
    Tests for exit_error function. Tests included:
    - message is printed, Tested with different types of variables.
    - error code is -1.
    """

    @pytest.mark.parametrize(
        "msg",
        [
            "Hello",
            "Goodbye",
            234,
            False,
        ],
    )
    def test_exit_error_message(self, msg: str, capsys) -> None:
        """Test exit_error function prints message in output."""
        with pytest.raises(SystemExit):
            alarm.exit_error(msg)
        captured = capsys.readouterr()
        assert captured.out == f"{msg}\n{alarm.EXIT_HELP_MSG}\n"

    def test_exit_error_exit_code(self) -> None:
        """Test exit_error function gives exit code == -1"""
        with pytest.raises(SystemExit) as pytest_wrapped_e:
            alarm.exit_error("msg")
        assert pytest_wrapped_e.type == SystemExit
        assert pytest_wrapped_e.value.code == -1

class TestValidateArgs:
    """
    Tests validate_args function. Tests included:
    - no SystemExit is raised if arguments are correct
    - if none wait and at exist, get error message
    - if both wait and at exist, get error message
    - at argument is a valid time, if it exists
    - wait argument is valid, if it exists

    for later -- not yet implemented:
    - at time must be later than now
    """

    @pytest.mark.parametrize(
        "cli",
        [
            "--at 15:30",
            "--wait 5s",
            "--wait 05:00",
            "--wait 3h 25m 5s",
            "--wait 25m",
        ],
    )
    def test_validate_args_ok(self, cli: str) -> None:
        """Test that no SystemExit is raised if arguments are correct"""
        sys.argv = sys.argv[:1]
        for element in cli.split():
            sys.argv.append(element)

        args = alarm.command_line()
        alarm.validate_args(args)
        assert True  # only works if no exitcode

    @pytest.mark.parametrize(
        "cli",
        ["--message test", ""],
    )
    def test_validate_args_no_wait_no_at(self, cli: str, capsys) -> None:
        """Test SystemExit is raised if no wait, and no at"""
        sys.argv = sys.argv[:1]
        for element in cli.split():
            sys.argv.append(element)

        args = alarm.command_line()
        with pytest.raises(SystemExit):
            alarm.validate_args(args)

        captured = capsys.readouterr()
        msg = "You need to provide at least one time argument."
        assert captured.out == f"{msg}\n{alarm.EXIT_HELP_MSG}\n"

    @pytest.mark.parametrize("cli", ["--at 5:00 --wait 5s", "--wait 5s --at 5:00"])
    def test_validate_args_wait_and_at(self, cli: str, capsys) -> None:
        """Test SystemExit is raised if both wait and at are present"""
        sys.argv = sys.argv[:1]
        for element in cli.split():
            sys.argv.append(element)

        args = alarm.command_line()
        with pytest.raises(SystemExit):
            alarm.validate_args(args)

        captured = capsys.readouterr()
        msg = "Only one of the --wait and --at arguments should be provided."
        assert captured.out == f"{msg}\n{alarm.EXIT_HELP_MSG}\n"

    @pytest.mark.parametrize(
        "cli",
        ["--at wrong", "--at 135", "--at 27:30", "--at 11:87", "--at 1:35"],
    )
    def test_validate_args_at_error(self, cli: str, capsys) -> None:
        """Test SystemExit is raised at arguments are wrong"""
        sys.argv = sys.argv[:1]
        for element in cli.split():
            sys.argv.append(element)

        args = alarm.command_line()
        with pytest.raises(SystemExit):
            alarm.validate_args(args)

        captured = capsys.readouterr()
        msg = "After --at you should provide a valid time."
        assert captured.out == f"{msg}\n{alarm.EXIT_HELP_MSG}\n"

    @pytest.mark.parametrize(
        "cli",
        [
            "--wait wrong",
            "--wait 13 23 23",
            "--wait hours",
            "--wait 5.0s",
            "--wait 1:30",
        ],
    )
    def test_validate_args_wait_error(self, cli: str, capsys) -> None:
        """Test SystemExit is raised at arguments are wrong"""
        sys.argv = sys.argv[:1]
        for element in cli.split():
            sys.argv.append(element)

        args = alarm.command_line()
        with pytest.raises(SystemExit):
            alarm.validate_args(args)

        captured = capsys.readouterr()
        msg = "After --wait you should provide a valid time."
        assert captured.out == f"{msg}\n{alarm.EXIT_HELP_MSG}\n"

class TestGetTimeLeft:
    """
    Tests for function get_time_left. It includes:
    - at tests: gives the correct result
    - wait tests: gives the correct result
    """

    @pytest.mark.parametrize("at_time", ["20:24", "23:15:30"])
    def test_get_time_left_at(self, at_time: time):
        """Test at parameters"""
        now = datetime.now()
        left, final = alarm.get_time_left(None, at_time)

        assert final.time() == time.fromisoformat(at_time)
        assert final.date() == now.date()
        assert left == final - now

    @pytest.mark.parametrize(
        "params, wait_time",
        [
            (["02:00"], "02:00:00"),
            (["00:30:50"], "00:30:50"),
            (["1h"], "01:00:00"),
            (["1h", "30m"], "01:30:00"),
            (["1h", "10m", "30s"], "01:10:30"),
        ],
    )
    def test_get_time_left_wait(self, params: list[str], wait_time: str):
        """Test wait parameters"""
        now = datetime.now()
        left, final = alarm.get_time_left(params, None)

        h = time.fromisoformat(wait_time)
        wait_delta = timedelta(hours=h.hour, minutes=h.minute, seconds=h.second)
        wait_delta = time_to_delta(h)

        assert left == wait_delta
        assert final == now + left

@pytest.mark.slow
class TestCountDown:
    """
    Tests for function count_down. Tests included:
    - validate the execution time corresponds with time_left
    """

    @pytest.mark.parametrize(
        "left_time",
        [
            "00:00:03",
            "00:00:01",
            "00:00:05",
        ],
    )
    def test_count_down(self, left_time):
        """Validate the amount of time it takes to execute"""
        left = time.fromisoformat(left_time)
        left = time_to_delta(left)
        start_time = datetime.now()
        final = start_time + left
        alarm.count_down(left, final)
        end_time = datetime.now()
        delta = end_time - start_time
        assert left - delta < timedelta(microseconds=10)

class TestTriggerAlarm:
    """
    Tests for function trigger_alarm. Unclear how to test the
    subprocess.Popen function, but at least I can test the output
    on stdout. Tests included:
    - check that message passed is written on screen
    """

    @pytest.mark.parametrize("msg", ["Hola", "Time up!", "Testing message..."])
    def test_trigger_alarm(self, msg: str, capsys) -> None:
        """Check msg is written on screen"""
        alarm.trigger_alarm(msg)
        captured = capsys.readouterr()
        assert msg in captured.out

@pytest.mark.slow
class TestMain:
    """
    Tests for main function. These are integration test,
    they test the program end-to-end. They test the application works
    under different arguments. The following tests are included:
    - arguments --wait
        * alarm is triggered after expected time
        * message on screen is correct
    - arguments --at
        * alarm is triggered after expected time
        * message on screen is correct
    """

    @pytest.mark.parametrize("option", ["-w", "--wait"])
    @pytest.mark.parametrize("seconds", [2, 1])
    def test_main_wait_time_ok(self, seconds: int, option: str) -> None:
        """Argument with wait triggers alarm after the wait time"""
        sys.argv = sys.argv[:1]
        for arg in [option, f"{seconds}s"]:
            sys.argv.append(arg)

        start = datetime.now()
        alarm.main()
        end = datetime.now()
        delta = (end - start) - timedelta(seconds=seconds)
        assert delta < timedelta(microseconds=100_000)

    @pytest.mark.parametrize("option", ["-m", "--message"])
    @pytest.mark.parametrize("msg", ["testing...", "Time!"])
    def test_main_wait_message_ok(self, msg: str, option: str, capsys) -> None:
        """Argument with wait triggers alarm after the wait time"""
        sys.argv = sys.argv[:1]
        for arg in ["-w", "1s", option, msg]:
            sys.argv.append(arg)

        alarm.main()

        captured = capsys.readouterr()
        assert msg in captured.out

    @pytest.mark.parametrize("option", ["-a", "--at"])
    @pytest.mark.parametrize("seconds", [2, 1])
    def test_main_at_time_ok(self, seconds: int, option: str) -> None:
        """Argument with wait triggers alarm after the wait time"""
        wait_time = timedelta(seconds=seconds)
        start = datetime.now()
        at_time = start + wait_time
        sys.argv = sys.argv[:1]
        for arg in [option, f"{at_time.time().isoformat()}"]:
            sys.argv.append(arg)
        alarm.main()
        end = datetime.now()
        delta = (end - start) - wait_time
        assert delta < timedelta(microseconds=100_000)

    @pytest.mark.parametrize("option", ["-m", "--message"])
    @pytest.mark.parametrize("msg", ["testing...", "Time!"])
    def test_main_at_message_ok(self, msg: str, option: str, capsys) -> None:
        """Argument with wait triggers alarm after the wait time"""
        at_time = datetime.now() + timedelta(seconds=1)
        sys.argv = sys.argv[:1]
        for arg in ["-a", f"{at_time.time().isoformat()}", option, msg]:
            sys.argv.append(arg)
        alarm.main()
        captured = capsys.readouterr()
        assert msg in captured.out


from typing import List, Optional

from app import db
from app.models import User, Team, Token, Project, Schedule

from isoweek import Week

def get_or_create_user(*, name: str, email: str) -> User:
    session = db.get_session()
    user = session.query(User).filter(User.email == email).one_or_none()
    if user:
        return user
    user = User(name=name, email=email)
    session.add(user)
    session.commit()
    return user

def get_from_token(token: Token) -> User:
    session = db.get_session()
    return session.query(User).filter_by(id=token.user_id).one_or_none()

def get_from_id(user_id: int) -> Optional[User]:
    session = db.get_session()
    return session.query(User).filter(User.id == user_id).one_or_none()

def set_team(user_id: int, team_id: int) -> Optional[User]:
    session = db.get_session()
    user = session.query(User).filter(User.id == user_id).one_or_none()
    team = session.query(Team).filter(Team.id == team_id).one_or_none()

    if not user or not team:
        return None

    user._teams = [team]
    session.commit()

    return user

def get_projects(user_id: int) -> List[Project]:
    session = db.get_session()

    return session.query(Project).join(Team).join(User).filter(User.id == 1).all()

def get_projects_for_period(
    *, user_id: int, start_week: Week, end_week: Week
) -> List[Project]:
    session = db.get_session()

    return (
        session.query(Project)
        .filter(Schedule.user_id == user_id)
        .join(Schedule)
        .filter(Schedule.week >= start_week.toordinal())
        .filter(Schedule.week <= end_week.toordinal())
        .group_by(Schedule.project_id)
        .all()
    )


import redis
import json
import logging
import time
import arrow
from flight_database import FlightDatabase

logging.basicConfig(level=logging.WARNING)
redis_connection = redis.Redis("redis", decode_responses=True)
flight_db = FlightDatabase()

def process_message(message):
    if message["airline"] in ("XXX", "DCM", "FWR", "FFL", "XAA"):
        return

    # Split callsign into operator and suffix and remove leading zeros.
    operator_icao = message["callsign"][:3]
    suffix = message["callsign"][3:].lstrip("0")
    callsign = "{}{}".format(operator_icao, suffix)
    if not message["etd"]["etdType"] == "ACTUAL":
        return
    departure = int(arrow.get(message["etd"]["timeValue"]).timestamp())
    arrival = int(arrow.get(message["eta"]["timeValue"]).timestamp())
    duration = arrival - departure
    if duration <= 0:
        logging.warning(f"departure >= arrival: {message}")
        return
    if message["igtd"] is not None:
        igtd = int(arrow.get(message["igtd"]).timestamp())
    else:
        igtd = None
    if message.get("gate_departure") is not None:
        gate_departure = int(arrow.get(message["gate_departure"]).timestamp())
        if gate_departure > departure:
            logging.warning(f"gate_departure > departure: {message}")
            return
    else:
        gate_departure = None
    if message.get("gate_arrival") is not None:
        gate_arrival = int(arrow.get(message["gate_arrival"]).timestamp())
        if gate_arrival < arrival:
            logging.warning(f"gate_arrival < arrival: {message}")
            return
    else:
        gate_arrival = None
    origin = message["origin"]
    destination = message["destination"]
    historic_flight = {
        "callsign": callsign,
        "igtd": igtd,
        "gate_departure": gate_departure,
        "origin": origin,
        "departure": departure,
        "departure_actual": message["etd"]["etdType"] == "ACTUAL",
        "destination": destination,
        "arrival": arrival,
        "arrival_actual": message["eta"]["etaType"] == "ACTUAL",
        "gate_arrival": gate_arrival,
    }
    logging.debug(json.dumps(historic_flight, indent=2))
    logging.info(
        f"{callsign} {origin}-{destination} {duration / 60:.1f} minutes"
    )
    redis_connection.set("historic_flight_processed", time.time())
    flight_db.set_historic_flight(historic_flight)

while True:
    try:
        _pubsub = redis_connection.pubsub(ignore_subscribe_messages=True)
        _pubsub.subscribe("SWIM-ARRIVALS")
        for message in _pubsub.listen():
            if not message["type"] == "message":
                continue
            process_message(json.loads(message["data"]))
    except redis.exceptions.ConnectionError:
        logging.exception("reconnecting to Redis in 5s.")
        time.sleep(5)


# -*- coding: utf-8 -*-
"""
@author: Dinesh
"""

import rasterio
import pandas as pd
import numpy as np
from rasterio import plot
import matplotlib.pyplot as plt

def return_indices(raster):
    np.seterr(divide='ignore', invalid='ignore')
    blue, green, red = raster[0], raster[1], raster[2]
    vre5, vre6, vre7 = raster[3], raster[4], raster[5]
    nir, vre8a, swir11, swir12 = raster[6], raster[7], raster[8], raster[9]

    # Initialize indices
    ndvi = np.empty(blue.shape, dtype=rasterio.float32)
    mndwi = np.empty(blue.shape, dtype=rasterio.float32)
    ndbi = np.empty(blue.shape, dtype=rasterio.float32)

    # Checks
    check_mndwi = np.logical_or(swir11 > 0, red > 0)
    check_ndvi = np.logical_or(red > 0, nir > 0)
    check_ndbi = np.logical_or(swir11 > 0, nir > 0)

    # Calc indices
    ndvi = np.where(check_ndvi, (nir - red) / (nir + red), 0)
    mndwi = np.where(check_mndwi, (red - swir11) / (red + swir11), 0)
    ndbi = np.where(check_ndbi, (swir11 - nir) / (swir11 + nir), 0)

    # Normalize
    # mndwi = (mndwi - mndwi.min()) / (mndwi.max() - mndwi.min())
    # ndvi = (ndvi - ndvi.min()) / (ndvi.max() - ndvi.min())
    # ndbi = (ndbi - ndbi.min()) / (ndbi.max() - ndbi.min())
    return ndvi, mndwi, ndbi


import django_filters
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django_filters.views import FilterView
from django_tables2 import LazyPaginator
from django_tables2.views import SingleTableMixin

from .forms import CocktailCreate, IngredientCreate
from .models import Cocktail, Ingredient
from .tables import IngredientTable

class IngredientFilter(django_filters.FilterSet):
    class Meta:
        model = Ingredient
        fields = {
            'name': ['contains']
        }

class FilteredIngredientListView(SingleTableMixin, FilterView):
    model = Ingredient
    table_class = IngredientTable
    template_name = 'recipes/ingredients.html'
    paginator_class = LazyPaginator
    filterset_class = IngredientFilter

def index(request):
    return render(request, 'recipes/overview.html')

def cocktails(request):
    cocktail_list = Cocktail.objects.all()
    return render(request, 'recipes/cocktails.html', {'cocktail_list': cocktail_list})

def ingredients(request):
    ingredient_list = Ingredient.objects.all()
    return render(request, 'recipes/ingredients.html', {'ingredient_list': ingredient_list})

def cocktail_detail(request, cocktail_id):
    cocktail_id = int(cocktail_id)
    try:
        cocktail = Cocktail.objects.get(id=cocktail_id)
    except Cocktail.DoesNotExist:
        return redirect('cocktails')
    return render(request, 'recipes/cocktail_detail.html', {'cocktail': cocktail})

def ingredient_detail(request, ingredient_id):
    ingredient_id = int(ingredient_id)
    try:
        ingredient = Ingredient.objects.get(id=ingredient_id)
    except Ingredient.DoesNotExist:
        return redirect('ingredients')
    return render(request, 'recipes/ingredient_detail.html', {'ingredient': ingredient})

def upload_cocktail(request):
    upload = CocktailCreate()
    if request.method == 'POST':
        upload = CocktailCreate(request.POST, request.FILES)
        if upload.is_valid():
            upload.save()
            return redirect('cocktails')
        else:
            return HttpResponse("""your form is wrong, reload on reload""")

    else:
        return render(request, 'recipes/upload_cocktail.html', {'upload_form': upload})

def upload_ingredient(request):
    upload = IngredientCreate()
    if request.method == 'POST':
        upload = IngredientCreate(request.POST, request.FILES)
        if upload.is_valid():
            upload.save()
            return redirect('ingredients')
        else:
            return HttpResponse("""your form is wrong, reload on reload""")

    else:
        return render(request, 'recipes/upload_ingredient.html', {'upload_form': upload})

def update_cocktail(request, cocktail_id):
    cocktail_id = int(cocktail_id)
    try:
        recipe_sel = Cocktail.objects.get(id=cocktail_id)
    except Cocktail.DoesNotExist:
        return redirect('cocktails')
    recipe_form = CocktailCreate(request.POST or None, instance=recipe_sel)
    if recipe_form.is_valid():
        recipe_form.save()
        return redirect('cocktails')
    return render(request, 'recipes/upload_cocktail.html', {'upload_form': recipe_form})

def update_ingredient(request, ingredient_id):
    ingredient_id = int(ingredient_id)
    try:
        ingredient_sel = Ingredient.objects.get(id=ingredient_id)
    except Ingredient.DoesNotExist:
        return redirect('ingredients')
    recipe_form = IngredientCreate(request.POST or None, instance=ingredient_sel)
    if recipe_form.is_valid():
        recipe_form.save()
        return redirect('ingredients')
    return render(request, 'recipes/upload_ingredient.html', {'upload_form': recipe_form})

def delete_cocktail(request, cocktail_id):
    cocktail_id = int(cocktail_id)
    try:
        cocktail_sel = Cocktail.objects.get(id=cocktail_id)
    except Cocktail.DoesNotExist:
        return redirect('cocktails')
    cocktail_sel.delete()
    return redirect('cocktails')

def delete_ingredient(request, ingredient_id):
    ingredient_id = int(ingredient_id)
    try:
        ingredient_sel = Ingredient.objects.get(id=ingredient_id)
    except Ingredient.DoesNotExist:
        return redirect('ingredients')
    ingredient_sel.delete()
    return redirect('ingredients')


#coding=utf-8
from django.shortcuts import render
from django.http import HttpResponse,HttpResponseRedirect
from article.models import Article
from datetime import datetime
from django.http import Http404
from django.contrib.syndication.views import Feed
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
from django.views.decorators.csrf import csrf_exempt
from django.template import loader,Context
import csv
# Create your views here.

class RSSFeed(Feed):
    title = "RSS Feed"
    link = "/feed/"
    description = "RSS Feed"
    def items(self):
        return Article.objects.order_by('-datetime')

    def item_title(self,item):
        return item.title

    def item_pubdate(self,item):
        return item.datetime

    def item_description(self, item):
        return item.content

def show_request(request):
    request_path = request.path
    request_host = request.get_host()
    request_get_full_path = request.get_full_path()
    request_is_secure = request.is_secure()
    request_dict = {
            'request_path':request_path,
            'request_host':request_host,
            'request_get_full_path':request_get_full_path,
            'request_is_secure':request_is_secure,
            }
    request_meta_items = request.META.items()
    return render(request,'show_request.html',{
            "request_dict":request_dict,
            "request_meta_items":request_meta_items
            })

@csrf_exempt
def login(request):
    username = request.POST.get('username',None)
    if username:
        request.session['username'] = username
        return render(request,'login.html',{'username':username})
    return render(request,'login.html')

@csrf_exempt
def logout(request):
    try:
        del request.session['username']
    except KeyError:
        pass
    return HttpResponseRedirect('/login/')

class GBKHttpResponse(HttpResponse):
    def __init__(self,content=b'',*args,**kwargs):
        super(GBKHttpResponse,self).__init__(content=content,*args,**kwargs)
        self._charset = "GBK"

def output(request,filename):
    address = [(1,2),('三','四'),(1,2)]
    response = GBKHttpResponse(content_type="text/csv")
#    response.encoding = 'gbk'
    response['Content-Disposition'] = 'attachment;filename={0}'.format(filename)
#    writer = csv.writer(response)
#    writer.writerows(address)
    t = loader.get_template('csv.html')
    c = Context({
        'data':address,
        })
    response.write(t.render(c))
    return response

@csrf_exempt
def add(request):
    if request.POST.get('one',None):
        a = int(request.POST['one'])
        b = int(request.POST['two'])
    else:
        a=0
        b=0
    return render(request,'add.html',{'a':a,'b':b,'sum':a+b})

def home(request):
    posts = Article.objects.all()
    paginator = Paginator(posts,3)
    page = request.GET.get('page')
    try:
        post_list = paginator.page(page)
    except PageNotAnInteger:
        post_list = paginator.page(1)
    except EmptyPage:
        post_list = paginator.paginator(paginator.num_pages)
    return render(request,'home.html',{'post_list':post_list})

def detail(request,id):
    try:
        post = Article.objects.get(id=str(id))
    except Article.DoesNotExist:
        raise Http404
    return render(request,"post.html",{'post':post})

def archive(request):
    try:
        post_list = Article.objects.all()
    except Article.DoesNotExist:
        raise Http404
    return render(request,'archive.html',{'post_list':post_list})

def search_tag(request,tag):
    try:
        post_list=Article.objects.filter(catagory__iexact=tag)
    except Article.DoesNotExist:
        raise Http404
    return render(request,'tag.html',{'post_list':post_list})


#!/usr/bin/env python
import cv2

def main():
    # initial setup
    capture = cv2.VideoCapture(0)
    window_name = 'A5-Ex2'
    cv2.namedWindow(window_name,cv2.WINDOW_AUTOSIZE)
    while True:
        _, image = capture.read()  # get an image from the camera

        # add code to show acquired image
        cv2.imshow(window_name, image)
        # add code to wait for a key press
        cv2.waitKey(20)

if __name__ == '__main__':
    main()


# Matthew Raimondi
# Tuition Calculator
# 05 November 2020

# Import Statements
import sys
import matplotlib.pyplot as plt
from datetime import date

# Program Data and Information (variable definitions (declaration and initialization))
numbers = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.']
inflationRates = [0.015, 0.03, 0.017, 0.015, 0.008, 0.007, 0.021, 0.021, 0.019, 0.023, 0.014]
averageInflation = sum(inflationRates) / len(inflationRates)
helpText = "Welcome to the college tuition calculator. The commands are as follows:\n\tfind - find tuition information about a college\n\tadd - add a new college to the database\n\tcolleges - list the available colleges\n\thelp - print out help to the screen\n\tquit - stop the program\n\nYou may use the quit command from within any part of the program in order to stop the program. When you issue a command, if you change your mind while answering questions, you may type \"back\""
fileName = "colleges.csv"

# Function Definitions
def write_history(input_stream):
    f = open("TCH", 'a')
    f.write(input_stream + "\n")
    f.close()

def ask_question(question_to_ask):
    q = input(question_to_ask + ": ")
    write_history(q)
    if "quit" in q.lower() or "exit" in q.lower():
        exit(0)
    elif "back" in q.lower():
        doer()
    return q

def beautify_money(incoming_number):
    money_array = []
    money_input = str(round(incoming_number, 2))
    holder_array = money_input.split(".")

    if isinstance(incoming_number, int):
        money_string = ".00"
        for c in money_input:  # Make array from string
            money_array.append(c)
    else:
        money_string = ""
        money_array = holder_array[0]

    money_array_r = money_array[::-1]  # Reverse order of list/array

    k = 0  # Implement counter variable

    for n in money_array_r:

        if k % 3 == 0:  # Comma after after 3 digits
            if k == 0:  # Omit first time around
                money_string = n + money_string
            else:
                money_string = n + "," + money_string
            k += 1

        else:
            money_string = n + money_string
            k += 1

    money_string = "$" + money_string

    if isinstance(incoming_number, int):
        return money_string
    else:
        if holder_array[1] == "0":
            return money_string + '.' + holder_array[1] + "0"
        else:
            return money_string + '.' + holder_array[1]

def beautify_money_reverse(incoming_price):
    number_string_array = []
    for o in incoming_price:
        if o in numbers:
            number_string_array.append(o)

    return float(''.join(number_string_array))

def run_college_file_load(filename):
    f = open(filename, 'r')
    data_dump = f.readlines()
    f.close()
    return data_dump

def load_college_data(incoming_data):
    college_list = {}

    for line in incoming_data:
        line_split = line.split(',')
        new_key = line_split[0]
        new_val = eval(line_split[1])
        college_list.update({new_key: new_val})

    return college_list

def print_colleges(dictionary):
    print("We have information on the following colleges:")

    for key in dictionary:
        print(key + ": " + beautify_money(dictionary[key]))

def get_tuition(college_dict, what_college):
    for key in college_dict:
        if what_college.lower() == key.lower():
            return [key, college_dict[key]]
        elif what_college.lower() in key.lower():
            print("Could not find exact match. Searching for similar")
            return [key, college_dict[key]]

def total_cost(tuition):
    tuition_inc = tuition
    cost_by_year = []

    for i in range(4):
        if i != 0:
            tuition += (tuition * averageInflation)
            tuition_inc += tuition

        cost_by_year.append(round(tuition, 2))

    cost_by_year.append(round(tuition_inc, 2))

    return cost_by_year

def plot_tuition_cost(total, year):
    x_axis_year = []
    y_axis_tuition = []
    y_ticks = []

    for i in range(4):
        x_axis_year.append(year + i)
        y_axis_tuition.append(beautify_money(total[i]))

    for i in range(100000):
        if i % 20000 == 0:
            y_ticks.append(beautify_money(i))

    plt.figure(figsize=(12, 8))
    plt.bar(x_axis_year, y_axis_tuition, label="Tuition", color="#0000fa")
    plt.title(f"College Tuition\nYears: {year}-{year + 4}")
    plt.ylabel("Tuition in USD")
    plt.xlabel("Year Tuition (Projected)")
    plt.xticks(x_axis_year)
    plt.yticks(y_ticks)
    plt.grid(True)

    plt.show()

def put_away_data(filename, college_data):
    f = open(filename, 'a')
    f.write(college_data + "\n")
    f.close()

def parse_input(input_stream):
    if "find" in input_stream.lower():
        while True:
            what_year = ask_question("Which year will your child enroll in college?")
            try:
                start_year = eval(what_year)
                this_year = date.today().year
                if start_year <= this_year:
                    print("Can't find tuition for past years")
                    continue
                else:
                    break
            except Exception as not_number:
                print(f"{not_number}\nSorry, {what_year} is not a valid number.")

        what_college = ask_question("Which college would you like information about?")

        try:
            tuition = get_tuition(load_college_data(run_college_file_load(fileName)), what_college)
            print(f"The tuition for {tuition[0]} is {beautify_money(tuition[1])}")
            total = total_cost(tuition[1])
            print(f"The total cost for {tuition[0]} is {beautify_money(total[4])}")
            for i in range(4):
                print(f"\tYear {i + 1} {start_year + i}-{start_year + (i + 1)}: {beautify_money(total[i])}")
            # plot_tuition_cost(total, start_year)
            # ^ not working yet
        except Exception as no_college:
            print(f"{no_college}\nSorry, we do not have information on {what_college}.")

    elif "add" in input_stream.lower():
        new_college = ask_question("What is the name of the college you would like to add?")
        new_cost = ask_question("What is their total cost per year?")
        new_data = (new_college + ',' + str(beautify_money_reverse(new_cost)) + ',')
        put_away_data(fileName, new_data)

    elif "help" in input_stream.lower():
        print(helpText)

    elif "colleges" in input_stream.lower():
        print_colleges(load_college_data(run_college_file_load(fileName)))

    elif "clear" in input_stream.lower():
        pass

    elif "quit" in input_stream.lower() or "exit" in input_stream.lower():
        exit(0)

    else:
        print("Not a valid command")

def main():
    print("Tuition Calculator> ", end="")
    stream = sys.stdin.readline().strip("\n")
    write_history(stream)
    parse_input(stream)

def doer():
    while True:
        main()

# Executed Code
print_colleges(load_college_data(run_college_file_load(fileName)))
print(helpText)
doer()


from datetime import date, timedelta
from enum import IntEnum, StrEnum

from .calandarExceptions import (
    TodaySundayException,
)

class DayOfTheWeekNumber(IntEnum):
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6

class DayOfTheWeek(StrEnum):
    MONDAY = "Понедельник"
    TUESDAY = "Вторник"
    WEDNESDAY = "Среда"
    THURSDAY = "Четверг"
    FRIDAY = "Пятница"
    SATURDAY = "Суббота"
    SUNDAY = "Воскресенье"

class CustomCalendar:
    @staticmethod
    def getDateToday():
        return date.today()

    @staticmethod
    def getTomorrowsDate():
        dateToday = date.today()
        return dateToday + timedelta(days=1)

    @staticmethod
    def getDayNumber(date):
        return date.day

    @staticmethod
    def getDayOfTheWeek(date):
        dayOfTheWeekNumber = DayOfTheWeekNumber(date.weekday())
        if dayOfTheWeekNumber == 6:
            raise TodaySundayException(date)
        dayOfTheWeek = DayOfTheWeek[dayOfTheWeekNumber.name]
        return dayOfTheWeek


from openpyxl import Workbook
from random import randint

wb = Workbook()
ws = wb.active

#1줄씩 데이터 넣기
ws.append(["번호","영어","수학"]) # A, B, C
for i in range(1,11): # 10개의 데이터 넣기
    ws.append([i, randint(0,100), randint(0,100)])

# col_B = ws["B"] #영어 column 만 가지고 오기
#print(col_B)
# for cell in col_B: #
#     print(cell.value)

# col_range = ws["b:c"] #영어 수학 컬럼 함께 가져오기
# for cols in col_range:
#     for celll in cols:
#         print(celll.value)

# row_title = ws[1] # 첫번째 row 만 들고 오기
# for cell in row_title:
#     print(cell.value)

# row_range = ws[2:6] #1번째 줄 제외 2번째 줄에서 6번째 줄까지 가지고 오기
# for rows in row_range:
#     for cell in rows:
#         print(cell.value, end=" ")
#     print()

# from openpyxl.utils.cell import coordinate_from_string

# row_range = ws[2:ws.max_row]
# for rows in row_range:
#     for cell in rows:
#         # print(cell.value, end=" ")
#         # print(cell.coordinate, end=" ") #좌표 정보 가져오기
#         xy = coordinate_from_string(cell.coordinate) #A/10 AZ/250...
#         # print(xy, end=" ")
#         print(xy[0],end="") #A
#         print(xy[1],end=" ") #1

#     print()

# 전체 로우 들고오기
# print(tuple(ws.rows))

#전체 columns
# print(tuple(ws.columns))

# for row in tuple(ws.rows):
#     print(row[1].value)

# for column in tuple(ws.columns):
#     print(column[0].value)

# for row in ws.iter_rows(): #전체 row
#     print(row[2].value)

# for column in ws.iter_cols(): #전체 column
#     print(column[0].value ,end=" ")

#2번째 줄 부터 11번째 줄까지, 2번째 열부터 3번째 열까지
# for row in ws.iter_rows(min_row=2,max_row=11,min_col=2,max_col=3):
#     # print(row[0].value, row[1].value) #수학, 영어
#     print(row)

for col in ws.iter_cols(min_row=1, max_row=5, min_col=1, max_col=3):
    print(col)

wb.save("sample.xlsx")


from typing import Any, Dict

from openslides_backend.permissions.permissions import Permissions
from tests.system.action.base import BaseActionTestCase

class MediafileUnsetFontActionTest(BaseActionTestCase):
    def setUp(self) -> None:
        super().setUp()
        self.permission_test_models: Dict[str, Dict[str, Any]] = {
            "meeting/1": {
                "name": "name_meeting1",
                "font_projector_h1_id": 17,
                "font_projector_h2_id": 17,
                "is_active_in_organization_id": 1,
            },
            "mediafile/17": {
                "is_directory": False,
                "mimetype": "image/png",
                "owner_id": "meeting/1",
                "used_as_font_projector_h1_in_meeting_id": 1,
                "used_as_font_projector_h2_in_meeting_id": 1,
            },
        }

    def test_unset_font(self) -> None:
        self.set_models(
            {
                "meeting/222": {
                    "name": "name_meeting222",
                    "font_projector_h1_id": 17,
                    "font_projector_h2_id": 17,
                    "is_active_in_organization_id": 1,
                },
                "mediafile/17": {
                    "is_directory": False,
                    "mimetype": "image/png",
                    "owner_id": "meeting/222",
                    "used_as_font_projector_h1_in_meeting_id": 222,
                    "used_as_font_projector_h2_in_meeting_id": 222,
                },
            }
        )
        response = self.request(
            "meeting.unset_font", {"id": 222, "place": "projector_h1"}
        )
        self.assert_status_code(response, 200)
        model = self.get_model("meeting/222")
        assert model.get("font_projector_h1_id") is None
        assert model.get("font_projector_h2_id") == 17

    def test_unset_font_no_permissions(self) -> None:
        self.base_permission_test(
            self.permission_test_models,
            "meeting.unset_font",
            {"id": 1, "place": "bold"},
        )

    def test_unset_font_permissions(self) -> None:
        self.base_permission_test(
            self.permission_test_models,
            "meeting.unset_font",
            {"id": 1, "place": "bold"},
            Permissions.Meeting.CAN_MANAGE_LOGOS_AND_FONTS,
        )


import warnings
warnings.filterwarnings('ignore')
import pandas as pd
from pandasql import sqldf
pysqldf = lambda q: sqldf(q, globals())
oly_evts = pd.read_csv("../data/120-years-of-olympic-history-athletes-and-results/athlete_events.csv", header = 'infer')
oly_rgn = pd.read_csv("../data/120-years-of-olympic-history-athletes-and-results/noc_regions.csv", header='infer')
print(oly_evts.shape)
print(oly_rgn.shape)
oly_evts.head()

male_df = df[df.Sex=='M']
sport_weight_height_metrics = male_df.groupby(['Sport'])['Weight','Height'].agg(
  ['min','max','mean'])

sport_weight_height_metrics.Weight.dropna().sort_values('mean', ascending=False)[:5]
sns.distplot(sport_weight_height_metrics.Weight.dropna()['mean'])

means = list(sport_weight_height_metrics.Weight.dropna()['mean'])
sports = list(sport_weight_height_metrics.Weight.dropna().index)
plot_data = sorted(zip(sports, means), key = lambda x:x[1])
plot_data_dict = {
    'x' : [i for i, _ in enumerate(plot_data)],
    'y' : [v[1] for i, v in enumerate(plot_data)],
    'group' :  [v[0] for i, v in enumerate(plot_data)]
}
sns.scatterplot(data = plot_data_dict, x = 'x' , y = 'y')

print('lightest:')
for sport,weight in plot_data[:5]:
    print(sport + ': ' + str(weight))

print('\nheaviest:')
for sport,weight in plot_data[-5:]:
    print(sport + ': ' + str(weight))

mean_heights = sport_weight_height_metrics.Height.dropna()['mean']
mean_weights = sport_weight_height_metrics.Weight.dropna()['mean']
avg_build = mean_weights/mean_heights
avg_build.sort_values(ascending = True)
builds = list(avg_build.sort_values(ascending = True))

plot_dict = {'x':[i for i,_ in enumerate(builds)],'y':builds}
sns.lineplot(data=plot_dict, x='x', y='y')

from collections import Counter

sport_min_year = male_df.groupby('Sport').Year.agg(['min','max'])['min'].sort_values('index')
year_count = Counter(sport_min_year)
year = list(year_count.keys())
new_sports = list(year_count.values())

data = {'x':year, 'y':new_sports}
sns.scatterplot(data=data, x = 'x', y='y')


import discord
from discord.ext import tasks
from Leetcode import get_total_question_solved, get_recent_AC_submission
from utils import log, timestampToString, loadUsers, saveUser
from dotenv import load_dotenv
import os

load_dotenv()
TOKEN = os.getenv('TOKEN')
NOTIFICATION_CHANNEL = int(os.getenv('NOTIFICATION_CHANNEL'))
bot = discord.Bot()

usernameAndQuestionCount = {}
usernameList = []
nextUser = 0
userCount = 0

@bot.event
async def on_ready():
    stalk.start()
    print(f"{bot.user} is ready and online!")

@bot.slash_command(name = "ping", description = "Get user's latency to bot server")
async def ping(ctx):
    await ctx.respond(f'{ctx.author}\'s ping is {round(bot.latency * 1000)}ms')

@bot.slash_command(name = "add_leetcode", description = "Add a leetcode profile to be tracked")
async def add(ctx, username):
    if username in usernameAndQuestionCount:
        await ctx.respond(f"User **{username}** already been tracked")
        return

    global userCount
    count, error = get_total_question_solved(username)
    if error != None:
        await ctx.respond(f"Unable to get status for **{username}** with error [{error}]")
    else:
        await ctx.respond(f'User\'s added, **{username}** solved {count} questions')
        usernameAndQuestionCount[username] = count
        usernameList.append(username)
        userCount = len(usernameList)
        saveUser(username)

@tasks.loop(seconds=1)
async def stalk():
    channel = bot.get_channel(NOTIFICATION_CHANNEL)
    if userCount == 0:
        return
    global nextUser
    username = usernameList[nextUser]
    nextUser = (nextUser + 1) % userCount
    question_count = usernameAndQuestionCount[username]

    count, error = get_total_question_solved(username)
    if error != None:
        log(f"error [{error}] when stalking user {username}")
        return
    if count == question_count:
        return

    usernameAndQuestionCount[username] = count
    if count < question_count:
        log(f"something wrong, current solved questions ({count}) smaller than server recorded ({question_count}) for user {username}")
        return
    new_quesiton, error =  get_recent_AC_submission(username)
    if error != None:
        log(f"error [{error}] when get recent AC for user {username}")
        return
    await channel.send(f"**{username}** just solved **{new_quesiton['title']}** (https://leetcode.com/problems/{new_quesiton['titleSlug']}/) at {timestampToString(new_quesiton['timestamp'])}")

if __name__ == "__main__":
    usernameList = loadUsers()
    for username in usernameList:
        count, error = get_total_question_solved(username)
        if error != None:
            log(f"error [{error}] when get recent AC for user {username}")
            continue
        usernameAndQuestionCount[username] = count
    userCount = len(usernameList)
    bot.run(TOKEN)


import numpy as np
import cv2
import base64
from matplotlib import pyplot as plt
import matplotlib.patches as mpatches

def validate_segmentation(pet_mip, seg_pred):
    assert isinstance(
        seg_pred, np.ndarray), "Segmentation was not succesfully decoded as a numpy array"
    assert pet_mip.shape == seg_pred.shape, f"Segmentation of shape {seg_pred.shape} is not identical to image shape {pet_mip.shape}"

    unique_vals = list(np.unique(seg_pred))
    allowed_vals = [0, 255]
    unique_vals_str = ", ".join([str(x) for x in (unique_vals)])
    all_values_are_allowed = all(
        x in allowed_vals for x in unique_vals)
    assert all_values_are_allowed,  f"The segmentation contains values {{{unique_vals_str}}} but only values {{0,255}} are allowed"

    assert np.all(seg_pred[:, :, 0] == seg_pred[:, :, 1]) & np.all(
        seg_pred[:, :, 1] == seg_pred[:, :, 2]), "The segmentation values should be identical along the 3 color channels."

def dice_score(y_true: np.ndarray, y_pred:np.ndarray):
    y_true_bin = y_true > 0
    y_pred_bin = y_pred > 0
    return 2 * (y_true_bin & y_pred_bin).sum() / (y_true_bin.sum() + y_pred_bin.sum())

def encode_request(np_array: np.ndarray) -> str:
    # Encode the NumPy array as a png image
    success, encoded_img = cv2.imencode('.png', np_array)

    if not success:
        raise ValueError("Failed to encode the image")

    # Convert the encoded image to a base64 string
    base64_encoded_img = base64.b64encode(encoded_img.tobytes()).decode()

    return base64_encoded_img

def decode_request(request) -> np.ndarray:
    encoded_img: str = request.img
    np_img = np.fromstring(base64.b64decode(encoded_img), np.uint8)
    a = cv2.imdecode(np_img, cv2.IMREAD_ANYCOLOR)
    return a

def plot_prediction(mip,seg,seg_pred):

    score = dice_score(seg,seg_pred)
    print("Dice Score:", dice_score(seg,seg_pred))
    plt.figure(figsize=(9.2,3))

    plt.subplot(1,4,1)
    plt.imshow(mip)
    plt.axis("off")
    plt.title("PET MIP")

    plt.subplot(1,4,2)
    plt.imshow(seg)
    plt.axis("off")
    plt.title("True Segmentation")

    plt.subplot(1,4,3)
    plt.imshow(seg_pred)
    plt.axis("off")
    plt.title("Predicted Segmentation")

    TP = ((seg_pred>0)&(seg>0))[:,:,:1]
    FP = ((seg_pred>0)&(seg==0))[:,:,:1]
    FN = ((seg_pred==0)&(seg>0))[:,:,:1]
    img = np.concatenate((FP,TP,FN),axis=2).astype(np.uint8)*255

    plt.subplot(1,4,4)
    plt.imshow(img)
    plt.axis("off")
    plt.title(f"dice score = {score:.02f}")
    plt.legend(["a","b"])

    # Create green, red, and blue squares as proxy artists
    green_square = mpatches.Patch(color='green', label='TP')
    red_square = mpatches.Patch(color='red', label='FP')
    blue_square = mpatches.Patch(color='blue', label='FN')

    # Add the proxy artists to the legend
    plt.legend(handles=[green_square, red_square, blue_square],loc="lower right")
    plt.tight_layout(h_pad=2,w_pad=0,pad=1.5)
    plt.show()


# *_*coding:utf-8 *_*

from itertools import permutations
from apriori import apriori
import rdflib
import os, re, time, sys
import argparse

def get_index(g):
    index = 1
    dict_forward = {}
    dict_reverse = {}
    for subject, predicate, obj in g:
        if not (subject, predicate, obj) in g:
            raise Exception("Iterator / Container Protocols are Broken!!")
        if dict_forward.get(str(subject), -1) == -1:
            dict_forward[str(subject)] = index
            dict_reverse[index] = str(subject)
            index = index + 1
        if dict_forward.get(str(predicate), -1) == -1:
            dict_forward[str(predicate)] = index
            dict_reverse[index] = str(predicate)
            index = index + 1
        if dict_forward.get(str(obj), -1) == -1:
            dict_forward[str(obj)] = index
            dict_reverse[index] = str(obj)
            index = index + 1
    return dict_forward,dict_reverse

def match_path(g,path,path1,dict_forward):
    match_result = {}
    if len(path) == 1:
        for sub in g.subjects():
            if re.search(path[0], sub) is None:
                continue
            if match_result.get(sub, -1) == -1:
                match_result[sub] = {sub}
            else:
                match_result[sub].add(sub)
    else:
        if int(path1[0]) == 1:
            for sub in g.subjects():
                if re.search(path[0], sub) is None:
                    continue
                for pre, obj in g.predicate_objects(sub):
                    if re.search(path[1], obj) is None:
                        continue
                    if match_result.get(sub, -1) == -1:
                        match_result[sub] = {obj}
                    else:
                        match_result[sub].add(obj)
        else:
            for obj in g.objects():
                if re.search(path[0], obj) is None:
                    continue
                for sub, pre in g.subject_predicates(obj):
                    if re.search(path[1], sub) is None:
                        continue
                    if match_result.get(obj, -1) == -1:
                        match_result[obj] = {sub}
                    else:
                        match_result[obj].add(sub)
        for idx, val in enumerate(path1[1:], 1):
            if int(val) == 1:
                for key, values in match_result.items():
                    set_tmp = set()
                    for value in values:
                        for pre, obj in g.predicate_objects(value):
                            if re.search(path[idx + 1], obj) is None:
                                continue
                            set_tmp.add(obj)
                    match_result[key] = set_tmp
            else:
                for key, values in match_result.items():
                    set_tmp = set()
                    for value in values:
                        for sub, pre in g.subject_predicates(value):
                            if re.search(path[idx + 1], sub) is None:
                                continue
                            set_tmp.add(sub)
                    match_result[key] = set_tmp
    record = []
    for key, values in match_result.items():
        for value in values:
            list_tmp = []
            for pre, obj in g.predicate_objects(key):
                list_tmp.append(dict_forward[str(obj)])
            for sub, pre in g.subject_predicates(key):
                list_tmp.append(dict_forward[str(sub)])
            if key == value:
                continue
            for pre, obj in g.predicate_objects(value):
                list_tmp.append(dict_forward[str(obj)])
            for sub, pre in g.subject_predicates(value):
                list_tmp.append(dict_forward[str(sub)])
            list_tmp = list(set(list_tmp))
            record.append(list_tmp)
    return record
def match_path2(g,path,path1,dict_forward):
    match_result = {}
    if len(path) == 1:
        for sub in g.subjects():
            if sub.find(path[0]) < 0:
                continue
            if match_result.get(sub, -1) == -1:
                match_result[sub] = {sub}
            else:
                match_result[sub].add(sub)
    else:
        if int(path1[0]) == 1:
            for sub in g.subjects():
                if sub.find(path[0]) < 0:
                    continue
                for pre, obj in g.predicate_objects(sub):
                    if obj.find(path[1]) < 0:
                        continue
                    if match_result.get(sub, -1) == -1:
                        match_result[sub] = {obj}
                    else:
                        match_result[sub].add(obj)
        else:
            for obj in g.objects():
                if obj.find(path[0]) < 0:
                    continue
                for sub, pre in g.subject_predicates(obj):
                    if sub.find(path[1]) < 0:
                        continue
                    if match_result.get(obj, -1) == -1:
                        match_result[obj] = {sub}
                    else:
                        match_result[obj].add(sub)
        for idx, val in enumerate(path1[1:], 1):
            if int(val) == 1:
                for key, values in match_result.items():
                    set_tmp = set()
                    for value in values:
                        for pre, obj in g.predicate_objects(value):
                            if obj.find(path[idx + 1]) < 0:
                                continue
                            set_tmp.add(obj)
                    match_result[key] = set_tmp
            else:
                for key, values in match_result.items():
                    set_tmp = set()
                    for value in values:
                        for sub, pre in g.subject_predicates(value):
                            if sub.find(path[idx + 1]) < 0:
                                continue
                            set_tmp.add(sub)
                    match_result[key] = set_tmp
    record = []
    for key, values in match_result.items():
        for value in values:
            list_tmp = []
            for pre, obj in g.predicate_objects(key):
                list_tmp.append(dict_forward[str(obj)])
            for sub, pre in g.subject_predicates(key):
                list_tmp.append(dict_forward[str(sub)])
            if key == value:
                continue
            for pre, obj in g.predicate_objects(value):
                list_tmp.append(dict_forward[str(obj)])
            for sub, pre in g.subject_predicates(value):
                list_tmp.append(dict_forward[str(sub)])
            list_tmp = list(set(list_tmp))
            record.append(list_tmp)
    return record

def match_sparql(g,sparql_query,dict_forward):
    qres = g.query(sparql_query)
    record = []
    for row in qres:
        list_tmp = []
        for item in row:
            for pre, obj in g.predicate_objects(item):
                list_tmp.append(dict_forward[str(obj)])
            for sub, pre in g.subject_predicates(item):
                list_tmp.append(dict_forward[str(sub)])
        list_tmp = list(set(list_tmp))
        record.append(list_tmp)
    return record

def write_file(graphfile, record,query_node,dict_reverse):
    itemfile = "path_itemsets.txt"
    rulesfile = "path_rules.txt"
    rulesdict_file= "path_rule_dict.text"
    if os.path.exists(itemfile):
        os.remove(itemfile)
    if os.path.exists(rulesfile):
        os.remove(rulesfile)
    if os.path.exists(rulesdict_file):
        os.remove(rulesdict_file)
    itemfile_write = open(itemfile, 'a', encoding='utf-8')
    rulesfile_write = open(rulesfile, 'a', encoding='utf-8')

    #print(len(record))
    itemsets, rules = apriori(record,query_node=query_node, min_support=0.5, min_confidence=0.9)
    for (key, value) in itemsets.items():
        itemset = []
        for item_tuple in value.keys():
            for item in item_tuple:
                itemset.append(dict_reverse[item])
        #print(itemset)
        itemfile_write.write(str(itemset))
        itemfile_write.write("\n")
    rule_dict = {}
    for rule in rules:
        lhs_itemset = []
        rhs_itemset = []
        for item in rule.lhs:
            lhs_itemset.append(dict_reverse[item])
        for item in rule.rhs:
            rhs_itemset.append(dict_reverse[item])
        for item in rule.lhs+rule.rhs:
            if rule_dict.get(dict_reverse[item], -1) == -1:
                rule_dict[dict_reverse[item]] = [str(lhs_itemset) + "->" + str(rhs_itemset)+"\tsupport="+str(rule.support)+"\tconfidence="+str(rule.confidence)+'\tlift='+str(rule.lift)]
            else:
                rule_dict[dict_reverse[item]].append(str(lhs_itemset) + "->" + str(rhs_itemset)+"\tsupport="+str(rule.support)+"\tconfidence="+str(rule.confidence)+'\tlift='+str(rule.lift))

        rulesfile_write.write(str(lhs_itemset))
        rulesfile_write.write("->")
        rulesfile_write.write(str(rhs_itemset))
        rulesfile_write.write("\n")
    with open(rulesdict_file, 'w', encoding='utf8') as outfile:
        for key, values in rule_dict.items():
            outfile.write(key + ":[")
            for value in values:
                lhs_itemset = []
                rhs_itemset = []
                for item in rule.lhs:
                    lhs_itemset.append(dict_reverse[item])
                for item in rule.rhs:
                    rhs_itemset.append(dict_reverse[item])
                outfile.write(str(lhs_itemset))
                outfile.write("->")
                outfile.write(str(rhs_itemset))
                outfile.write(", ")
            outfile.write("]\n")
    return rule_dict
def get_query_node_result(query_node,rule_dict,dict_reverse):
    rule_set=set(rule_dict[dict_reverse[query_node[0]]])
    for node in query_node[1:]:
        node=dict_reverse[node]
        rule_set=rule_set.intersection(rule_dict[node])
    with open("query_result.txt",'w',encoding='utf8') as outfile:
        for rule in rule_set:
            outfile.write(rule+'\n')
    for i in range(len(query_node)):
        query_node[i]=dict_reverse[query_node[i]]

    for p in permutations(query_node):
        for i in range(1,len(query_node)):
            rule_str='[\''
            for j in range(i):
                rule_str+=p[j]+'\', \''
            rule_str=rule_str[:-3]+']->[\''
            for j in range(i,len(p)):
                rule_str+=p[j]+'\', \''
            rule_str=rule_str[:-3]+']'
            result_dict={"rule":rule_str}
            for rule in rule_set:
                if rule.find(rule_str)>=0:
                    re_group=re.search('support=(.*?)confidence=(.*?)lift=(.*)', rule)
                    result_dict["support"]=re_group[1]
                    result_dict["confidence"]=re_group[2]
                    result_dict["lift"]=re_group[3]
                    print(result_dict)

def main_algorithm(args):
    graphfile = args.graph_file
    g = rdflib.Graph()
    result = g.parse(graphfile, format="nt")
    dict_forward,dict_reverse=get_index(g)
    query_node=args.query_node.strip('\n').split(',')
    for i in range(len(query_node)):
        query_node[i]='http://www.tsinghua-west.com/Guxi/'+query_node[i]

    if args.query_type == 'path':
        path_node_list=args.query_file.strip('\n').split(',')
        path=path_node_list[:int((len(path_node_list)+1)/2)]
        path1=path_node_list[int((len(path_node_list)+1)/2):]
        #print(path,path1)
        match_result = match_path2(g, path, path1, dict_forward)
        for i in range(len(query_node)):
            query_node[i]=dict_forward[query_node[i]]

        rule_dict=write_file(g,match_result,query_node,dict_reverse)
        get_query_node_result(query_node,rule_dict,dict_reverse)
    else:
        sparql_query=args.query_file
        sparql_query=sparql_query.replace('_',' ')
        match_result = match_sparql(g, sparql_query, dict_forward)
        for i in range(len(query_node)):
            query_node[i]=dict_forward[query_node[i]]
        rule_dict = write_file(g, match_result, query_node, dict_reverse)
        get_query_node_result(query_node, rule_dict, dict_reverse)

if __name__ == '__main__':
    start_time=time.time()
    parser = argparse.ArgumentParser()
    basic = parser.add_argument('-g', '--graph_file',dest='graph_file', type=str, required=True, help='input graph file')
    basic = parser.add_argument('-t', '--query_type',dest='query_type', type=str, required=True, help='query type')
    basic = parser.add_argument('-q', '--query_file', dest='query_file', type=str, required=True, help='query file')
    basic = parser.add_argument('-x', '--query_node', dest='query_node', type=str, required=True, help='query node')
    if len(sys.argv[1:])!=8:
        parser.print_help()        #print usage
        parser.exit()
    args = parser.parse_args()

    main_algorithm(args)


from collections import deque

INF = int(1e10)

def bfs(board, cost, N):
    global answer
    dy = [0, 1, 0, -1]
    dx = [1, 0, -1, 0]
    cost[0][0][0] = cost[1][0][0] = 0
    q = deque([(0, 0, 0)])
    q = deque([(0, 0, 1)])
    while q:
        y, x, d = q.popleft()
        for i in range(4):
            ny = y + dy[i]
            nx = x + dx[i]
            nd = i
            if 0 <= ny < N and 0 <= nx < N and board[ny][nx] == 0:
                c = cost[d][y][x] + 100
                if (x, y) != (0, 0) and nd != d:
                    c += 500
                if c < cost[nd][ny][nx]:
                    cost[nd][ny][nx] = c
                    if ny == N - 1 and nx == N - 1:
                        answer = min(answer, c)
                    else:
                        q.append((ny, nx, nd))

def solution(board):
    global answer
    answer = INF
    N = len(board)
    cost = [[[INF] * N for _ in range(N)] for _ in range(4)]
    bfs(board, cost, N)
    return answer


"""
Neural State Space Models - N-step ahead System ID
"""
import numpy as np
import matplotlib.pyplot as plt
import torch
import pandas as pd
import scipy.linalg as LA

import os
import sys
sys.path.append(os.path.abspath('../'))
os.chdir('../')
from system_id_nlin import SSM_black, SSM_gray, SSM_white, RNN, Building_DAE
from system_id_nlin_con import SSM_black_con, SSM_gray_con, SSM_white_con, Building_DAE

def plot_matrices(matrices, labels, figname):
    rows = len(matrices)
    cols = len(matrices[0])
    fig, axes = plt.subplots(nrows=rows, ncols=cols)

    for i in range(rows):
        for j in range(cols):
            axes[i, j].imshow(matrices[i][j])
            axes[i, j].set(xticklabels= [],
                            xticks = [],
                            yticks=[],
                            yticklabels=[])
    axes[0, 0].set_xlabel('True System')
    axes[0, 1].set_xlabel('PI-RNN')
    axes[1, 0].set_xlabel('$ODE_B$')
    axes[1, 1].set_xlabel('$ODE_G$')
    axes[2, 0].set_xlabel('$ODE_w$')
    axes[2, 1].set_xlabel('$cODE_B$')
    axes[3, 0].set_xlabel('$cODE_G$')
    axes[3, 1].set_xlabel('$cODE_W$')
    plt.tight_layout()
    plt.savefig(figname+'.pdf')
    plt.savefig(figname+'.png')

nx, n_m, n_dT, nu, nd, n_hidden = 4, 1, 1, 1, 3, 8

ssmwhite = SSM_white(nx, n_m, n_dT, nu, nd, n_hidden, bias=False, device='cpu')
ssmwhite.load_state_dict(torch.load('iclr_models/ssmwhite128.pth', map_location=torch.device('cpu')))

ssmgray = SSM_gray(nx, n_m, n_dT, nu, nd, n_hidden, bias=False, device='cpu')
ssmgray.load_state_dict(torch.load('iclr_models/ssmgray64.pth', map_location=torch.device('cpu')))

ssmblack = SSM_black(nx, n_m, n_dT, nu, nd, n_hidden, bias=False, device='cpu')
ssmblack.load_state_dict(torch.load('iclr_models/ssmblack64.pth', map_location=torch.device('cpu')))

ssmwhite_con = SSM_white_con(nx, n_m, n_dT, nu, nd, n_hidden, bias=False)
ssmwhite_con.load_state_dict(torch.load('iclr_models/ssmwhitecon128.pth', map_location=torch.device('cpu')))

ssmgray_con = SSM_gray_con(nx, n_m, n_dT, nu, nd, n_hidden, bias=False)
ssmgray_con.load_state_dict(torch.load('iclr_models/ssmgraycon128.pth', map_location=torch.device('cpu')))

ssmblack_con = SSM_black_con(nx, n_m, n_dT, nu, nd, n_hidden, bias=False)
ssmblack_con.load_state_dict(torch.load('iclr_models/ssmblackcon128.pth', map_location=torch.device('cpu')))

rnn = RNN(nx, n_m, n_dT, nu, nd, n_hidden, bias=False)
rnn.load_state_dict(torch.load('iclr_models/rnn.pth', map_location=torch.device('cpu')))
building = Building_DAE()

a_matrices = [(np.asarray(building.A), rnn.cells[-1].weight_hh.data.numpy()),
              (ssmblack.A.effective_W().T.detach().cpu().numpy(), ssmgray.A.effective_W().T.detach().cpu().numpy()),
              (ssmwhite.A.effective_W().T.detach().cpu().numpy(), ssmblack_con.A.effective_W().T.detach().cpu().numpy()),
              (ssmgray_con.A.effective_W().T.detach().cpu().numpy(), ssmwhite_con.A.effective_W().T.detach().cpu().numpy())]
plot_matrices(a_matrices, None, 'parameters')

df = pd.DataFrame(index=['True', 'RNN', '$ODE_B$', '$ODE_G$', '$ODE_W$', '$cODE_B$', '$cODE_G$', '$cODE_W$'], columns=['$\lambda_1$', '$\lambda_2$', '$\lambda_3$', '$\lambda_4$'])
a_matrices = [np.asarray(building.A), rnn.cells[-1].weight_hh.data.numpy(),
              ssmblack.A.effective_W().T.detach().cpu().numpy(), ssmgray.A.effective_W().T.detach().cpu().numpy(),
              ssmwhite.A.effective_W().T.detach().cpu().numpy(), ssmblack_con.A.effective_W().T.detach().cpu().numpy(),
              ssmgray_con.A.effective_W().T.detach().cpu().numpy(), ssmwhite_con.A.effective_W().T.detach().cpu().numpy()]
for model, mat in zip(['True', 'RNN', '$ODE_B$', '$ODE_G$', '$ODE_W$', '$cODE_B$', '$cODE_G$', '$cODE_W$'], a_matrices):
    print(model)
    w, v = LA.eig(mat)
    df.loc[model] = w
print(df.to_latex(float_format=lambda x: '%.3f' % x))


import os
os.environ["CUDA_VISIBLE_DEVICES"]="1"

import sys
import math
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error
from sklearn.metrics import precision_score
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import f1_score
from sklearn.metrics import recall_score
from sklearn import metrics
from sklearn.model_selection import GridSearchCV
from collections import Counter

def create_missing_indicator(org, data, columns):
    for col in columns:
        selected = pd.isnull(org[col])
        data.loc[selected, col + "_missing"] = 1
        data.loc[~selected, col + "_missing"] = 0
    return data.loc[:,[c for c in data.columns.values if 'missing' in c]]

def create_model():
    model = tf.keras.models.Sequential()
    model.add(tf.keras.layers.LSTM(128, input_dim=X_train.shape[2]))
    model.add(tf.keras.layers.Dense(128, activation='relu'))
    model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
    lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
        initial_learning_rate,
        decay_steps=decay_steps,
        decay_rate=decay_rate,
        staircase=True)
    optimizer = tf.keras.optimizers.Adam(learning_rate = lr_schedule)
    model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
    early_stopping = tf.keras.callbacks.EarlyStopping(monitor='loss', verbose=1, mode='auto')
    return model

if __name__ == '__main__':
	dataset = sys.argv[1]
	missing_indicator = int(sys.argv[2])

	# path storing complete data
	path = './result/' + dataset + '_24h_imputation_result/'
	# path storing data with native missing values for MI
	org_path = './data/'+dataset+ '/data_with_missing/'
	# path storing prediction results
	save = './result/' + dataset + '_24h_prediction/'

	if dataset == 'cchs':
	    NUM = 2842
	elif dataset == 'mayo':
	    NUM = 6836
	elif dataset == 'mimic':
	    NUM = 772

	max_length = 70 # maximum sequence length

	# parameters of LSTM
	initial_learning_rate = 0.005
	decay_steps = 1000
	decay_rate = 0.96
	nodeN = 128

	df = pd.read_csv(path + str(1) + '.csv')
	org_df = pd.read_csv(org_path + str(1) + '.csv')
	visitIdx = [1 for d in range(len(df))]
	df['VisitIdx'] = visitIdx

	for i in range(2, NUM+1):
	    tmp_df = pd.read_csv(path + str(i) + '.csv')
	    tmp_org_df = pd.read_csv(org_path + str(i) + '.csv')
	    visitIdx = [i for d in range(len(tmp_df))]
	    tmp_df['VisitIdx'] = visitIdx
	    df = pd.concat([df, tmp_df], ignore_index=True)
	    org_df = pd.concat([org_df, tmp_org_df], ignore_index=True)

	numeric_columns = [c for c in df.columns.values if 'X' in c]
	mi_columns = create_missing_indicator(org_df, df, numeric_columns)
	print('Imputed data contains any NaN?', df[numeric_columns].isnull().values.any())
	new_columns = ['VisitIdx'] + numeric_columns
	if missing_indicator:
	    new_columns += [c for c in mi_columns.columns.values if 'missing' in c]
	df[numeric_columns] = (df[numeric_columns] - df[numeric_columns].mean())/df[numeric_columns].std()

	X = []
	y = []
	lengths = []

	# Separate input sequence X and target y
	targets = {}
	selected = (df.VisitIdx <= int(NUM/2))
	for v in df.loc[selected, :].VisitIdx.unique().tolist():
	    targets[str(int(v))] = 1
	selected = (df.VisitIdx > int(NUM/2))
	for v in df.loc[selected, :].VisitIdx.unique().tolist():
	    targets[str(int(v))] = 0

	sequences = {}
	for row in df.loc[:, new_columns].values:
	    if str(int(row[0])) not in sequences:
	        sequences[str(int(row[0]))] = []
	    sequences[str(int(row[0]))].append(row[1:])

	for key in sequences.keys():
	    X.append(sequences[key])
	    y.append(targets[key])
	    lengths.append(len(sequences[key]))

	print("mean length of seqs:", np.array(lengths).mean())
	print ('Number of septic shock/ non septic shock patients')
	print(Counter(y))
	X = np.array(X)
	y = np.array(y)

	X = tf.compat.v1.keras.preprocessing.sequence.pad_sequences(X, maxlen=max_length, padding = 'pre', truncating='pre',dtype = 'float64')
	X = np.reshape(X, (X.shape[0], max_length, X.shape[2]))
	print('X dim after padding:', X.shape)

	tf.compat.v1.reset_default_graph()
	tf.compat.v1.disable_eager_execution()
	np.random.seed(1000)

	cv_folds = 5

	kf = KFold(n_splits=cv_folds, shuffle=True)

	performances = pd.DataFrame(columns=['Accuracy', 'std' ,'Precision', 'std', 'Recall', 'std', 'F-measure', 'std', 'AUC', 'std'])
	accuracy, precision, recall, f_measure, auc_roc = [0] * cv_folds, [0] * cv_folds, [0] * cv_folds, [0] * cv_folds, [0] * cv_folds
	fold = 0
	for train_index, test_index in kf.split(X):
	    print('#'*15, "Cross-Validation fold", fold)

	    X_train, X_test = X[train_index], X[test_index]
	    y_train, y_test = y[train_index], y[test_index]

	    model = tf.keras.wrappers.scikit_learn.KerasClassifier(build_fn=create_model, verbose=1)
	    epochs = [25]
	    batches = [64]
	    param_grid = dict(epochs=epochs, batch_size=batches)
	    grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=2)
	    # with tf.device('/gpu:2'):
	    grid_result = grid.fit(X_train, y_train)

	    print(grid_result.best_params_)

	    predict = grid_result.predict(X_test)
	    predict_prob = grid_result.predict_proba(X_test)
	    test_pred, test_pred_prob = [], []
	    for each in predict:
	        test_pred.append(each[0])
	    for each in predict_prob:
	        test_pred_prob.append(each[0])

	    accuracy[fold] = accuracy_score(y_test, test_pred)
	    recall[fold] =  recall_score(y_test, test_pred)
	    f_measure[fold] = f1_score(y_test, test_pred)
	    precision[fold] = precision_score(y_test, test_pred)
	    fpr, tpr, thresholds = metrics.roc_curve(y_test, test_pred_prob, pos_label=0)
	    auc_roc[fold] = metrics.auc(fpr, tpr)
	    print ("Confusion Matrix:")
	    print (confusion_matrix(y_test, test_pred))
	    fold += 1

	performances.loc[0] = [np.mean(accuracy), np.std(accuracy), np.mean(precision), np.std(precision),\
	                         np.mean(recall), np.std(recall), np.mean(f_measure), np.std(recall), np.mean(auc_roc), np.std(auc_roc)]
	performances.to_csv(save+'lstm_'+dataset + '_MI' + str(missing_indicator)+'_lr_' + str(initial_learning_rate) + "_" + str(decay_steps) + "_" + str(decay_rate) + '.csv', sep='\t')