import pr_util as util
import numpy as np
import datetime
import time

from sklearn import svm, neighbors
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import cross_val_score
from generate_global_features_2 import generate_global_features

from multiprocessing import Pool, Lock, cpu_count, RLock

def create_table(features):
    table = []
    for feat in features:
        table.append([feat])
    return table

def print_table(table):
    print(' | '.rjust(23), end = '')
    for classifier in util.CLASSIFIERS:
        print('{} | '.format(classifier.rjust(20)), end = '')
    print()
    for line in table:
        for element in line:
            print('{} | '.format(str(element).rjust(20)), end = '')
        print()

def write_table(table, file_exp):
    file_exp.write(' | '.rjust(23))
    for classifier in util.CLASSIFIERS:
        file_exp.write('{} | '.format(classifier.rjust(20)))
    file_exp.write('\n')
    for line in table:
        for element in line:
            file_exp.write('{} | '.format(str(element).rjust(20)))
        file_exp.write('\n')
    file_exp.write('\n')

def generate_results_table(table, clf, scoring, scores):
    result = '{0:.2f} (+/- {1:.2f})'.format(scores.mean(), scores.std() * 2)
    table.append(result)
    print('{0} - {1}: {2:.2f} (+/- {3:.2f})'.format(clf, scoring, scores.mean(), scores.std() * 2))
    print(scores)

def generate_experiments(num_species, file_exp, song_or_call = 'song', scoring = 'f1_weighted'):

    n_global = 4

    data_dirs = util.choose_species(num_species, num_min, song_or_call)
    #data_dirs = util.check_num_files(data_dirs, song_or_call, num_species, num_min)

    print("Diretórios: ")
    for dir in data_dirs:
        print(dir)
    print()

    file_exp.write("Diretórios: \n")
    for dir in data_dirs:
        file_exp.write("{} \n".format(dir))
    print()

    file_exp.write("type of score: {}\n".format(scoring))
    table = create_table(util.FEATURES)

    for version in util.VERSIONS_EXPERIMENTS:
        i = 0
        table = create_table(util.FEATURES)
        for feat in util.FEATURES:
            print('Feature: {} | Version: {}'.format(feat, version))
            labels_dict, labels, data = generate_global_features(n_global, feat, data_dirs, song_or_call, util.GLOBAL_FUNCTIONS, version = version)

            clf     = neighbors.KNeighborsClassifier(3, weights = 'uniform')
            scores  = cross_val_score(clf, data, labels, n_jobs = -1, cv = 5, scoring=scoring)
            generate_results_table(table[i], 'kNN', scoring, scores)

            # naïve-bayes
            gnb    = GaussianNB()
            scores = cross_val_score(gnb, data, labels, n_jobs = -1, cv = 5, scoring=scoring)
            generate_results_table(table[i], 'GaussianNB', scoring, scores)

            # SVM
            #clf = svm.SVC(kernel = 'rbf', C = 1)
            #clf = svm.SVC(kernel = 'poly', C = 1)

            clf = svm.SVC(kernel = 'linear', C = 1, decision_function_shape='ovr')
            file_exp.write(str(clf) + '\n')
            scores = cross_val_score(clf, data, labels, n_jobs = -1, cv = 5, scoring=scoring)
            generate_results_table(table[i], 'SVM', scoring, scores)

            print()
            i += 1

        file_exp.write('Type of recording: ' + str(version) + '\n')
        print_table(table)
        write_table(table, file_exp)

def generate_exp_file(num_exp, num_min, song_or_call, spc):
    spcs = ''
    for s in spc:
        spcs += '_' + str(s)

    spcs += '_'

    return 'experiment-' + 'spcs' + spcs + '-' + 'numexps-' + str(num_exp) + '-num_min-' + str(num_min) + '-' + str(song_or_call) + '-' + datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')

# info[0] = feature
# info[1] = version
# info[2] = data_dirs
# info[3] = song_or_call
# info[4] = n_species
# info[5] = scoring

def generate_experiment(info):
    n_function_global =  4

    feat         = info[0]
    version      = info[1]
    data_dirs    = info[2]
    song_or_call = info[3]
    n_species    = info[4]
    scoring      = info[5]
    num_min      = info[6]
    num_exp      = info[7]
    num_max      = info[8]
    kernel       = 'linear'
    k            = 3
    cv           = 5

    print("{} {} {} {} {}".format(feat, version, data_dirs, song_or_call, n_species))
    print(data_dirs)
    labels_dict, labels, data = generate_global_features(n_function_global, feat, data_dirs, song_or_call, util.GLOBAL_FUNCTIONS, version = version)

    len_data = len(data)
    len_labels = len(labels)
    #print("len data: {}".format(len_data))
    #print("len labels: {}".format(len_labels))

    if len_data != len_labels or data == [] or len_data == 0 or len_labels == 0:
        print("ACHAMOS UMA INCOSISTENCIA")
        print(data_dirs)
        resp = dict(n_species = n_species,  feat = feat, version = version, dirs = data_dirs, song_or_call = song_or_call, scoring = scoring, knn = '-1', gnb = '-1', svm = '-1', num_min = num_min, num_max = num_max, num_exp = num_exp, kernel = kernel, kNN = k, cv = cv)

    else:
        #print("kNN Starting ->")
        clf         = neighbors.KNeighborsClassifier(k, weights = 'uniform')
        scores      = cross_val_score(clf, data, labels, n_jobs = 1, cv = cv, scoring=scoring)
        result_knn  = '{0:.2f} (+/- {1:.2f})'.format(scores.mean(), scores.std() * 2)
        #print("[DONE] kNN Done <-")

        #print("GNB Starting ->")
        gnb        = GaussianNB()
        scores     = cross_val_score(gnb, data, labels, n_jobs = 1, cv = cv, scoring=scoring)
        result_gnb = '{0:.2f} (+/- {1:.2f})'.format(scores.mean(), scores.std() * 2)
        #print("[DONE] GNB Done <-")

        #print("SVM Starting ->")
        clf        = svm.SVC(kernel = kernel, C = 1, decision_function_shape='ovr')
        scores     = cross_val_score(clf, data, labels, n_jobs = 1, cv = cv, scoring=scoring)
        result_svm = '{0:.2f} (+/- {1:.2f})'.format(scores.mean(), scores.std() * 2)
        #print("[DONE] SVM Done <-")

        resp = dict(n_species = n_species,  feat = feat, version = version, dirs = data_dirs, song_or_call = song_or_call, scoring = scoring, knn = result_knn, gnb = result_gnb, svm = result_svm, num_min = num_min, num_max = num_max, num_exp = num_exp, kernel = kernel, kNN = k, cv = cv)

    return resp

def generate_info(num_species, num_exp, num_min, num_max, song_or_call):
    print("Generating infos for parallel...")
    infos = []
    for n in range(num_exp):
        for spc in num_species:
            if num_min == -1:
                DIRS = util.return_n_most_frequent_species(spc, song_or_call)
            else:
                DIRS = util.choose_species(spc, num_min, num_max, song_or_call)
            for feat in util.FEATURES:
                for version in util.VERSIONS_EXPERIMENTS:
                    print((feat, version, DIRS, song_or_call, spc, 'f1_weighted'))
                    infos.append((feat, version, DIRS, song_or_call, spc, 'f1_weighted', num_min, n + 1, num_max))
    print("[DONE] Info generated.")
    print("Info lenght: {}".format(len(infos)))
    return infos

def write_info(d, file_exp):
    file_exp.write('\n--------------------------------------\n')
    file_exp.write('experimento número: {} \n'.format(d['num_exp']))
    file_exp.write('numero de especies: {}\n'.format(d['n_species']))
    file_exp.write('numero minimo de arquivos por especie (-1 most frequent): {}\n'.format(d['num_min']))
    file_exp.write('numero máximo de arquivos por especie: {}\n'.format(d['num_max']))
    file_exp.write('diretórios: {}\n'.format(d['dirs']))
    file_exp.write('scoring: {}\n'.format(d['scoring']))
    file_exp.write('song_or_call: {}\n'.format(d['song_or_call']))
    file_exp.write('versão: {}\n'.format(d['version']))
    file_exp.write('kernel SVM: {}\n'.format(d['kernel']))
    file_exp.write('k do kNN: {}\n'.format(d['kNN']))
    file_exp.write('numero cross-validation: {}'.format(d['cv']))
    file_exp.write('\n--------------------------------------\n')

def tables_from_dicts(dicts, file_exp):

    while len(dicts) > 0:
        lines = []
        ds = []
        current = dicts[0]

        for d in dicts:
            if d['dirs'] == current['dirs'] and d['version'] == current['version'] and d['num_exp'] == current['num_exp']:
                ds.append(d)

        for d in ds:
            dicts.remove(d)
            lines.append([d['feat'], d['knn'], d['gnb'], d['svm']])

        print_table(lines)
        write_info(ds[0], file_exp)
        write_table(lines, file_exp)

def experiments_parallel(num_exp, num_cores, num_min, num_max, song_or_call, spc):

    if num_cores == -2:
        num_cores = cpu_count()

    pool = Pool(num_cores)

    DIR = util.EXPERIMENTS_DIR + '/' + generate_exp_file(num_exp, num_min, song_or_call, spc)

    song_or_call = 'song'

    infos = generate_info(spc, num_exp, num_min, num_max, song_or_call)

    dicts = []

    with open(DIR, 'w') as f:
        for result in pool.imap(generate_experiment, infos):
            f.write(str(result))
            f.write('\n')
            f.write('\n')
            f.write('\n')
            dicts.append(result)
        pool.close()
        pool.join()
        tables_from_dicts(dicts, f)

def main():

    num_exp      = int(input("número de experimentos: "))
    spc          = str(input("lista com número de espécies separado por espaços (ex: 3 5 8): "))
    num_min      = int(input("número minimo de arquivos por especie (-1 para as n especies + freq): "))
    num_max      = int(input("número maximo de arquivos por especie: "))
    num_cores    = int(input("número de cores (-1 sem paralelismo, -2 número máximo possível): "))
    song_or_call = str(input("song or call: "))

    spc = [int(n) for n in spc.split(' ')]

    if num_cores != -1:
        experiments_parallel(num_exp, num_cores, num_min, num_max, song_or_call, spc)

    # else:
    #     #num_species = [3, 5, 8, 12, 20]
    #     num_species = [3]
    #     file_exp = open(util.EXPERIMENTS_DIR + '/' + generate_exp_file(), "w+")
    #     for num in num_species:
    #         file_exp.write('Número de espécies: {}\n'.format(num))
    #         for i in range(num_exp):
    #             print("Número espécie: {} | Exp: {}/{}".format(num, i + 1, num_exp))
    #             generate_experiments(num, file_exp, song_or_call = 'song')
    #     file_exp.close()

if __name__ == '__main__':
    main()


# Import modules
import tensorflow as tf
import numpy as np
import argparse
import os
import time
import h5py
import sys
import matplotlib.pyplot as plt
import horovod.tensorflow as hvd

# Prevent python from saving out .pyc files
sys.dont_write_bytecode = True

# Add models and tasks to path
sys.path.insert(0, './models')
sys.path.insert(0, './tasks')

# Import methods
from util import log
from input_ops import *

def check_path(path):
    if not os.path.exists(path):
        os.mkdir(path)

class Trainer(object):

    def __init__(self,config):

        # Initialize Horovod
        hvd.init()

        # Pin GPU to be used to process local rank (one GPU per process)
        self.session_config = tf.ConfigProto()
        self.session_config.gpu_options.visible_device_list = str(hvd.local_rank())
        self.session_config.gpu_options.allow_growth = True
        log.info('# of GPUs = ' + str(hvd.size()))

        # Config
        self.config = config
        self.config.N_GPUs = hvd.size()
        self.model_name = config.model_name

        # Get dataset path
        self.dataset_path = './datasets'

        # Get variable names to create input operations
        model_script_name = "model_" + self.model_name
        create_input_var_names = getattr(__import__(model_script_name, fromlist=["create_input_var_names"]), "create_input_var_names")
        input_var_names = create_input_var_names()

        ## Training set
        # Read file
        train_set_name = self.config.dset_names[self.config.train_set_ind]
        train_filename = 'analogy_' + train_set_name + '.hy'
        train_file = os.path.join(self.dataset_path, train_filename)
        log.info("Reading %s...", train_file)
        train_data = h5py.File(train_file, 'r')
        # Create dataset structure
        train_ids = np.arange(len(train_data))
        np.random.shuffle(train_ids)
        train_set = Dataset(train_ids, train_data, 'analogy_' + train_set_name, input_var_names)
        # Create input operation
        self.train_batch_ops = create_input_ops(train_set, int(self.config.batch_size), scope = 'analogy_' + train_set_name + '_inputs')

        # Import model class
        Model = getattr(__import__(model_script_name, fromlist=["Model"]), "Model")
        log.info("Using Model class: %s", self.model_name)

        # Create 'global_step' variable
        log.info("Initializing global step and optimizer...")
        self.global_step = tf.get_variable('global_step', [], initializer=tf.constant_initializer(0), trainable=False, dtype=tf.int64)

        # Build model
        log.info("Building training model...")
        with tf.variable_scope(tf.get_variable_scope()):
            self.model = Model(self.config, self.train_batch_ops)
            tf.get_variable_scope().reuse_variables()
            self.train_loss = self.model.train_loss

        # Create optimizer
        log.info("Create optimizer...")
        self.opt = tf.train.AdamOptimizer(learning_rate=self.config.learning_rate)
        self.opt = hvd.DistributedOptimizer(self.opt)

        # Set up training operation (to apply gradients)
        log.info('Setting up training operation...')
        self.apply_gradient_op = self.opt.minimize(self.train_loss, global_step=self.global_step)

        # Model name (for output directory)
        self.model_name = self.config.model_name + '_trained_on_' + train_set_name + '_run' + self.config.run

        # Create directory for training output
        check_path('./train_dir')
        self.train_dir = './train_dir/%s' % (self.model_name) + '/'
        check_path(self.train_dir)
        log.info("Train Dir: %s", self.train_dir)

        # Summaries and saving output
        self.summary_op = tf.summary.merge_all()
        self.saver = tf.train.Saver(max_to_keep=len(self.config.save_checkpoint_steps))

        # Set up session
        log.info("Setting up session...")
        init = tf.global_variables_initializer()
        bcast = hvd.broadcast_global_variables(0)
        self.session = tf.Session(config=self.session_config)
        self.session.run(init)
        self.session.run(bcast)
        tf.train.start_queue_runners(sess=self.session)

    def train(self):

        # Use CPU to coordinate training
        with tf.Graph().as_default(), tf.device('/cpu:0'):

            log.info("Training begins...")

            # Setup file for recording timecourses
            timecourse_dir = './timecourses'
            check_path(timecourse_dir)
            timecourse_filename = self.model_name + '.txt'
            timecourse_filename_path = os.path.join(timecourse_dir, timecourse_filename)
            timecourse_file_ID = open(timecourse_filename_path, 'w')
            header_line = 'step loss'
            for key in self.model.all_out.keys():
                header_line = header_line + ' ' + key
            header_line = header_line + '\n'
            timecourse_file_ID.write(header_line)

            train_steps = int(self.config.train_steps)
            save_checkpoint_steps = np.round(np.array(self.config.save_checkpoint_steps)).astype(np.int)
            for s in range(train_steps):

                ## Training
                # Single training step
                summary, step_time, train_loss, train_all_out = self.run_single_step(s)
                # Periodic output
                if s % 10 == 0:
                    # Output performance
                    self.log_summary(s+1, step_time, train_loss, train_all_out)
                    # Write performance to timecourse file
                    timecourse_line = str(int(s+1)) + ' ' + str(train_loss)
                    for key in self.model.all_out.keys():
                        timecourse_line += ' ' + str(float("{0:.2f}".format(train_all_out[key])))
                    timecourse_line += '\n'
                    timecourse_file_ID.write(timecourse_line)

                # Save parameters
                if np.any(s+1 == save_checkpoint_steps):
                    log.info('Saving parameters...')
                    self.saver.save(self.session, self.train_dir, global_step=self.global_step)

            # Close timecourse file
            timecourse_file_ID.close()

    def run_single_step(self, step):

        # Start time
        start_time = time.time()

        # Read in batch and run step
        _, summary, train_loss, train_all_out = self.session.run(
            [self.apply_gradient_op, self.summary_op,
            self.model.train_loss, self.model.all_out])

        # End time
        end_time = time.time()
        step_time = end_time - start_time

        return summary, step_time, train_loss, train_all_out

    def log_summary(self, step, step_time, train_loss, train_out):

        if step_time == 0:
            step_time = 0.001

        # Step number
        if step == 'final':
            text = " [train step final] "
        else:
            text = " [train step {step:4d}] ".format(step = step)

        # Training loss
        text = text + "{var_name:s}: {var_val:.4f} ".format(var_name = 'train_loss', var_val = train_loss)

        # Training outputs
        for key, val in train_out.items():
            text = text + "{var_name:s}: {var_val:.4f} ".format(var_name = key, var_val = val)

        # Step time
        text = text + "({sec_per_batch:.3f} sec/batch)".format(sec_per_batch = step_time)

        # Print
        log.info(text)

def main():

    parser = argparse.ArgumentParser()
    parser.add_argument('--model_name', type=str, default='context_norm')
    parser.add_argument('--dset_names', nargs="*", type=str, default=['train', 'test1', 'test2', 'test3', 'test4', 'test5'])
    parser.add_argument('--train_set_ind', type=int, default=0)
    parser.add_argument('--run', type=str, default='1')
    parser.add_argument('--batch_size', type=int, default=32)
    parser.add_argument('--train_steps', type=int, default=10000)
    parser.add_argument('--save_checkpoint_steps', nargs="*", type=int, default=[100,200,500,1000,2000,10000])
    parser.add_argument('--learning_rate', type=float, default=5e-4)

    config = parser.parse_args()

    # Construct trainer
    trainer = Trainer(config)

    # Train
    trainer.train()

if __name__ == '__main__':
    main()


import hiero_utils

class PlateIngestionController(object):
    def __init__(self, view, model, selection):
        super(PlateIngestionController, self).__init__()
        self.view = view
        self.model = model
        self.trackitems = hiero_utils.get_selection(selection)
        self.connect_signal_slot()

    def show_panel(self):
        self.view.show()

    def connect_signal_slot(self):
        self.view.tagButton.clicked.connect(self.generate_tag)

    def generate_tag(self):
        if str(self.view.seqEdit.text()) == "" or str(self.view.fromEdit.text()) == "" or str(self.view.stepEdit.text()) == "":
            self.view.show_message('shot information missing.\n please check edit fields.')
        elif self.view.showTypeList.currentText() == "TV series" and str(self.view.episodeEdit.text()) == "":
            self.view.show_message('shot information missing.\n please check edit fields.')
        else:
            try:
                seq = self.view.seqEdit.text()
                shotnum = int(self.view.fromEdit.text())
                step = int(self.view.stepEdit.text())
                if hiero_utils.generate_tag(self.trackitems, seq, shotnum, step):
                    row_count = self.view.shotCodeForm.rowCount()
                    for row in range(0, row_count):
                        self.view.shotCodeForm.removeRow(0)
                    self.view.set_shotcode_list(self.trackitems)
                    self.view.messageLabel.setText('all tags generated successfully.')
            except:
                self.view.messageLabel.setText('failed to generate tags.')


import pandas as pd
from sklearn.model_selection import train_test_split
import keras
from keras.models import Sequential
from keras.layers import Dense
from sklearn.metrics import confusion_matrix, accuracy_score #importa a matriz confusao e a matriz de acerto

previsores = pd.read_csv('entradas-breast.csv')
classe = pd.read_csv('saidas-breast.csv')

previsores_treinamento, previsores_teste, classe_treinamento, classe_teste = train_test_split(previsores, classe, test_size=0.25)

classificador = Sequential()
classificador.add(Dense(units = 16, activation = 'relu',
                        kernel_initializer = 'random_uniform', input_dim=30)) #primeira camada oculta
classificador.add(Dense(units = 16, activation = 'relu',
                        kernel_initializer = 'random_uniform')) #segunda camada oculta
classificador.add(Dense(units = 1, activation = 'sigmoid')) #camada de saida
classificador.compile('adam', loss='binary_crossentropy', metrics = ['binary_accuracy']) #optimizer padrao
            #otimizador = keras.optimizers.Adam(lr = 0.001, decay = 0.0001, clipvalue = 0.5) #mudando parametros do optimizer => abaixou muito a precisao
            #classificador.compile(otimizador, loss='binary_crossentropy', metrics = ['binary_accuracy']) #usando o optimizer configurado
classificador.fit(previsores_treinamento, classe_treinamento, batch_size = 10, epochs = 100)

pesos0 = classificador.layers[0].get_weights() #mostra os pesos que a IA encontrou, o 0 indica a primeira camada
pesos1 = classificador.layers[1].get_weights()
pesos2 = classificador.layers[2].get_weights()
print(pesos0)
print(len(pesos0))

previsoes = classificador.predict(previsores_teste)
previsoes = (previsoes > 0.5)

precisao = accuracy_score(classe_teste, previsoes) #essa matriz mostra a precisao da rede
matriz = confusion_matrix(classe_teste, previsoes) #essa matriz mostra quantos a rede errou ou acertou
resultado = classificador.evaluate(previsores_teste, classe_teste) #outra maneira de achar a precisao, mas agr usando o keras


import numpy
import cupy as cp
import timeit
from scipy import sparse

# x = pycuda.driver.managed_zeros(shape=4, dtype=numpy.int32, mem_flags=cuda.mem_attach_flags.GLOBAL)
# y = pycuda.driver.managed_zeros(shape=4, dtype=numpy.int32, mem_flags=cuda.mem_attach_flags.GLOBAL)
# x[0:4] = [1, 2, 3, 4]
# y[0:4] = [1, 2, 3, 4]
# l2_gpu = cupy.linalg.norm(y)
# a = cupy.multiply(x,y)
# print(l2_gpu)

# a = cupy.random.rand(44, 20)
# b = cupy.random.rand(20, 1)

pool = cp.cuda.MemoryPool(cp.cuda.malloc_managed)
cp.cuda.set_allocator(pool.malloc)

sizes = [100, 1000, 2000]
for size in sizes:
    print("---- Size: ",size, " x ", size, " ----")
    a = cp.ones((size, size), dtype=numpy.float32)
    b = cp.ones((size, size), dtype=numpy.float32)

    a[0:size] = 2

    start_time = timeit.default_timer()
    cp.dot(a, b)    # cupy.matmul(a, b, out=None)
    cp.cuda.Device(0).synchronize()
    print("duration gpu: ", timeit.default_timer() - start_time)

    start_time = timeit.default_timer()
    for i in range(10):
        a[0:size] = i
        c = cp.dot(a, b)
        cp.cuda.Device(0).synchronize()
    print("avg duration gpu: ", (timeit.default_timer() - start_time)/10.0)

    # numpy
    a_np = numpy.random.random((size, size))
    b_np = numpy.random.random((size, size))
    result_time = timeit.timeit("c_np = numpy.dot(a_np, b_np)", globals=globals(), number=10)
    print("duration numpy: ", result_time / 10.0)

    # # numpy int
    # BLAS, which is used in numpy, only works with float32 and float64 See https://www.benjaminjohnston.com.au/matmul
    a_np = numpy.random.randint(0, 10, (size, size))
    b_np = numpy.random.randint(0, 10, (size, size))
    result_time = timeit.timeit("c_np = numpy.dot(a_np, b_np)", globals=globals(), number=1)
    print("duration numpy int: ", result_time / 1.0)

    # # numpy int32
    # BLAS, which is used in numpy, only works with float32 and float64 See https://www.benjaminjohnston.com.au/matmul
    a_np = numpy.random.randint(0, 10, (size, size), dtype=numpy.int32)
    b_np = numpy.random.randint(0, 10, (size, size), dtype=numpy.int32)
    result_time = timeit.timeit("c_np = numpy.dot(a_np, b_np)", globals=globals(), number=1)
    print("duration numpy int32: ", result_time / 1.0)

    # sparse
    M = sparse.random(size, size, .30)
    result_time = timeit.timeit("c_sparse = M.dot(M)", globals=globals(), number=10)
    print("avg duration sparse: ", result_time/10.0)

    print()

# "C:\Program Files\Python36\python.exe" C:/Users/tfischle/Github/PyCuda_examples/matrix_multiplication.py
# ---- Size:  100  x  100  ----
# duration gpu:  0.5852928000000001
# avg duration gpu:  0.00014606999999999815
# duration numpy:  0.0003451800000000005
# duration numpy int:  0.0009719999999999729
# duration numpy int32:  0.0009557000000000038
# avg duration sparse:  0.0009325600000000045
#
# ---- Size:  1000  x  1000  ----
# duration gpu:  0.0015964000000000533
# avg duration gpu:  0.0031100099999999964
# duration numpy:  0.013901710000000001
# duration numpy int:  0.6493503
# duration numpy int32:  0.633626
# avg duration sparse:  0.2823481
#
# ---- Size:  2000  x  2000  ----
# duration gpu:  0.006359999999999921
# avg duration gpu:  0.00414393999999998
# duration numpy:  0.07829159999999993
# duration numpy int:  27.2362269               # BLAS, which is used in numpy, only works with float32 and float64 See https://www.benjaminjohnston.com.au/matmul
# duration numpy int32:  27.470195999999994
# avg duration sparse:  2.313961


# import modules
import json
import pandas as pd
import dash
import dash_html_components as html
from dash.dependencies import Input, Output, State
import dash_table

# prepare dash_table
size = 5
df = pd.DataFrame([], index=range(size))
df['num'] = range(size)
df['char'] = list('abcdefghijk')[:size]
tab = dash_table.DataTable(
    id='table',
    columns=[{"name": i, "id": i} for i in df.columns],
    data=df.to_dict('records'),
    tooltip_data=[
        {
            # (B) multiply cell value by 10 for demonstration purpose
            column: {'value': str(value*10), 'type': 'markdown'}
            for column, value in row.items()
        } for row in df.to_dict('records')
    ],
    tooltip_delay=0,
    tooltip_duration=None
)

# set layout
app = dash.Dash('SimpleExample')
app.layout = html.Div([
    tab,
    html.Div(id='click-data', style={'whiteSpace': 'pre-wrap'}),

])

# define callback
@app.callback(
    Output('click-data', 'children'),
    [Input('table', 'active_cell')],
     # (A) pass table as data input to get current value from active cell "coordinates"
    [State('table', 'data')]
)
def display_click_data(active_cell, table_data):
    if active_cell:
        cell = json.dumps(active_cell, indent=2)
        row = active_cell['row']
        col = active_cell['column_id']
        value = table_data[row][col]
        out = '%s\n%s' % (cell, value)
    else:
        out = 'no cell selected'
    return out

# run app
if __name__ == '__main__':
    app.run_server(debug=True)


n = int(input())
s = list(input())
q_num = int(input())
query = []
for _ in range(q_num):
    query.append(input().split())

for q, i, j in query:
    if q == "1":
        i = int(i)
        s[i-1] = j
    else:
        i = int(i)
        j = int(j)
        print(len(set(s[i-1:j])))


'''
Finds and returns pieces of raw COVID-19 press-release that look
like tables. There are 3 such tables in typical release:

- COVID-19 cases in Russian regions
- new deaths
- new recovered per

Скрипт находит таблицы новых случев и смертей в пресс-релизе оперштаба
и возвращает обработанные скриптом regioncounter.py абзацы с отсортированными
регионами.

Первая таблица - с новыми случаями - должна быть обрезана (убираем все регионы,
где менее 100 случаев за последние сутки. Вторую таблицу даем полностью.
'''

import re

import ifaxbotcovid.parser.regioncounter as r
from ifaxbotcovid.parser.regexp import table_detect

# задаем regex'ы для парсинга двух таблиц в релизе

REGEXES = table_detect.detector_dict

# здесь будут храниться таблицы
TABLES = dict(newcases='NO_TABLE', newdeaths='NO_TABLE')

def get_key(dct, v):
    '''
    Возвращает key в dict, зная value
    '''
    return [key for key, value in dct.items() if value == v][0]

def choose_value(value):
    '''
    Сокращает tuple вида ('', 'цифра', '') до 'цифра'
    '''
    if type(value) == tuple:
        if len(value) > 1:
            for item in value:
                if item == '':
                    None
                else:
                    return item
            return ''
        else:
            return value[0]
    else:
        return value

def find_tables(raw_txt):
    global TABLES
    for regex in REGEXES.values():
        try:
            table_name = get_key(REGEXES, regex)
        except Exception:
            print('Не удалось определить формат таблицы')
            return
        try:
            table_regex = re.compile(regex, re.DOTALL)
            raw_table = table_regex.findall(raw_txt)[0]
            raw_table = choose_value(raw_table)
            TABLES[table_name] = raw_table
        except Exception:
            print('Не удалось найти таблицу {}'.format(table_name))
    return

def get_prepared(ready_table, table_type, short=100):
    if table_type == 'ready_cases':
        splitted_ready_cases = ready_table.split(',')
        if 'Москв' in splitted_ready_cases[0]:
            first_reg = splitted_ready_cases[1]
            splitted_ready_cases[1] = first_reg.replace(
                '-', 'новых случаев COVID-19 зафиксировано', 1
            )
            joined_ready_cases = ','.join(
                splitted_ready_cases[1:]
            )  # убираем Москву
        else:
            joined_ready_cases = ','.join(
                splitted_ready_cases
            )  # Москва не в начале, не трогаем
        cases_final = ''.join((
            'По информации оперативного штаба, еще',
            joined_ready_cases,
            'В других регионах России суточный прирост не превышает ',
            str(short),
            '.'
        ))
        return cases_final
    if table_type == 'ready_deaths':
        splitted_deaths = ready_table.split(',')
        first_reg = splitted_deaths[0]
        if ' - ' in first_reg:
            splitted_deaths[0] = first_reg.replace(
                '-', 'пациентов скончалось за сутки', 1
            )
        joint_deaths = ','.join(splitted_deaths)
        cases_final = 'Согласно данным оперштаба о смертности, ' + joint_deaths
        return cases_final

def process_tables(short=100):
    rc_ready_cases = r.RegionCounter(
        TABLES['newcases'],
        table_type='new_cases',
        short=short
    )
    rc_ready_deaths = r.RegionCounter(
        TABLES['newdeaths'],
        table_type='dead'
    )
    ready_cases, log1 = rc_ready_cases(), rc_ready_cases.log
    ready_deaths, log2 = rc_ready_deaths(), rc_ready_deaths.log
    log = log1 + log2
    ready_cases = get_prepared(ready_cases, 'ready_cases', short=short)
    ready_deaths = get_prepared(ready_deaths, 'ready_deaths', short=short)
    return ready_cases, ready_deaths, log

def tables(rawtext, short=100):
    # для вызова за пределами скрипта
    try:
        find_tables(rawtext)
        ready_cases, ready_deaths, log = process_tables(short=short)
        return ready_cases, ready_deaths, log
    except Exception:
        print(
            'Не удалось обработать и добавить таблицы новых случаев и умерших'
        )

# for testing usage only
if __name__ == '__main__':
    import pyperclip
    if pyperclip.paste is not None:
        rawtext = pyperclip.paste()
        ready_cases, ready_deaths, log = tables(rawtext, short=150)
        print(ready_cases)
        print(ready_deaths)
        print(log)
    else:
        print('Нет текста в буфере обмена')


# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""
/install endpoint for tecs API
"""
import os
import copy
import subprocess
import time
import commands

import traceback
import webob.exc
from oslo_config import cfg
from oslo_log import log as logging
from webob.exc import HTTPBadRequest
from webob.exc import HTTPForbidden
from webob.exc import HTTPServerError

import threading
from threading import Thread

from daisy import i18n
from daisy import notifier

from daisy.api import policy
import daisy.api.v1

from daisy.common import exception
import daisy.registry.client.v1.api as registry
from daisy.api.backends.tecs import config
from daisy.api.backends import driver
from daisy.api.network_api import network as neutron
from ironicclient import client as ironic_client
import daisy.api.backends.os as os_handle
import daisy.api.backends.common as daisy_cmn
import daisy.api.backends.tecs.common as tecs_cmn
import daisy.api.backends.tecs.install as instl
import daisy.api.backends.tecs.uninstall as unstl
import daisy.api.backends.tecs.upgrade as upgrd

try:
    import simplejson as json
except ImportError:
    import json

LOG = logging.getLogger(__name__)
_ = i18n._
_LE = i18n._LE
_LI = i18n._LI
_LW = i18n._LW

tecs_state = tecs_cmn.TECS_STATE

class API(driver.DeploymentDriver):
    """
    The hosts API is a RESTful web service for host data. The API
    is as follows::

        GET  /hosts -- Returns a set of brief metadata about hosts
        GET  /hosts/detail -- Returns a set of detailed metadata about
                              hosts
        HEAD /hosts/<ID> -- Return metadata about an host with id <ID>
        GET  /hosts/<ID> -- Return host data for host with id <ID>
        POST /hosts -- Store host data and return metadata about the
                        newly-stored host
        PUT  /hosts/<ID> -- Update host metadata and/or upload host
                            data for a previously-reserved host
        DELETE /hosts/<ID> -- Delete the host with id <ID>
    """

    def __init__(self):
        super(API, self).__init__()
        return

    def install(self, req, cluster_id):
        """
        Install TECS to a cluster.

        param req: The WSGI/Webob Request object
        cluster_id:cluster id
        """

        tecs_install_task = instl.TECSInstallTask(req, cluster_id)
        tecs_install_task.start()

    def _get_roles_and_hosts_ip_list(self, req, cluster_id):
        host_ha_list = set()
        host_ip_list = set()
        role_id_list = set()
        hosts_id_list = []
        hosts_list = []

        roles = daisy_cmn.get_cluster_roles_detail(req,cluster_id)
        cluster_networks = daisy_cmn.get_cluster_networks_detail(req, cluster_id)
        for role in roles:
            if role['deployment_backend'] != daisy_cmn.tecs_backend_name:
                continue
            role_hosts = daisy_cmn.get_hosts_of_role(req, role['id'])
            if role_hosts:
                for role_host in role_hosts:
                    host = daisy_cmn.get_host_detail(req, role_host['host_id'])
                    host_ip = tecs_cmn.get_mngt_network_ip(host, cluster_networks)
                    if role['name'] == "CONTROLLER_HA":
                        host_ha_list.add(host_ip)
                    host_ip_list.add(host_ip)
                    hosts_id_list.append({host['id']:host_ip})
                role_id_list.add(role['id'])
        for host in hosts_id_list:
            if host not in hosts_list:
                hosts_list.append(host)
        return (role_id_list, host_ip_list, host_ha_list, hosts_list)

    def _query_progress(self, req, cluster_id, action=""):
        nodes_list = []
        roles = daisy_cmn.get_roles_detail(req)
        (role_id_list,host_ip_list,host_ha_list,hosts_list) = self._get_roles_and_hosts_ip_list(req, cluster_id)
        for host in hosts_list:
            node = {}
            host_id = host.keys()[0]
            host = daisy_cmn.get_host_detail(req, host_id)
            node['id'] = host['id']
            node['name'] = host['name']

            if 0 == cmp("upgrade", action):
                node['os-progress'] = host['os_progress']
                node['os-status'] = host['os_status']
                node['os-messages'] = host['messages']

            if host['status'] == "with-role":
                host_roles = [ role for role in roles if role['name'] in host['role'] and role['cluster_id'] == cluster_id]
                if host_roles:
                    node['role-status'] = host_roles[0]['status']
                    node['role-progress'] = str(host_roles[0]['progress'])
                    # node['role-message'] = host_roles[0]['messages']
            nodes_list.append(node)
        return {'tecs_nodes': nodes_list}

    def uninstall(self, req, cluster_id):
        """
        Uninstall TECS to a cluster.

        :param req: The WSGI/Webob Request object

        :raises HTTPBadRequest if x-install-cluster is missing
        """

        (role_id_list, host_ip_list,host_ha_list, hosts_list) = self._get_roles_and_hosts_ip_list(req, cluster_id)
        if role_id_list:
            if not host_ip_list:
                msg = _("there is no host in cluster %s") % cluster_id
                raise exception.ThreadBinException(msg)

            unstl.update_progress_to_db(req, role_id_list, tecs_state['UNINSTALLING'], hosts_list, 0.0)
            uninstall_progress_percentage = round(1*1.0/len(host_ip_list), 2)*100

            threads = []
            for host_ip in host_ip_list:
                t = threading.Thread(target=unstl.thread_bin,args=(req,host_ip,role_id_list,uninstall_progress_percentage))
                t.setDaemon(True)
                t.start()
                threads.append(t)
            LOG.info(_("uninstall threads have started, please waiting...."))

            try:
                for t in threads:
                    t.join()
            except:
                LOG.warn(_("Join uninstall thread %s failed!" % t))
            else:
                uninstall_failed_flag = False
                for role_id in role_id_list:
                    role = daisy_cmn.get_role_detail(req, role_id)
                    # if progress is 100, it means some thing is not ready when uninstalling
                    # here can't use status ==uninstalling or progress==0
                    if role['progress'] == 100:
                        unstl.update_progress_to_db(req, role_id_list, tecs_state['UNINSTALL_FAILED'], hosts_list)
                        uninstall_failed_flag = True
                        break
                    if role['status'] == tecs_state['UNINSTALL_FAILED']:
                        uninstall_failed_flag = True
                        break
                if not uninstall_failed_flag:
                    LOG.info(_("all uninstall threads have done, set all roles status to 'init'!"))
                    unstl.update_progress_to_db(req, role_id_list, tecs_state['INIT'], hosts_list)

        try:
            (status, output) = commands.getstatusoutput('rpm -e --nodeps openstack-packstack\
                        openstack-packstack-puppet openstack-puppet-modules puppet')
        except exception.Invalid as e:
            raise HTTPBadRequest(explanation=e.msg, request=req)

    def uninstall_progress(self, req, cluster_id):
        return self._query_progress(req, cluster_id, "uninstall")

    def upgrade(self, req, cluster_id):
        """
        update TECS to a cluster.

        :param req: The WSGI/Webob Request object

        :raises HTTPBadRequest if x-install-cluster is missing
        """
        (role_id_list,host_ip_list,host_ha_list,hosts_list) = self._get_roles_and_hosts_ip_list(req, cluster_id)
        if role_id_list:
            if not host_ip_list:
                msg = _("there is no host in cluster %s") % cluster_id
                raise exception.ThreadBinException(msg)
            unreached_hosts = daisy_cmn.check_ping_hosts(host_ip_list, 1)
            if unreached_hosts:
                self.message = "hosts %s ping failed" % unreached_hosts
                raise exception.NotFound(message=self.message)

            daisy_cmn.subprocess_call('rm -rf /root/.ssh/known_hosts')

            if os_handle.check_tfg_exist():
                os_handle.upgrade_os(req, hosts_list)
                unreached_hosts = daisy_cmn.check_ping_hosts(host_ip_list, 30)
                if unreached_hosts:
                    self.message = "hosts %s ping failed after tfg upgrade" % unreached_hosts
                    raise exception.NotFound(message=self.message)
            upgrd.update_progress_to_db(req, role_id_list, tecs_state['UPDATING'], 0.0)
            update_progress_percentage = round(1*1.0/len(host_ip_list), 2)*100
            threads = []
            LOG.info(_("begin to update TECS controller nodes, please waiting...."))
            for host_ip in host_ha_list:
                LOG.info(_("update TECS controller node %s..." % host_ip))
                upgrd.thread_bin(req,host_ip,role_id_list,update_progress_percentage)
            LOG.info(_("begin to update TECS other nodes, please waiting...."))
            for host_ip in (host_ip_list - host_ha_list):
                t = threading.Thread(target=upgrd.thread_bin,args=(req,host_ip,role_id_list,update_progress_percentage))
                t.setDaemon(True)
                t.start()
                threads.append(t)
            try:
                for t in threads:
                    t.join()
            except:
                LOG.warn(_("Join update thread %s failed!" % t))
            else:
                update_failed_flag = False
                for role_id in role_id_list:
                    role = registry.get_role_metadata(req.context, role_id)
                    # if progress is 0, it means some thing is not ready when updating
                    # here can't use status ==updating or progress==100
                    if role['progress'] == 0:
                        upgrd.update_progress_to_db(req, role_id_list, tecs_state['UPDATE_FAILED'])
                        update_failed_flag = True
                        break
                    if role['status'] == tecs_state['UPDATE_FAILED']:
                        update_failed_flag = True
                        break
                if not update_failed_flag:
                    LOG.info(_("all update threads have done, set all roles status to 'active'!"))
                    upgrd.update_progress_to_db(req, role_id_list, tecs_state['ACTIVE'])

    def upgrade_progress(self, req, cluster_id):
        return self._query_progress(req, cluster_id, "upgrade")

    def export_db(self, req, cluster_id):
        """
        Export daisy db data to tecs.conf and HA.conf.

        :param req: The WSGI/Webob Request object

        :raises HTTPBadRequest if x-install-cluster is missing
        """

        (tecs_config, mgnt_ip_list) = instl.get_cluster_tecs_config(req, cluster_id)

        config_files = {'tecs_conf':'','ha_conf':''}
        tecs_install_path = "/home/tecs_install"
        tecs_config_file = ''
        if tecs_config:
            cluster_conf_path = tecs_install_path + "/" + cluster_id
            create_cluster_conf_path = "rm -rf %s; mkdir %s" % (cluster_conf_path, cluster_conf_path)
            daisy_cmn.subprocess_call(create_cluster_conf_path)
            config.update_tecs_conf(tecs_config, cluster_conf_path)

            get_tecs_conf = "ls %s|grep tecs.conf" % cluster_conf_path
            obj = subprocess.Popen(get_tecs_conf, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (stdoutput, erroutput) = obj.communicate()
            tecs_conf_file = ""
            if stdoutput:
                tecs_conf_file = stdoutput.split('\n')[0]
                config_files['tecs_conf'] = cluster_conf_path + "/" + tecs_conf_file

            get_ha_conf_cmd = "ls %s|grep HA_1.conf" % cluster_conf_path
            obj = subprocess.Popen(get_ha_conf_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            (stdoutput, erroutput) = obj.communicate()
            ha_conf_file = ""
            if stdoutput:
                ha_conf_file = stdoutput.split('\n')[0]
                config_files['ha_conf'] = cluster_conf_path + "/" + ha_conf_file
        return config_files


from odoo import models, fields, api
from datetime import datetime, date

class HrEmployee(models.Model):
    _inherit = "hr.employee"

    # Nationalité & Autre Information
    x_nationalite_id = fields.Many2one('ref_nationalite', default=lambda self: self.env['ref_nationalite'].search(
        [('code_nationalite', '=', 'BF')]), string='Nationalité')
    matricule_genere = fields.Char(string="Matricule")
    matricule = fields.Char(string="Mle Fonctionnaire ")
    x_type_piece_id = fields.Many2one("hr_typepiece", string="Type pièce", required=True)
    ref_identification = fields.Char(string='Ref.Identification', required=True)
    # Information de  contact
    tel = fields.Char(string='Telephone', required=True)
    personne_id = fields.Char('Personne à prevenir en cas de besoin', required=True)
    x_email = fields.Char(string="Email")
    # Statut
    genre = fields.Selection([
        ('masculin', 'Masculin'), ('feminin', 'Feminin'),
        ('autre', 'Autre')],
        string='Genre', default="masculin")
    situation_marital = fields.Selection([
        ('celibataire', 'Célibataire'),
        ('marie', 'Marié(e)'),
        ('concubinage', 'Concubinage'),
        ('veuf(ve)', 'Veuf(ve)'),
        ('divorce', 'Divorcé(e)')],
        string='Etat civil', default="celibataire")
    charge_femme = fields.Integer(string="Charge femme", required=True)
    charge_enfant = fields.Integer(string="Charge enfant", required=True, compute="calcul_charge_enfant")
    charge = fields.Integer(store=True, string="Charge total", readonly=True, compute="calcul_charge")
    x_montant_charge = fields.Float(string="Montant charge", readonly=True, store=True, compute='mnt_charge_field')

    # Naissance
    x_date_naissance = fields.Date(string='Date de naissance', required=True)
    x_nb_annee_retraite = fields.Many2one("hr_nbreannee", string="Age de retraite", required=True)
    x_date_retraite = fields.Date(string='Date retraite', compute="_compute_date_retraite")
    lieu_naiss = fields.Many2one('ref_localite', string='Lieu de naissance')
    # Éducation
    x_diplome_id = fields.Many2one("hr_diplome", string="Dernier diplôme")
    x_diplome_recrut_id = fields.Many2one("hr_diplome", string="Diplôme de recrutement")
    branche = fields.Char(string="Branche d'étude")
    ecole = fields.Char(string="Ecole/Université")
    observations = fields.Html(string='Observations')

    # Informations professionnelles
    type_id = fields.Many2one(comodel_name='hr_contract_type', string='Type contrat')  # type contrat
    x_categorie_employe_id = fields.Many2one("hr_catemp", string="Catégorie employé", required=True)
    struct_id = fields.Many2one("hr.payroll.structure", string="Type employé",
                                required=True)  # type employe/srtuture salariale
    struct_id_code = fields.Char(string='Struct_id_code', required=False, related="struct_id.code")
    x_emploi_id = fields.Many2one("hr_emploi", string="Emploi", required=True, )
    x_fonction_id = fields.Many2one("hr_fonctionss", string="Fonction", required=True)
    hr_service = fields.Many2one("hr_service", string="Service")
    x_unite_id = fields.Many2one("hr_unite", string="Unite", required=False)
    x_zone_id = fields.Many2one("hr_zone", string="Zone", required=True)
    x_mode_paiement = fields.Selection([
        ('billetage', 'Billetage'),
        ('virement', 'Virement'),
    ], string="Mode de Paiement", default='virement', required=True)
    num_banque = fields.Char('N° compte bancaire', required=True)
    x_banque_id = fields.Many2one('res.bank', 'Banque', required=True)
    intitule = fields.Char(string="Intitulé du compte")

    date_sercice = fields.Date(string="Date Service")

    # Situation administrative de l'agent dans l'Etat
    x_classees_id = fields.Many2one('hr_classe', string='Classe')
    x_categorie_id = fields.Many2one('hr_categorie', string='Catégorie')
    x_echelle_id = fields.Many2one('hr_echelle', string='Echelle')
    x_echellon_id = fields.Many2one('hr_echellon', string='Echelon')
    x_classification = fields.Char(store=True, string="Classification")
    date_modiff = fields.Date(string='Date effet', default=date.today())
    date_debut = fields.Date(string="Date d'engagement", default=date.today())
    date_fin = fields.Date(string="Date fin")
    x_solde_indiciaire = fields.Float(string="Solde Indiciaire", readonly=True,
                                      compute="cal_sal_base")  # salaire de base des fonctionnaires
    x_solde_indiciaire_net = fields.Float(string="Salaire de base", compute="cal_sal_base",
                                          readonly=True)  # salaire de base des fonctionnaires + montant residence
    x_indice = fields.Float(string='Indice', compute="cal_sal_base")

    # Situation administrative de l'agent dans l'EPE
    x_categorie_c_id = fields.Many2one('hr_categorie', string='Catégorie', required=False)
    x_echelle_c_id = fields.Many2one('hr_echelle', string='Echelle', required=False)
    x_echellon_c_id = fields.Many2one('hr_echellon', required=False, string='Echelon')
    x_solde_indiciaire_ctrct = fields.Float(string="Salaire de base", required=True)  # salaire de base des contractuels

    date_embauche = fields.Date(string="Date d'embauche", default=date.today())
    date_fin_embauche = fields.Date(string="Date fin")
    date_modiff = fields.Date(string='Date effet', default=date.today())

    # CALCUL DE L'EMOLUMENT
    x_emolument_ctrct = fields.Float(string="Emolument Brut")
    x_taux_retenu_emolmt = fields.Float(string="Taux retenue (%)")
    x_mnt_taux_retenu_emolmt = fields.Float(string="Montant Taux")
    x_emolument_ctrct_net = fields.Float(string="Emolument Net", readonly=True)

    # Declaration variables Indemnités
    x_indem_resp = fields.Float(string="Indemn.Resp", readonly=False)
    x_indem_astr = fields.Float(string="Indemn.Astreinte", compute="cal_indem_astr")
    x_indem_techn = fields.Float(string="Indemn.Technicité", readonly=False)
    x_indem_specif = fields.Float(string="Indemn.Spécifique GRH", readonly=False)
    x_indem_loge = fields.Float(string="Indemn.Logement", readonly=False)
    x_indem_transp = fields.Float(string="Indemn.Transport", readonly=False)
    x_indem_inform = fields.Float(string="Indemn.Informatique", readonly=False)
    x_indem_exploit = fields.Float(string="Indemn.Exploitation-Réseau", readonly=False)
    x_indem_finance = fields.Float(string="Indemn.Resp.Financière", readonly=False)
    x_indem_garde = fields.Float(string="Indemn.Garde", readonly=False)
    x_indem_risque = fields.Float(string="Indemn.Risque.Contagion", readonly=False)
    x_indem_suj = fields.Float(string="Indemn.Sujétion Géographique", readonly=False)
    x_indem_form = fields.Float(string="Indemn.Formation", readonly=False)
    x_indem_caisse = fields.Float(string="Indemn.Caisse", readonly=False)
    x_indem_veste = fields.Float(string="Indemn.Vestimentaire", readonly=False)
    x_indem_spec_inspect_trav = fields.Float(string="Indemn.Spécifique Inspecteur de Travail", readonly=False)
    x_indem_spec_inspect_ifc = fields.Float(string="Indemn.Spécifique Forfaitaire Compensatrice", readonly=False)
    x_indem_spec_inspect_irp = fields.Float(string="Indemn.Spécifique de Responsabilité Pécunière", readonly=False)
    x_indem_spec_inspect_ish = fields.Float(string="Indemn.Spécifique Harmonisée Personnel MENA et MESRSI",
                                            readonly=False)
    x_indem_prime_rendement = fields.Float(string="Prime rendement", readonly=False)
    x_indemnite_residence = fields.Float(string='Indemnité de residence', compute="cal_x_indemnite_residence")
    x_allocation_familial = fields.Float(store=True, string='Allocation Familiale', compute='mnt_alloc')

    # exoneration
    x_indem_resp_exo = fields.Float(store=True, string="Exo.Resp", readonly=True,
                                    compute='calcul_exoneration_responsabilite')
    x_indem_astr_exo = fields.Float(store=True, string="Exo.Astreinte", readonly=True,
                                    compute='calcul_exoneration_astreintes')
    x_indem_techn_exo = fields.Float(store=True, string="Exo.Technicité", readonly=True,
                                     compute='calcul_exoneration_technicites')
    x_indem_specif_exo = fields.Float(store=True, string="Exo.Spécifique GRH", readonly=True,
                                      compute='calcul_exoneration_specifiques')
    x_indem_specif_it_exo = fields.Float(store=True, string="Exo.Spécifique IT", readonly=True,
                                         compute='calcul_exoneration_specifiques_it')
    x_indem_specif_irp_exo = fields.Float(store=True, string="Exo.Spécifique IRP", readonly=True,
                                          compute='calcul_exoneration_specifiques_irp')
    x_indem_specif_ifc_exo = fields.Float(store=True, string="Exo.Spécifique IFC", readonly=True,
                                          compute='calcul_exoneration_specifiques_ifc')
    x_indem_specif_ish_exo = fields.Float(store=True, string="Exo.Spécifique ISH", readonly=True,
                                          compute='calcul_exoneration_specifiques_ish')
    x_total_exo = fields.Float(store=True, string="Exo.Total", readonly=True, compute='calcul_exoneration_total')
    x_indem_loge_exo = fields.Float(store=True, string="Exo.Logement", readonly=True,
                                    compute='calcul_exoneration_logement')
    x_indem_transp_exo = fields.Float(store=True, string="Exo.Transport", readonly=True,
                                      compute='calcul_exoneration_transport')
    x_indem_inform_exo = fields.Float(store=True, string="Exo.Informatique", readonly=True,
                                      compute='calcul_exoneration_informatique')
    x_indem_exploit_exo = fields.Float(store=True, string="Exo.Exploitation-Réseau", readonly=True,
                                       compute='calcul_exoneration_exploitation')
    x_indem_finance_exo = fields.Float(store=True, string="Exo.Resp.Financière", readonly=True,
                                       compute='calcul_exoneration_resp_finance')
    x_indem_garde_exo = fields.Float(store=True, string="Exo.Garde", readonly=True, compute='calcul_exoneration_garde')
    x_indem_risque_exo = fields.Float(store=True, string="Exo.Risque.Contagion", readonly=True,
                                      compute='calcul_exoneration_risque')
    x_indem_suj_exo = fields.Float(store=True, string="Exo.Sujétion Géographique", readonly=True,
                                   compute='calcul_exoneration_sujetion')
    x_indem_form_exo = fields.Float(store=True, string="Exo.Formation", readonly=True,
                                    compute='calcul_exoneration_formation')
    x_indem_caisse_exo = fields.Float(store=True, string="Exo.Caisse", readonly=True,
                                      compute='calcul_exoneration_caisse')
    x_indem_veste_exo = fields.Float(store=True, string="Exo.Vestimentaire", readonly=True,
                                     compute='calcul_exoneration_veste')
    x_indem_residence_exo = fields.Float(store=True, string="Exo.Résidence", readonly=True,
                                         compute='calcul_exoneration_residence')
    x_prime_exo = fields.Float(store=True, string="Exo.Prime Rendement", readonly=True,
                               compute='calcul_exoneration_prime')

    # Information Salariale
    x_salaire_base = fields.Float(string="Salaire de base", required=True, compute='cal_sal_base_c_f')
    x_total_indemnites = fields.Float(store=True, string="Totale indemnité", readonly=True, compute='cal_total_indem')
    x_remuneration_total = fields.Float("Rémunération totale", readonly=True, store=True,
                                        compute='calcul_remuneration_total')
    x_salaire_brut = fields.Float(store=True, string="Salaire Brut", readonly=True, compute='calcul_salaire_brut')
    mnt_total_retenues = fields.Float(string='Totale retenue', store=True, readonly=True, compute='cal_total_retenue')
    x_abattement_forfaitaire = fields.Float(store=True, string="Abattement forfaitaire", readonly=True,
                                            compute='calcul_abattement')  # Abattement
    x_salaire_net_imposable = fields.Float(store=True, string="Base imposable", readonly=True,
                                           compute='calcul_sni')  # SNI

    # cotisation sociale
    x_mnt_carfo = fields.Float(string="Montant CARFO", readonly=False)  # Montant CARFO = 8% du salaire de base
    x_mnt_patronal_carfo = fields.Float(string="Part Patronale CARFO",
                                        readonly=False)  # Montant CARFO Patronal = 15.5% du salaire de base
    x_mnt_cnss = fields.Float(string="Montant CNSS", readonly=False, store=True,
                              compute='mnt_cnss_field')  # Montant CNSS = 5.5% de la remuneration total
    x_mnt_patronal_cnss = fields.Float(string="Part Patronale CNSS", readonly=False,
                                       store=True)  # Montant CNSS Patronal = 16% de la remuneration total
    # Retenue IUTS
    x_retenue_iuts = fields.Float(string="IUTS 0 Charge", readonly=False, store=True, compute='retenue_iuts_field')
    x_iuts_net = fields.Float(string="IUTS avec Charge", readonly=True, compute='net_iuts_fields')
    x_net_payer = fields.Float(string="Net à payer", readonly=False, store=True,
                               compute='net_payer_field')  # Net à payer fonctionnaire

    # Affectation du solde indiciaire et calcul du salaire de base fonctionnaire
    @api.depends('x_echellon_id', 'x_classees_id', 'x_echelle_id', 'x_categorie_id')
    def cal_sal_base(self):
        for rec in self:
            val_class = int(rec.x_classees_id)
            val_echel = int(rec.x_echelle_id)
            val_echellon = int(rec.x_echellon_id)
            val_cat = int(rec.x_categorie_id)
            if val_class != False and val_echel != False and val_echellon != False and val_cat != False:
                res = self.env['hr_grillesalariale'].search(
                    [('x_echellon_id', '=', val_echellon), ('x_class_id', '=', val_class),
                     ('x_categorie_id', '=', val_cat),
                     ('x_echelle_id', '=', val_echel)])
                rec.x_solde_indiciaire = round(res.x_salbase)  # x_salbase=solde indiciaire
                x_indemnite_residence = round((res.x_salbase * 10 / 100) + 0.1)  # 0.1:permet de mieux arrondir
                rec.x_solde_indiciaire_net = rec.x_solde_indiciaire + x_indemnite_residence  # solde indiciaire_net= salaire de base
                rec.x_indice = round(res.x_indice)
            else:
                rec.x_solde_indiciaire = 0
                rec.x_indemnite_residence = 0
                rec.x_solde_indiciaire_net = 0
                rec.x_indice = 0

    # def write(self, vals):
    #     struct_id = self.struct_id
    #     vals['struct_id'] = None
    #     super(HrEmployee, self).write(vals)
    #     print(self.struct_id)
    #     vals['struct_id'] = struct_id
    #     return super(HrEmployee, self).write(vals)

    @api.depends('x_solde_indiciaire', 'struct_id')
    def cal_x_indemnite_residence(self):
        for rec in self:
            if rec.struct_id.code == 'FCT_MD':
                rec.x_indemnite_residence = round((rec.x_solde_indiciaire * 10 / 100) + 0.1)
            else:
                rec.x_indemnite_residence = 0

    # fonction de recherche permettant de retourner le salaire de base dans la grille des contractuels en fonction
    # des paramètres
    @api.onchange('x_echellon_c_id', 'x_echelle_c_id', 'x_categorie_c_id')
    def cal_sal_basec(self):
        for rec in self:
            val_echel_c = int(rec.x_echelle_c_id)
            val_echellon_c = int(rec.x_echellon_c_id)
            val_struct = int(rec.company_id.id)
            val_cat_c = int(rec.x_categorie_c_id)
            if val_echel_c != False and val_echellon_c != False and val_cat_c != False and val_struct != False:
                res = self.env['hr_grillesalariale_contractuel'].search(
                    [('x_echellon_c_id', '=', val_echellon_c), ('x_categorie_c_id', '=', val_cat_c),
                     ('x_echelle_c_id', '=', val_echel_c), ('company_id', '=', val_struct)])
                rec.x_solde_indiciaire_ctrct = round(res.x_salbase_ctrt)
            else:
                rec.x_solde_indiciaire_ctrct = 0

    # Affectation
    @api.depends('x_solde_indiciaire', 'x_solde_indiciaire_ctrct', 'struct_id')
    def cal_sal_base_c_f(self):
        for rec in self:
            rec.x_salaire_base = 0
            if rec.struct_id.code == 'FCT_MD':
                rec.x_salaire_base = rec.x_solde_indiciaire_net
            if rec.struct_id.code in ('CTRCT', 'FCT_DETACH'):
                rec.x_salaire_base = rec.x_solde_indiciaire_ctrct

                # fonction qui permet d'additionner les indemnités

    @api.depends('x_indem_resp', 'x_indem_astr', 'x_indem_techn', 'x_indem_specif', 'x_indem_spec_inspect_trav',
                 'x_indem_spec_inspect_irp', 'x_indem_spec_inspect_ish', 'x_indem_spec_inspect_ifc', 'x_indem_loge',
                 'x_indem_transp', 'x_indem_inform', 'x_indem_exploit', 'x_indem_finance', 'x_indem_garde',
                 'x_indem_risque', 'x_indem_suj', 'x_indem_form', 'x_indem_caisse', 'x_solde_indiciaire',
                 'x_indem_prime_rendement')
    def cal_total_indem(self):
        for val in self:
            val.x_total_indemnites = round(
                val.x_indem_resp + val.x_indem_astr + val.x_indem_techn + val.x_indem_specif
                + val.x_indem_spec_inspect_trav + val.x_indem_spec_inspect_irp + val.x_indem_spec_inspect_ish
                + val.x_indem_spec_inspect_ifc + val.x_indem_loge + val.x_indem_transp + val.x_indem_inform
                + val.x_indem_exploit + val.x_indem_finance + val.x_indem_garde + val.x_indem_risque
                + val.x_indem_suj + val.x_indem_form + val.x_indem_caisse + val.x_indem_prime_rendement + val.x_indemnite_residence)

    @api.depends('x_salaire_base', 'x_total_indemnites')
    def calcul_remuneration_total(self):
        for rec in self:
            if rec.struct_id.code == 'FCT_MD':
                rec.x_remuneration_total = (rec.x_solde_indiciaire + rec.x_total_indemnites)
            if rec.struct_id.code in ('CTRCT', 'FCT_DETACH'):
                rec.x_remuneration_total = (rec.x_salaire_base + rec.x_total_indemnites)

    # fonction qui permet d'avoir le montant carfo à partir du salaire de base
    @api.onchange('struct_id', 'x_solde_indiciaire')
    def mnt_carfo_fields(self):
        for rec in self:
            if rec.struct_id.code in ('FCT_MD', 'FCT_DETACH'):
                rec.x_mnt_carfo = round((rec.x_solde_indiciaire * 8) / 100)
                rec.x_mnt_patronal_carfo = round((rec.x_solde_indiciaire * 15.5) / 100)

                rec.x_mnt_cnss = 0.0
                rec.x_mnt_patronal_cnss = 0.0

    # fonction qui permet d'avoir le montant cnss à partir du salaire de base
    @api.depends('struct_id', 'x_remuneration_total')
    def mnt_cnss_field(self):
        for val in self:
            if val.struct_id.code == 'CTRCT':
                resul = round((val.x_remuneration_total * 5.5) / 100)
                if resul > 33000:
                    val.x_mnt_cnss = 33000
                    val.x_mnt_patronal_cnss = 96000
                else:
                    val.x_mnt_cnss = round((val.x_remuneration_total * 5.5) / 100)
                    val.x_mnt_patronal_cnss = round((val.x_remuneration_total * 16) / 100)

                val.x_mnt_carfo = 0.0
                val.x_mnt_patronal_carfo = 0.0

    # fonction qui permet d'avoir le montant cnss à partir du salaire de base
    @api.depends('x_mnt_carfo', 'x_mnt_cnss', 'x_iuts_net')
    def cal_total_retenue(self):
        for rec in self:
            rec.mnt_total_retenues = rec.x_mnt_carfo + rec.x_mnt_cnss + rec.x_iuts_net

    # calcul du salaire brut pour le fonctionnaire puisque Rénumeration Totale déjà connue
    @api.depends('x_remuneration_total', 'x_mnt_carfo', 'x_salaire_base')
    def calcul_salaire_brut(self):
        for vals in self:
            cnss_deductible = 0
            if vals.struct_id.code == 'CTRCT':
                cnss_sb = vals.x_salaire_base * 0.08
                cnss_rt = vals.x_remuneration_total * (5.5 / 100)
                cnss_deductible = min([cnss_sb, cnss_rt, 33000])

            vals.x_salaire_brut = vals.x_remuneration_total - cnss_deductible - vals.x_mnt_carfo

    # calcul abattement forfaitaire
    @api.depends('x_salaire_base', 'x_solde_indiciaire')
    def calcul_abattement(self):
        for vals in self:
            x_cat = vals.x_categorie_c_id.name
            x_cat_f = vals.x_categorie_id.name
            if vals.struct_id.code in ('CTRCT', 'FCT_DETACH'):
                if x_cat == '1' or x_cat == '2' or x_cat == '6':
                    vals.x_abattement_forfaitaire = round((vals.x_salaire_base * 20) / 100)
                else:
                    vals.x_abattement_forfaitaire = round((vals.x_salaire_base * 25) / 100)
            if vals.struct_id.code == 'FCT_MD':
                if x_cat_f == 'A' or x_cat_f == 'B' or x_cat_f == 'P':
                    vals.x_abattement_forfaitaire = round((vals.x_solde_indiciaire * 20) / 100)
                else:
                    vals.x_abattement_forfaitaire = round((vals.x_solde_indiciaire * 25) / 100)

    # calcul DU SALAIRE NET IMPOSABLE (SNI)
    @api.depends('x_salaire_brut', 'x_total_exo', 'x_abattement_forfaitaire')
    def calcul_sni(self):
        for vals in self:
            val1 = vals.x_salaire_brut - vals.x_total_exo - vals.x_abattement_forfaitaire
            val2 = val1 - (val1 % 100)
            vals.x_salaire_net_imposable = val2

    # fonction qui permet d'avoir la de l'iuts
    @api.depends('x_salaire_net_imposable')
    def retenue_iuts_field(self):
        for val in self:
            if 0 <= val.x_salaire_net_imposable <= 30000.0:
                val.x_retenue_iuts = round(val.x_salaire_net_imposable * 0)
            elif 30001.0 <= val.x_salaire_net_imposable <= 50000.0:
                val.x_retenue_iuts = round((val.x_salaire_net_imposable - 30001.0) * 12.1 / 100)
            elif 50001.0 <= val.x_salaire_net_imposable <= 80000.0:
                val.x_retenue_iuts = round(((val.x_salaire_net_imposable - 50001.0) * 13.9 / 100) + 2420)
            elif 80001.0 <= val.x_salaire_net_imposable <= 120000.0:
                val.x_retenue_iuts = round(((val.x_salaire_net_imposable - 80001.0) * 15.7 / 100) + 6590)
            elif 120001.0 <= val.x_salaire_net_imposable <= 170000.0:
                val.x_retenue_iuts = round(((val.x_salaire_net_imposable - 120001.0) * 18.4 / 100) + 12870)
            elif 170001.0 <= val.x_salaire_net_imposable <= 250000.0:
                val.x_retenue_iuts = round(((val.x_salaire_net_imposable - 170001.0) * 21.7 / 100) + 22070)
            else:
                val.x_retenue_iuts = round(((val.x_salaire_net_imposable - 250001.0) * 25 / 100) + 39430)

    # fonction qui permet d'avoir le montant en fonction du nombre de charge pour les deux
    @api.depends('charge', 'x_retenue_iuts')
    def mnt_charge_field(self):
        for val in self:
            if val.charge == 0:
                val.x_montant_charge = round(val.x_retenue_iuts * 0)
            elif val.charge == 1:
                val.x_montant_charge = round((val.x_retenue_iuts * 8) / 100)
            elif val.charge == 2:
                val.x_montant_charge = round((val.x_retenue_iuts * 10) / 100)
            elif val.charge == 3:
                val.x_montant_charge = round((val.x_retenue_iuts * 12) / 100)
            else:
                val.x_montant_charge = round((val.x_retenue_iuts * 14) / 100)

    # fonction qui permet d'avoir le montant net de l'iuts pour le contractuel 031002
    @api.depends('x_retenue_iuts', 'x_montant_charge')
    def net_iuts_fields(self):
        for val in self:
            val.x_iuts_net = round(val.x_retenue_iuts - val.x_montant_charge)

    # fonction qui permet d'avoir le montant net à payer pour le fonctionnaires
    @api.depends('x_remuneration_total', 'x_allocation_familial', 'mnt_total_retenues', )
    def net_payer_field(self):
        for val in self:
            val.x_net_payer = round(
                val.x_remuneration_total + val.x_allocation_familial - val.mnt_total_retenues)

    # fonction de calcul du montant de l'allocation
    @api.depends('charge', 'struct_id')
    def mnt_alloc(self):
        for vals in self:
            if vals.struct_id.code in ('FCT_MD', 'FCT_DETACH'):
                vals.x_allocation_familial = vals.charge_enfant * 2000
            else:
                vals.x_allocation_familial = 0

    # Calcul exonérations
    @api.depends('x_indem_resp', 'x_salaire_brut')
    def calcul_exoneration_responsabilite(self):
        for vals in self:
            indem_resp = vals.x_indem_resp
            premiere_limite_res = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_resp_exo = min([premiere_limite_res, indem_resp, 50000])

    @api.depends('x_indem_astr', 'x_salaire_brut')
    def calcul_exoneration_astreintes(self):
        for vals in self:
            premiere_limite_astr = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_astr_exo = min([premiere_limite_astr, vals.x_indem_astr, 50000])

    # indemnités de technicités
    @api.depends('x_indem_techn', 'x_salaire_brut')
    def calcul_exoneration_technicites(self):
        for vals in self:
            premiere_limite_tech = round((vals.x_salaire_brut * 5) / 100)
            x_indem_techn = round(vals.x_indem_techn)
            vals.x_indem_techn_exo = min([premiere_limite_tech, x_indem_techn, 50000])

    # indemnités de spécifiques GRH
    @api.depends('x_indem_specif', 'x_salaire_brut')
    def calcul_exoneration_specifiques(self):
        for vals in self:
            vals.x_indem_specif_exo = 0
            premiere_limite_spec = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_specif_exo = min([premiere_limite_spec, vals.x_indem_specif, 50000])

    # indemnités de spécifiques IT
    @api.depends('x_indem_spec_inspect_trav', 'x_salaire_brut')
    def calcul_exoneration_specifiques_it(self):
        for vals in self:
            premiere_limite_spec_it = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_specif_it_exo = min([premiere_limite_spec_it, vals.x_indem_spec_inspect_trav, 50000])

    # indemnités de spécifiques IRP
    @api.depends('x_indem_spec_inspect_irp', 'x_salaire_brut')
    def calcul_exoneration_specifiques_irp(self):
        for vals in self:
            premiere_limite_spec_irp = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_specif_irp_exo = min([premiere_limite_spec_irp, vals.x_indem_spec_inspect_irp, 50000])

    # indemnités de spécifiques ISH
    @api.depends('x_indem_spec_inspect_ish', 'x_salaire_brut')
    def calcul_exoneration_specifiques_ish(self):
        for vals in self:
            premiere_limite_spec_ish = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_specif_ish_exo = min([premiere_limite_spec_ish, vals.x_indem_spec_inspect_ish, 30000])

    # indemnités de spécifiques IFC
    @api.depends('x_indem_spec_inspect_ifc', 'x_salaire_brut')
    def calcul_exoneration_specifiques_ifc(self):
        for vals in self:
            premiere_limite_ifc = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_specif_ifc_exo = min([premiere_limite_ifc, vals.x_indem_spec_inspect_ifc, 50000])

    # indemnités d'informatiques
    @api.depends('x_indem_inform', 'x_salaire_brut')
    def calcul_exoneration_informatique(self):
        for vals in self:
            premiere_limite_info = round(vals.x_salaire_brut * 0.05)
            vals.x_indem_inform_exo = min([premiere_limite_info, vals.x_indem_inform, 50000])

    # indemnités de exploitations reseaux
    @api.depends('x_indem_exploit', 'x_salaire_brut')
    def calcul_exoneration_exploitation(self):
        for vals in self:
            premiere_limite_exploi = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_exploit_exo = min([premiere_limite_exploi, vals.x_indem_exploit, 50000])

    # indemnités de responsabilités financières
    @api.depends('x_indem_finance', 'x_salaire_brut')
    def calcul_exoneration_resp_finance(self):
        for vals in self:
            premiere_limite_finance = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_finance_exo = min([premiere_limite_finance, vals.x_indem_finance, 50000])

    # indemnités de garde
    @api.depends('x_indem_garde', 'x_salaire_brut')
    def calcul_exoneration_garde(self):
        for vals in self:
            premiere_limite_garde = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_garde_exo = min([premiere_limite_garde, vals.x_indem_garde, 50000])

    # indemnités de risque
    @api.depends('x_indem_risque', 'x_salaire_brut')
    def calcul_exoneration_risque(self):
        for vals in self:
            premiere_limite_risque = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_risque_exo = min([premiere_limite_risque, vals.x_indem_risque, 50000])

    # indemnités de sujetion
    @api.depends('x_indem_suj', 'x_salaire_brut')
    def calcul_exoneration_sujetion(self):
        for vals in self:
            premiere_limite_suj = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_suj_exo = min([premiere_limite_suj, vals.x_indem_suj, 50000])

    # indemnités de formation
    @api.depends('x_indem_form', 'x_salaire_brut')
    def calcul_exoneration_formation(self):
        for vals in self:
            premiere_limite_form = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_form_exo = min([premiere_limite_form, vals.x_indem_form, 50000])

    # indemnités de caisse
    @api.depends('x_indem_caisse', 'x_salaire_brut')
    def calcul_exoneration_caisse(self):
        for vals in self:
            premiere_limite_caisse = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_caisse_exo = min([premiere_limite_caisse, vals.x_indem_caisse, 50000])

    # indemnités de veste
    @api.depends('x_indem_veste', 'x_salaire_brut')
    def calcul_exoneration_veste(self):
        for vals in self:
            premiere_limite_veste = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_veste_exo = min([premiere_limite_veste, vals.x_indem_veste, 50000])

    # indemnités de residence
    @api.depends('x_solde_indiciaire', 'x_salaire_brut')
    def calcul_exoneration_residence(self):
        for vals in self:
            if vals.struct_id.code == 'FCT_MD':
                premiere_limite = round((vals.x_salaire_brut * 5) / 100)
                vals.x_indem_residence_exo = min([premiere_limite, vals.x_solde_indiciaire, 50000])
            else:
                vals.x_indem_residence_exo = 0

    # exoneration prime de rendement
    @api.depends('x_indem_prime_rendement', 'x_salaire_brut')
    def calcul_exoneration_prime(self):
        for vals in self:
            premiere_limite = round((vals.x_salaire_brut * 5) / 100)
            vals.x_prime_exo = min([premiere_limite, vals.x_indem_prime_rendement, 50000])

    @api.depends('x_indem_loge', 'x_salaire_brut')
    def calcul_exoneration_logement(self):
        for vals in self:
            premiere_limite_log = round((vals.x_salaire_brut * 20) / 100)
            vals.x_indem_loge_exo = min([premiere_limite_log, vals.x_indem_loge, 75000])

    @api.depends('x_indem_transp', 'x_salaire_brut')
    def calcul_exoneration_transport(self):
        for vals in self:
            premiere_limite_trans = round((vals.x_salaire_brut * 5) / 100)
            vals.x_indem_transp_exo = min([premiere_limite_trans, vals.x_indem_transp, 30000])

    @api.depends('x_indem_resp_exo', 'x_indem_astr_exo', 'x_indem_techn_exo', 'x_indem_specif_exo',
                 'x_indem_specif_it_exo', 'x_indem_specif_ifc_exo', 'x_indem_specif_irp_exo',
                 'x_indem_specif_ish_exo', 'x_indem_loge_exo', 'x_indem_transp_exo', 'x_indem_inform_exo',
                 'x_indem_exploit_exo', 'x_indem_finance_exo', 'x_indem_garde_exo', 'x_indem_risque_exo',
                 'x_indem_suj_exo', 'x_indem_form_exo', 'x_indem_caisse_exo', 'x_indem_veste_exo', 'x_prime_exo',
                 'x_indem_residence_exo')
    def calcul_exoneration_total(self):
        for vals in self:
            vals.x_total_exo = vals.x_indem_resp_exo + vals.x_indem_astr_exo + vals.x_indem_techn_exo + vals.x_indem_specif_exo + vals.x_indem_loge_exo + vals.x_indem_transp_exo + vals.x_indem_inform_exo + vals.x_indem_exploit_exo + vals.x_indem_finance_exo + vals.x_indem_garde_exo + vals.x_indem_risque_exo + vals.x_indem_suj_exo + vals.x_indem_form_exo + vals.x_indem_caisse_exo + vals.x_indem_veste_exo + vals.x_indem_specif_it_exo + vals.x_indem_specif_ifc_exo + vals.x_indem_specif_irp_exo + vals.x_indem_specif_ish_exo + vals.x_prime_exo + vals.x_indem_residence_exo

    @api.depends('charge_femme', 'charge_enfant')
    def calcul_charge(self):
        self.charge = self.charge_femme + self.charge_enfant

    enfants_ids = fields.One2many('hr_employee_enfant', 'employee_id', string="Enfant", index=True)
    x_acte_ids = fields.One2many('hr_piece_detachement', 'x_employees_id', string='Joindre acte Detachement')
    x_acte_dec_ids = fields.One2many('hr_piece_disposition', 'x_employees_id', string='Joindre acte Decision')
    x_actes_ids = fields.One2many('hr_decret_nomination', 'x_employees_id', string='Joindre acte de nomination')
    x_dossier_ind_ids = fields.One2many('hr_dossier_individuel', 'x_employees_id', string='Joindre Fichiers')
    x_note_ids = fields.One2many(
        comodel_name='hr_employee_note',
        inverse_name='employee_id',
        string='Notes',
        required=False)

    # FONCTION DE calcul de la date de depart a la retraite d'un employe
    @api.depends("x_date_naissance", "x_nb_annee_retraite")
    def _compute_date_retraite(self):
        for rec in self:
            rec.x_date_retraite = None
            if rec.x_nb_annee_retraite or rec.x_date_naissance:
                anne = int(rec.x_nb_annee_retraite.name)
                date1 = rec.x_date_naissance.strftime("%Y-%m-%d")
                date2 = rec.x_date_naissance.year + anne
                rec.x_date_retraite = datetime(date2, rec.x_date_naissance.month, rec.x_date_naissance.day, 0, 0, 0,
                                               0).date()

    @api.depends('enfants_ids')
    def calcul_charge_enfant(self):
        for val in self:
            charge_enfant = 0
            for e in val.enfants_ids:
                if e.etat == "1" and e.age <= 15:
                    charge_enfant = charge_enfant + 1
                elif e.etat == "2" and e.age <= 17:
                    charge_enfant = charge_enfant + 1
                elif e.etat == "3" and e.age <= 20:
                    charge_enfant = charge_enfant + 1
                elif e.etat == "4":
                    charge_enfant = charge_enfant + 1

            val.charge_enfant = charge_enfant

class HrEmployeesEnfant(models.Model):
    _name = "hr_employee_enfant"

    name = fields.Char(string="Nom et Prénoms", required=True)
    date_naissance = fields.Date(string='Date naissance', required=True, default=date.today())
    age = fields.Integer(string="Age", compute="calcul_age")

    est_pris = fields.Boolean(string='Est_pris', required=False, compute="cal_est_pris")

    etat = fields.Selection([
        ('1', 'Ordinaire'),
        ('2', 'Apprentissage'),
        ('3', 'Etude'),
        ('4', 'Besoin Spécifique'),
    ], 'Status', default='1', required=True)

    sexe = fields.Selection([
        ('M', 'Masculin'),
        ('F', 'Feminin'),
    ], 'Sexe', default='M', index=True, required=True)

    employee_id = fields.Many2one('hr.employee', string='Employé', required=True)

    @api.depends('date_naissance')
    def calcul_age(self):
        for val in self:
            val.age = 0
            if val.date_naissance:
                start_date = date.today()
                end_date = val.date_naissance
                val.age = (start_date - end_date).days / 365

    @api.depends('etat', 'age')
    def cal_est_pris(self):
        for val in self:
            if val.etat == "1" and val.age <= 15:
                val.est_pris = True
            elif val.etat == "2" and val.age <= 17:
                val.est_pris = True
            elif val.etat == "3" and val.age <= 20:
                val.est_pris = True
            elif val.etat == "4":
                val.est_pris = True
            else:
                val.est_pris = False

# Creation de la classe hr_employee_ fichier_oint_line permettant d'ajouter un  acte de detachement
class HrEmployeePieceDetachement(models.Model):
    _name = "hr_piece_detachement"
    x_employees_id = fields.Many2one('hr.employee', string='Employee')
    date_acte = fields.Date(string="Date acte")
    date_effet = fields.Date(string="Date effet")
    date_fin = fields.Date(string="Date fin")
    ref_acte = fields.Char(string="Ref acte")
    fichier_joint = fields.Binary(string='Joindre acte', attachment=True)

# Creation de la classe hr_employee_ decret_nomination permettant d'ajouter un  acte de nomination
class HrEmployeeDecretNomination(models.Model):
    _name = "hr_decret_nomination"
    x_employees_id = fields.Many2one('hr.employee', string='Employee')
    date_nomination = fields.Date(string="Date nomination")
    date_effet = fields.Date(string="Date effet")
    date_fin = fields.Date(string="Date fin")
    ref_acte = fields.Char(string="Ref acte nomination")
    fichier_joint = fields.Binary(string='Joindre acte', attachment=True)
    etat_nomination = fields.Selection([
        ('1', "En cours"),
        ('2', "Terminé"),
    ], required=True, string="Etat Nomination", default=1)

# Creation de la classe hr_employee_ fichier_oint_line permettant d'ajouter un  acte de decision
class HrEmployeePieceDecision(models.Model):
    _name = "hr_piece_disposition"
    x_employees_id = fields.Many2one('hr.employee', string='Employee')
    date_acte_dec = fields.Date(string="Date acte disposition")
    date_effet_dec = fields.Date(string="Date effet disposition")
    date_fin_dec = fields.Date(string="Date fin disposition")
    ref_acte_dec = fields.Char(string="Ref acte disposition")
    fichier_joint_dec = fields.Binary(string='Joindre acte disposition', attachment=True)

# Creation de la classe hr_employee_ fichier_joint_line permettant de suivre le dossier individuel du personnel
class HrEmployeeDossierIndividuel(models.Model):
    _name = "hr_dossier_individuel"
    x_employees_id = fields.Many2one('hr.employee', string='Employee')
    date_op = fields.Date(string="Date", default=date.today())
    objet_ligne = fields.Char(string="Intitulé")
    fichier_joint = fields.Binary(string='Joindre Fichier', attachment=True)

class HrEmployeeNote(models.Model):
    _name = "hr_employee_note"

    note = fields.Integer(string='Note', required=False)
    employee_id = fields.Many2one(
        comodel_name='hr.employee',
        string='Employee',
        required=False)

    @api.model
    def annee_selection(self):
        year = 2000  # replace 2000 with your a start year
        year_list = []
        while year != 2100:  # replace 2030 with your end year
            year_list.append((str(year), str(year)))
            year += 1
        return year_list

    def _default_annee(self):
        print(fields.Date.context_today(self).year)
        return str(fields.Date.context_today(self).year)

    annee = fields.Selection(
        annee_selection,
        string="Année",
        default=_default_annee)