# -*- coding: utf-8 -*-
from django import template
from django.template import Context
from django.core.urlresolvers import reverse

from html5tags.templatetags import CaktNode, parse_args_kwargs

register = template.Library()

@register.tag("breadcrumb")
def do_breadcrumb(parser, token):
    try:
        tag_name, breadcrumb, request = token.split_contents()
    except ValueError:
        raise template.TemplateSyntaxError("%r tag requires a single argument" % tag_name)
    return BreadcrumbNode(breadcrumb, request)

class BreadcrumbNode(template.Node):
    def __init__(self, breadcrumb, request):
        self.breadcrumb = template.Variable(breadcrumb)
        self.request = template.Variable(request)

    def render(self, context):
        t = template.loader.get_template("breadcrumb.html")
        new_context = Context({'breadcrumb': self.breadcrumb.resolve(context),
                               'request': self.request.resolve(context)}, autoescape=context.autoescape)
        return t.render(new_context)

class AddCrumbNode(CaktNode):
    def render_with_args(self, context, crumb, url=None, *args, **kwargs):
        href = None
        if url:
            if '/' in url:
                href = url
            else:
                href = reverse(url, args=args)
        if 'request' in context:
            if not hasattr(context['request'], 'breadcrumbs'):
                context['request'].breadcrumbs = []
            context['request'].breadcrumbs.append({"name": crumb, "url": href})
        return ''

@register.tag
def add_crumb(parser, token):
    tag_name, args, kwargs = parse_args_kwargs(parser, token)
    return AddCrumbNode(*args, **kwargs)

@register.inclusion_tag('breadcrumb.html', takes_context=True)
def render_breadcrumbs(context):
    if 'request' in context and hasattr(context['request'], 'breadcrumbs'):
        crumbs = context['request'].breadcrumbs
    else:
        crumbs = None
    if crumbs and len(crumbs) == 0:
        crumbs = None
    return {
        'breadcrumb': crumbs,
    }


# Data from Saraga et al. (2003) paper
# changed morphology and some channel densities (BPG 12-1-09)
#   OLM_Cell
# basic_shape,
# pre_list, connect2target
# soma, dend1, dend2, axon
# all
from neuron import h

class BistratifiedCell():
    """ Bistratified Cell definition """
    def __init__(self, gid = -1):
        self.x = 0; self.y = 0; self.z = 0
        self.gid = gid
        self.create_sections()
        self.build_topology()
        self.build_subsets() # subsets()
        self.define_geometry() # geom()
        self.define_biophysics() # biophys()
        # pre_list = new List()
        self.addSynapses() # synapses
        self.is_art = 0
        self.nc = []

    def __repr__(self):
        return "Bistratified Cell {}".format(self.gid)

    def create_sections(self):
        self.soma = h.Section(name='soma', cell=self)
        self.radT2 = h.Section(name='radT2', cell=self)
        self.radM2 = h.Section(name='radM2', cell=self)
        self.radt2 = h.Section(name='radt2', cell=self)
        self.radT1 = h.Section(name='radT1', cell=self)
        self.radM1 = h.Section(name='radM1', cell=self)
        self.radt1 = h.Section(name='radt1', cell=self)
        self.oriT1 = h.Section(name='oriT1', cell=self)
        self.oriM1 = h.Section(name='oriM1', cell=self)
        self.orit1 = h.Section(name='orit1', cell=self)
        self.oriT2 = h.Section(name='oriT2', cell=self)
        self.oriM2 = h.Section(name='oriM2', cell=self)
        self.orit2 = h.Section(name='orit2', cell=self)

    def build_topology(self):
        self.radT2.connect(self.soma(1))
        self.radM2.connect(self.radT2(1))
        self.radt2.connect(self.radM2(1))
        self.radT1.connect(self.soma(0))
        self.radM1.connect(self.radT1(1))
        self.radt1.connect(self.radM1(1))

        self.oriT1.connect(self.soma(0))
        self.oriM1.connect(self.oriT1(1))
        self.orit1.connect(self.oriM1(1))

        self.oriT2.connect(self.soma(1))
        self.oriM2.connect(self.oriT2(1))
        self.orit2.connect(self.oriM2(1))

    def define_geometry(self):
        for sec in self.all:
            sec.pt3dclear()

        h.pt3dadd(0, 0, 0, 10, sec=self.soma)
        h.pt3dadd(15, 0, 0, 10, sec=self.soma)

        h.pt3dadd(15, 0, 0, 1, sec=self.radT2)
        h.pt3dadd(45, 30, 0, 1, sec=self.radT2)

        h.pt3dadd(45, 30, 0, 1, sec=self.radM2)
        h.pt3dadd(75, 60, 0, 1, sec=self.radM2)

        h.pt3dadd(75, 60, 0, 1, sec=self.radt2)
        h.pt3dadd(90, 75, 0, 1, sec=self.radt2)

        h.pt3dadd(0, 0, 0, 1, sec=self.radT1)
        h.pt3dadd(-14, 15, 0, 1, sec=self.radT1)

        h.pt3dadd(-14, 15, 0, 1, sec=self.radM1)
        h.pt3dadd(-29, 30, 0, 1, sec=self.radM1)

        h.pt3dadd(-29, 30, 0, 1, sec=self.radt1)
        h.pt3dadd(-44, 45, 0, 1, sec=self.radt1)

        h.pt3dadd(0, 0, 0, 1, sec=self.oriT1)
        h.pt3dadd(-29, -29, 0, 1, sec=self.oriT1)

        h.pt3dadd(-29, -29, 0, 1, sec=self.oriM1)
        h.pt3dadd(-59, -59, 0, 1, sec=self.oriM1)

        h.pt3dadd(-59, -59, 0, 1, sec=self.orit1)
        h.pt3dadd(-89, -89, 0, 1, sec=self.orit1)

        h.pt3dadd(15, 0, 0, 1, sec=self.oriT2)
        h.pt3dadd(45, -29, 0, 1, sec=self.oriT2)

        h.pt3dadd(45, -29, 0, 1, sec=self.oriM2)
        h.pt3dadd(75, -59, 0, 1, sec=self.oriM2)

        h.pt3dadd(75, -59, 0, 1, sec=self.orit2)
        h.pt3dadd(105, -89, 0, 1, sec=self.orit2)

        self.soma.L = 20
        self.soma.diam = 10
        self.radT2.L = 100
        self.radT2.diam = 4
        self.radM2.L = 100
        self.radM2.diam = 3
        self.radt2.L = 200
        self.radt2.diam = 2

        self.radT1.L = 100
        self.radT1.diam = 4
        self.radM1.L = 100
        self.radM1.diam = 3
        self.radt1.L = 200
        self.radt1.diam = 2

        self.oriT1.L = 100
        self.oriT1.diam = 2
        self.oriM1.L = 100
        self.oriM1.diam = 1.5
        self.orit1.L = 100
        self.orit1.diam = 1

        self.oriT2.L = 100
        self.oriT2.diam = 2
        self.oriM2.L = 100
        self.oriM2.diam = 1.5
        self.orit2.L = 100
        self.orit2.diam = 1

        for sec in self.all:
            h("lf = lambda_f(100)")
            sec.nseg = int((sec.L/(0.1*h.lf)+.9)/2)*2 + 1

    def build_subsets(self):
        self.all = h.SectionList()
        self.all.wholetree(sec=self.soma)

    def define_biophysics(self):

        gna = 0.3

        self.soma.insert("ichan2")
        for seg in self.soma:
            seg.gnatbar_ichan2 = gna  		# 0.12 //original 0.030 to .055
            seg.gkfbar_ichan2 = 0.013  		# original 0.015
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.radt1.insert("ichan2")
        for seg in self.radt1:
            seg.gnatbar_ichan2 = gna  		# 0.4  	//original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.radt2.insert("ichan2")
        for seg in self.radt2:
            seg.gnatbar_ichan2 = gna  		# 0.4  	//original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.radM1.insert("ichan2")
        for seg in self.radM1:
            seg.gnatbar_ichan2 = gna  		# 0.3  	//original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.radM2.insert("ichan2")
        for seg in self.radM2:
            seg.gnatbar_ichan2 = gna  		# 0.3  	//original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.radT1.insert("ichan2")
        for seg in self.radT1:
            seg.gnatbar_ichan2 = gna  		# 0.2  	//original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.radT2.insert("ichan2")
        for seg in self.radT2:
            seg.gnatbar_ichan2 = gna  		# 0.2  	//original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.oriT1.insert("ichan2")
        for seg in self.oriT1:
            seg.gnatbar_ichan2 = gna  		# original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.oriT2.insert("ichan2")
        for seg in self.oriT2:
            seg.gnatbar_ichan2 = gna  		# original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.oriM1.insert("ichan2")
        for seg in self.oriM1:
            seg.gnatbar_ichan2 = gna  		# original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.oriM2.insert("ichan2")
        for seg in self.oriM2:
            seg.gnatbar_ichan2 = gna  		# original 0.015
            seg.gkfbar_ichan2 = 0.013
            seg.gl_ichan2 = 0.00018
            cm=1.4

        self.orit1.insert("ichan2")
        for seg in self.orit1:
            seg.gnatbar_ichan2 = gna  		# Sodium conductance (original 0.015)
            seg.gkfbar_ichan2 = 0.013  		# Delayed K+ rectifier (fast)
            seg.gl_ichan2 = 0.00018         # Leak conductance
            cm=1.4

        self.orit2.insert("ichan2")
        for seg in self.orit2:
            seg.gnatbar_ichan2 = gna  		# Sodium conductance (original 0.015)
            seg.gkfbar_ichan2 = 0.013  		# Delayed K+ rectifier (fast)
            seg.gl_ichan2 = 0.00018         # Leak conductance
            cm=1.4

        for sec in self.all:
            # self.cm = Not setting cm
            self.Ra = 100			# 31.3 +/- 10.9
            self.enat = 55
            self.ekf = -90
            self.ek = -90
            self.elca = 130
            self.esk = -90
            self.el_ichan2 = -60			#-60.06
            self.cao_ccanl = 2

            sec.insert("ccanl")
            for seg in sec:
                seg.catau_ccanl = 10		# Time constant for decay of intracellular Ca2+
                seg.caiinf_ccanl = 5.e-6		# Steady-state intracellular Ca2+ concentration

            sec.insert("borgka")
            for seg in sec:
                seg.gkabar_borgka = 0.00015		# A-type K+ conductance

            sec.insert("nca") # N-type Ca2+ conductance
            for seg in sec:
                seg.gncabar_nca = 0.0008   		# check to modify- original 0.004

            sec.insert("lca")
            for seg in sec:
                seg.glcabar_lca = 0.005		# L-type Ca2+ conductance

            sec.insert("gskch")
            for seg in sec:
                seg.gskbar_gskch = 0.000002		# Ca2+-dependent K (SK) conductance

            sec.insert("mykca")
            for seg in sec:
                seg.gkbar_mykca = 0.0002			# Ca2+ and Voltage-dependent K+ (BK) conductance
		 					# make catau slower70e-3 	cao=2 cai=50.e-6

    def connect2target(self,target, delay = 1, weight=0.04): # { localobj nc #$o1 target point process, optional $o2 returned NetCon
        self.nc.append(h.NetCon(self.soma(0.5)._ref_v, target, sec=self.soma))
        self.nc[-1].threshold = -10 # mV
        self.nc[-1].delay = delay # ms
        self.nc[-1].weight[0] = weight # NetCon weight is a vector
        return self.nc[-1]

    def addSynapses(self):
        self.pre_list = []

        # E0
        syn_ = h.MyExp2Syn(self.radM1(0.5))
        self.pre_list.append(syn_)    # AMPA        EC (not used)
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # E1
        syn_ = h.MyExp2Syn(self.radM2(0.5))
        self.pre_list.append(syn_)    # AMPA        EC
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # E2
        syn_ = h.MyExp2Syn(self.radM1(0.5))
        self.pre_list.append(syn_)    # AMPA        CA3 Shaffer collateral
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # E3
        syn_ = h.MyExp2Syn(self.radM2(0.5))
        self.pre_list.append(syn_)    # AMPA        CA3 Shaffer collateral
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # E4
        syn_ = h.MyExp2Syn(self.radT1(0.5))
        self.pre_list.append(syn_)    # AMPA        CA3 Shaffer collateral
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # E5
        syn_ = h.MyExp2Syn(self.radT2(0.5))
        self.pre_list.append(syn_)    # AMPA        CA3 Shaffer collateral
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # E6
        syn_ = h.MyExp2Syn(self.oriT1(0.5))
        self.pre_list.append(syn_)    # AMPA        PC
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # E7
        syn_ = h.MyExp2Syn(self.oriT2(0.5))
        self.pre_list.append(syn_)    # AMPA        PC
        syn_.tau1 = 0.5
        syn_.tau2 = 3
        syn_.e = 0

        # I8
        syn_ = h.MyExp2Syn(self.soma(0.5))
        self.pre_list.append(syn_)    # GABA-A	Neighboring bistratified cell
        syn_.tau1 = 1
        syn_.tau2 = 8
        syn_.e = -75

        # I9
        syn_ = h.MyExp2Syn(self.soma(0.5))
        self.pre_list.append(syn_)    # GABA-A	Basket cell
        syn_.tau1 = 1
        syn_.tau2 = 8
        syn_.e = -75

        # I10
        syn_ = h.MyExp2Syn(self.oriT1(0.6))
        self.pre_list.append(syn_)    # GABA-A	Septum
        syn_.tau1 = 1
        syn_.tau2 = 8
        syn_.e = -75

        # I11
        syn_ = h.MyExp2Syn(self.oriT2(0.6))
        self.pre_list.append(syn_)    # GABA-A	Septum
        syn_.tau1 = 1
        syn_.tau2 = 8
        syn_.e = -75

        # I12
        syn_ = h.MyExp2Syn(self.oriT1(0.6))
        self.pre_list.append(syn_)    # GABA-B	Septum
        syn_.tau1 = 35
        syn_.tau2 = 100
        syn_.e = -75

        # I13
        syn_ = h.MyExp2Syn(self.oriT2(0.6))
        self.pre_list.append(syn_)    # GABA-B	Septum
        syn_.tau1 = 35
        syn_.tau2 = 100
        syn_.e = -75


# -*- coding: utf-8 -*-
"""
Created on Sun Sep 20 20:42:44 2020

@author: User
"""

import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestRegressor
import torch
import torch.nn.functional as F
import pdb
from xgboost import XGBRegressor
import random
import pickle
import shutil

def extract_data_set(dataset):
    X_train, y_train = dataset['train']
    X_valid, y_valid = dataset['valid']
    #X_test, y_test = dataset['test']
    return X_train, y_train, X_valid, y_valid

def score(model, X, y_true):

    if type(X) == torch.Tensor:
        y_pred = model(X).cpu().reshape(-1,)
        y_pred = y_pred.detach().numpy()
        y_true = y_true.cpu().numpy().reshape(-1,)
    else:
        y_pred = model.predict(X).reshape(-1,)
        y_true = y_true.reshape(-1,)

    # mae
    mae = np.mean(np.abs(y_pred-y_true))

    # mape
    non_zero_index = np.where(y_true>0)[0]
    y_pred_non_zero =  y_pred[non_zero_index]
    y_true_non_zero =  y_true[non_zero_index]
    mape = np.mean(np.abs((y_true_non_zero - y_pred_non_zero)/y_true_non_zero))

    return mae, mape

def cv_score(y_pred_list, y_true_list):
    mae_list = []
    mape_list = []
    for i in range(len(y_pred_list)):

        y_pred = y_pred_list[i].reshape(-1,)
        y_true = np.array(y_true_list[i]).reshape(-1,)
        mae = np.mean(np.abs(y_pred-y_true))

        # mape
        non_zero_index = np.where(y_true>0)[0]
        y_pred_non_zero =  y_pred[non_zero_index]
        y_true_non_zero =  y_true[non_zero_index]
        mape = np.mean(np.abs((y_true_non_zero - y_pred_non_zero)/y_true_non_zero))

        mae_list.append(mae)
        mape_list.append(mape)

    #pdb.set_trace()
    mae = np.mean(mae_list)
    mape = np.mean(mape_list)
    min_model_index = mape_list.index(min(mape_list))
    return mae, mape, min_model_index

def randomforest(dataset, config):
    X_train, y_train, X_valid, y_valid = extract_data_set(dataset)

    n_estimators = int(config['params']['n_estimators'])
    max_features = config['params']['max_features']
    if max_features not in ['auto','sqrt','log2']:
        max_features = float(max_features)

    max_depth = int(config['params']['max_depth'])
    min_samples_leaf = int(config['params']['min_samples_leaf'])
    random_state =int(config['params']['random_state'])

    regr = RandomForestRegressor(n_estimators=n_estimators, max_features=max_features, max_depth=max_depth, random_state=random_state, min_samples_leaf=int(len(X_train)*min_samples_leaf))
    regr.fit(X_train, y_train)
    feature_df = pd.DataFrame({'col':x_cols,'importance':np.round(regr.feature_importances_,4)})
    feature_df = feature_df.sort_values('importance', ascending=False).reset_index(drop=True)

    y_true = np.r_[y_valid,y_test]
    y_pred = np.r_[regr.predict(X_valid),regr.predict(X_test)]
    y_pred = np.array([i if i >=0 else 0 for i in y_pred])

    mae = np.mean(np.abs(y_pred-y_true))

    result_dict = {'feature_importance':feature_df,'dataset':dataset,
                   'y_pred':y_red,'y_true':y_true}

    return regr, result_dict

def mape_loss(predt, dtrain) :
    ''' Root mean squared log error metric.'''
    y = dtrain.get_label()
    non_zero_index = np.where(y>0)[0]
    predt = predt[non_zero_index]
    y = y[non_zero_index]

    elements = np.abs((y-predt)/y)
    return 'MAPE', float(np.mean(elements))

def xgboosting(dataset, config, save_path):
    X_train_set, y_train_set, X_valid_set, y_valid_set = extract_data_set(dataset)

    n_estimators = int(config['xg']['n_estimators'])
    max_depth = int(config['xg']['max_depth'])
    lr = float(config['xg']['lr'])
    objective = config['xg']['objective']
    n_jobs = int(config['xg']['n_jobs'])
    gamma = float(config['xg']['gamma'])
    min_child_weight = float(config['xg']['min_child_weight'])
    subsample = float(config['xg']['subsample'])
    colsample_bytree= float(config['xg']['colsample_bytree'])
    reg_lambda = float(config['xg']['reg_lambda'])
    reg_alpha = float(config['xg']['reg_alpha'])
    cv_value = int(config['settings']['cv_value'])

    eval_metric = config['xg']['eval_metric']
    eval_metric = eval_metric.split(',')
    if eval_metric[0] == 'mape':
        eval_metric = mape_loss

    random_state =int(config['xg']['random_state'])
    early_stopping_rounds = int(config['xg']['early_stopping_rounds'])
    param_dist = {'objective':objective, 'n_estimators':n_estimators,
                  'learning_rate':lr,'max_depth':max_depth,
                  'n_jobs':n_jobs,
                  'gamma':gamma, 'min_child_weight':min_child_weight,
                  'subsample':subsample, 'colsample_bytree':colsample_bytree,
                  'reg_lambda':reg_lambda, 'reg_alpha':reg_alpha,
                  'random_state':random_state}

    y_test_pred_list = []
    y_test_true_list = []
    feature_list = []
    train_loss_list = []
    valid_loss_list = []
    min_model_index = 0
    for cv in range(cv_value):
        X_train, y_train, X_valid, y_valid = X_train_set[cv], y_train_set[cv],\
            X_valid_set[cv], y_valid_set[cv]

        #X_test, y_test = X_test_set[0], y_test_set[0]

        model = XGBRegressor(**param_dist)
        if early_stopping_rounds==0:
            model.fit(X_train,y_train,
                      verbose=True)

        else:
            eval_set = [(X_train,y_train),(X_valid, y_valid)]
            model.fit(X_train,y_train,eval_set=eval_set,
                      eval_metric=eval_metric,
                      verbose=True,early_stopping_rounds=early_stopping_rounds)

        feature = model.feature_importances_
        feature_list.append(feature)

        y_test_pred_list.append(model.predict(X_valid))
        y_test_true_list.append(y_valid)

        #pdb.set_trace()
        #pdb.set_trace()
        train_loss = model.evals_result()['validation_0']
        valid_loss = model.evals_result()['validation_1']

        train_loss_list.append(train_loss)
        valid_loss_list.append(valid_loss)

        mae, mape = score(model, X_valid, y_valid)
        print('mae : %.3f, mape : %.3f'%(mae,mape))

        with open(save_path+'model_%s.pkl'%cv,'wb') as f:
            pickle.dump(model, f)
        model.save_model(save_path+'model_%s.json'%(cv))

    if cv_value <=1:
        mae, mape = score(model, X_valid, y_valid)

    else:

        mae, mape, min_model_index = cv_score(y_test_pred_list, y_test_true_list)

    result_dict = {'score':{'mae':mae, 'mape':mape, 'min_model_ind':min_model_index},
                   'feature':feature_list,
                   'pred':{'valid':y_test_pred_list},
                   'loss':{'train':train_loss,
                           'valid':valid_loss}}

    return result_dict

def lightbgm(dataset, config, save_path):
    X_train_set, y_train_set, X_valid_set, y_valid_set, X_test_set, y_test_set = extract_data_set(dataset)

    num_leaves = int(config['light']['num_leaves'])
    min_data_in_leaf = int(config['light']['min_data_in_leaf'])
    max_depth = int(config['light']['max_depth'])
    max_bin = int(config['light']['max_bin'])
    lr = float(config['light']['lr'])
    num_iterations = int(config['light']['num_iterations'])

    lambda_l1 = float(config['light']['lambda_l1'])
    lambda_l2 = float(config['light']['lambda_l2'])

    feature_fraction = float(config['light']['feature_fraction'])
    bagging_fraction = float(config['light']['bagging_fraction'])
    bagging_freq = int(config['light']['bagging_fractionbagging_freq'])

    early_stopping_rounds = int(config['light']['early_stopping_rounds'])
    objective = config['light']['objective']
    metric= config['light']['metric']

    n_jobs = int(config['light']['n_jobs'])
    cv_value = int(config['settings']['cv_value'])

    random_state =int(config['xg']['random_state'])
    early_stopping_rounds = int(config['xg']['early_stopping_rounds'])
    param_dist = {'objective':objective, 'n_estimators':n_estimators,
                  'learning_rate':lr,'max_depth':max_depth,
                  'n_jobs':n_jobs,
                  'min_child_weight':min_child_weight,
                  'max_delta_step':max_delta_step, 'max_delta_step':max_delta_step,
                  'reg_lambda':reg_lambda, 'reg_lambda':reg_lambda,
                  'seed':random_state}

    y_test_pred_list = []
    feature_list = []
    train_loss_list = []
    valid_loss_list = []
    min_model_index = 0
    for cv in range(cv_value):
        X_train, y_train, X_valid, y_valid = X_train_set[cv], y_train_set[cv],\
            X_valid_set[cv], y_valid_set[cv]

        X_test, y_test = X_test_set[0], y_test_set[0]

        model = XGBRegressor(**param_dist)
        if early_stopping_rounds==0:
            model.fit(X_train,y_train,
                      verbose=True)

        else:
            eval_set = [(X_train,y_train),(X_valid, y_valid)]
            model.fit(X_train,y_train,eval_set=eval_set,
                      eval_metric=eval_metric,
                      verbose=True,early_stopping_rounds=early_stopping_rounds)

        feature = model.feature_importances_
        feature_list.append(feature)

        y_test_pred_list.append(model.predict(X_test))

        #pdb.set_trace()
        #pdb.set_trace()
        train_loss = model.evals_result()['validation_0']
        valid_loss = model.evals_result()['validation_1']

        train_loss_list.append(train_loss)
        valid_loss_list.append(valid_loss)

        mae, mape = score(model, X_test, y_test)
        print('mae : %.3f, mape : %.3f'%(mae,mape))

        with open(save_path+'model_%s.pkl'%cv,'wb') as f:
            pickle.dump(model, f)
        model.save_model(save_path+'model_%s.json'%(cv))

    if cv_value <=1:
        mae, mape = score(model, X_test, y_test)

    else:
        mae, mape, min_model_index = cv_score(y_test_pred_list, y_test_set)

    result_dict = {'score':{'mae':mae, 'mape':mape}, 'feature':feature_list,
                   'pred':{'test':y_test_pred_list},
                   'loss':{'train':train_loss,
                           'valid':valid_loss}}

    return result_dict

class Net(torch.nn.Module):
    def __init__(self, input_size, layer_list):

        super(Net, self).__init__()
        self.hidden1 = torch.nn.Linear(input_size, layer_list[0])
        self.hidden2 = torch.nn.Linear(layer_list[0], layer_list[1])
        self.hidden3 = torch.nn.Linear(layer_list[1], layer_list[2])
        self.predict = torch.nn.Linear(layer_list[2], 1)   # output layer

    def forward(self, x):
        x = F.relu(self.hidden1(x))
        x = F.relu(self.hidden2(x))
        x = F.relu(self.hidden3(x))
        x = self.predict(x)
        return x

def change_to_tensor(dataset):

    t_X_train, t_y_train, t_X_valid, t_y_valid = extract_data_set(dataset)
    X_train_list = []
    y_train_list = []
    X_valid_list = []
    y_valid_list = []

    for i in range(len(t_X_train)):

        X_train, y_train = t_X_train[i], t_y_train[i]
        X_valid, y_valid = t_X_valid[i], t_y_valid[i]

        X_train = torch.from_numpy(X_train).float()
        y_train = torch.from_numpy(y_train).float().reshape(-1)
        X_valid = torch.from_numpy(X_valid).float()
        y_valid = torch.from_numpy(y_valid).float().reshape(-1)

        if torch.cuda.is_available():
            cuda = torch.device('cuda')
            X_train = X_train.cuda()
            y_train = y_train.cuda()
            X_valid = X_valid.cuda()
            y_valid = y_valid.cuda()

        X_train_list.append(X_train)
        y_train_list.append(y_train)
        X_valid_list.append(X_valid)
        y_valid_list.append(y_valid)

    return X_train_list, y_train_list, X_valid_list, y_valid_list

def set_seed(random_state):
    torch.manual_seed(random_state)
    torch.backends.cudnn.deterministic = True
    torch.backends.cudnn.benchmark = False
    np.random.seed(random_state)
    random.seed(random_state)
    random.seed(random_state)
    if torch.cuda.is_available():
        torch.cuda.manual_seed(random_state)
        torch.cuda.manual_seed_all(random_state)

def save_checkpoint(state, is_best, save_path, filename='checkpoint.pth.tar', add_name=''):
    torch.save(state, '%s/%s'%(save_path, filename+add_name))
    if is_best:
        shutil.copyfile('%s/%s'%(save_path, filename+add_name),
                        '%s/model_best%s.pth.tar'%(save_path,add_name))

def calculate_mape(y_pred,y_true):

    nonzero_index = y_true.nonzero().view(-1)

    nonzero_y_true = y_true[nonzero_index]
    nonzero_y_pred = y_pred[nonzero_index]
    mape = torch.mean(torch.abs((nonzero_y_true-nonzero_y_pred)/nonzero_y_true)).item()
    return mape

def mlp(dataset, config, save_path):

    epochs = int(config['mlp']['epoch'])
    lr = float(config['mlp']['lr'])
    weight_decay = float(config['mlp']['weight_decay'])

    random_state = int(config['mlp']['random_state'])
    early_stopping_rounds= int(config['mlp']['early_stopping_rounds'])

    eval_metric= config['mlp']['eval_metric']
    cv_value = int(config['settings']['cv_value'])

    layer = config['mlp']['layer']
    layer1,layer2,layer3 = layer.split(',')
    layer1,layer2,layer3 = int(layer1),int(layer2),int(layer3)
    layer_list = [layer1,layer2,layer3]

    set_seed(random_state)

    X_train_list, y_train_list, X_valid_list, y_valid_list = change_to_tensor(dataset)
    #pdb.set_trace()

    train_loss_list = []
    valid_loss_list = []
    mae_list = []
    mape_list = []
    for cv in range(cv_value):
        X_train, y_train = X_train_list[cv], y_train_list[cv]
        X_valid, y_valid = X_valid_list[cv], y_valid_list[cv]

        n,m = X_train_list[0].shape
        model = Net(m,layer_list)
        if torch.cuda.is_available():
            cuda = torch.device('cuda')
            model = model.cuda()
            torch.cuda.device(0)
        criterion = torch.nn.MSELoss()
        """
        #  weight_decay : 값이 커질수록 가중치 값이 작어지게 되고,
        오버피팅 현상을 해소할 수 있지만, weight_decay 값을 너무 크게 하면
        언더피팅 현상이 발생하므로 적당한 값을 사용
        """
        optimizer = torch.optim.Adam(model.parameters(), lr=lr, weight_decay=weight_decay)

        early_count = 0
        min_value = 9999999999999999999999999

        min_epoch = 0
        print('모델링 시작')

        for epoch in range(epochs):

            is_best = False

            model.train()

            optimizer.zero_grad()
            # Forward pass
            y_pred = model(X_train).reshape(-1)
            # Compute Loss
            train_loss = criterion(y_pred.squeeze(), y_train)
            #train_loss_value = y_pred.squeeze() - y_train
            #train_loss_list.append(train_loss)

            train_loss.backward()
            optimizer.step()

            # validation part
            model.eval()
            y_valid_pred = 0
            valid_loss = 0

            with torch.no_grad():
                y_valid_pred = model(X_valid).reshape(-1)
                valid_loss = criterion(y_valid_pred, y_valid)
                #valid_loss_list.append(valid_loss)

            # train mape, valid mape
            train_mape_loss = calculate_mape(y_pred, y_train)
            valid_mape_loss = calculate_mape(y_valid_pred, y_valid)

            # loss
            train_loss_list.append([train_loss.item(),train_mape_loss])
            valid_loss_list.append([valid_loss.item(),valid_mape_loss])

            # earlystopping
            if eval_metric == 'mape':
                target_value = valid_mape_loss
            else:
                target_value = valid_loss.item()

            if min_value > target_value:
                min_value = target_value
                early_count = 0
                min_epoch = epoch+1
                is_best = True

            else:
                early_count+=1

            print('Epoch {}: train loss: {} valid loss: {} | train mape : {} valid mape : {}'.format(epoch, train_loss.item(), valid_loss.item(), train_mape_loss,valid_mape_loss))

            # model_save
            save_checkpoint({
                    'epoch': epoch + 1,
                    'state_dict': model.state_dict(),
                    'best_acc1': min_value,
                    'optimizer' : optimizer.state_dict(),
                },is_best, save_path, add_name=str(cv))

            if early_stopping_rounds !=0 and early_stopping_rounds == early_count:
                print('early stopping. epoch:%s'%(min_epoch))
                break

        # 평가
        model = Net(m,layer_list)
        if torch.cuda.is_available():
            cuda = torch.device('cuda')
            model = model.cuda()
            torch.cuda.device(0)
        criterion = torch.nn.MSELoss()
        checkpoint = torch.load('%s/model_best%s.pth.tar'%(save_path,cv))

        model.load_state_dict(checkpoint['state_dict'])
        optimizer.load_state_dict(checkpoint['optimizer'])

        mae, mape = score(model, X_valid, y_valid)

        mae_list.append(mae)
        mape_list.append(mape)

    f_mae = np.mean(mae_list)
    f_mape = np.mean(mape_list)

    result_dict = {'score':{'mae':f_mae, 'mape':f_mape}}
    result_dict['loss'] = {'train':train_loss, 'valid':valid_loss}

    return result_dict


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap

trees = pd.read_csv("~/downloads/2015_Trees.csv")
column_headers = list(trees.columns.values)
print(column_headers)

def most_populous():
    brooklyn = trees['boroname'] == 'Brooklyn'
    brooklyn_most_populous = trees[brooklyn]['spc_common'].value_counts()
    print("Brooklyn =", brooklyn_most_populous.head(1))

    manhattan = trees['boroname'] == 'Manhattan'
    manhattan_most_populous = trees[manhattan]['spc_common'].value_counts()
    print("Manhattan =", manhattan_most_populous.head(1))

    bronx = trees['boroname'] == 'Bronx'
    bronx_most_populous = trees[bronx]['spc_common'].value_counts()
    print("Bronx =", bronx_most_populous.head(1))

    queens = trees['boroname'] == 'Queens'
    queens_most_populous = trees[queens]['spc_common'].value_counts()
    print("Queens =", queens_most_populous.head(1))

    manhattan = trees['boroname'] == 'Manhattan'
    manhattan_most_populous = trees[manhattan]['spc_common'].value_counts()
    print("Manhattan =", manhattan_most_populous.head(1))

    staten_island = trees['boroname'] == 'Staten Island'
    staten_island_most_populous = trees[staten_island]['spc_common'].value_counts()
    print('Staten Island = ', staten_island_most_populous.head(1))

#most_populous()

def map():
    the_points = trees
    fig = plt.figure()
    themap = Basemap(projection='gall',
                     llcrnrlon=0,  # lower-left corner longitude
                     llcrnrlat=0,  # lower-left corner latitude
                     urcrnrlon=40,  # upper-right corner longitude
                     urcrnrlat=74,  # upper-right corner latitude
                     resolution='l',
                     area_thresh=100000.0,
                     )
    themap.drawcoastlines()
    themap.drawcountries()
    themap.fillcontinents(color='gainsboro')
    themap.drawmapboundary(fill_color='steelblue')

#map()

def map2():
    fig = plt.figure(figsize=(10, 10))
    map = Basemap(projection='gall',
                  # with low resolution,
                  resolution='l',
                  # And threshold 100000
                  area_thresh=100000.0,
                  # Centered at 0,0 (i.e null island)
                  lat_0=4, lon_0=7
                  )

    map.drawcoastlines()
    map.drawcountries()
    map.fillcontinents(color='#888888')
    x, y = map(trees['longitude'].values, trees['latitude'].values)
    map.plot(x, y, 'ro', markersize=6)
    plt.show()

map2()

print("done")


import csv
import scipy.stats as stat
import numpy

demand = []                         # List of demand per year
amountFullfilledInAYear = []        # List of amount of demand/amount sold fulfilled per year
loss = []                           # list of loss per year.
serviceLevel = []                   # percentage of service level per year.

basePeriod = 1                  # Base Period in Days
serviceTargetLevel = 0.99          # Service Target Level for Demand
zNormalValue = zNormalValue = stat.norm.ppf(serviceTargetLevel)

meanInPeriod = 1000
stdDevInPeriod = 200
capacity = 1
numberPeriods = 1
orderToShipping = 1;

totalProductionYear = 1
numberScenarios = 100   # number of years we want to run.

def write_to_file(list_of_demand, list_of_loss, list_of_service_level):
    ofile = open('scenarios.csv', "wb")

    # writing the title of the columns
    row = "Scenarion #, Demand(Year), Production(Year), Sales Loss(Year), %Demand Satisfaction\n"
    ofile.write(row)

    totalProductionInAYear = str(totalProductionYear);
    for x in range(0, numberScenarios):
        row = str(x + 1) + "," + str(list_of_demand[x]) + "," + totalProductionInAYear + "," + str(list_of_loss[x]) + "," + str(list_of_service_level[x]) + "\n"
        ofile.write(row)

def ask_for_inputs():
    global orderToShipping
    orderToShipping = input('Enter order to shipping time: ')
    # serviceTargetLevel = float(input('Enter Service Target Level: '))

# Robust Demand Satisfaction requirements during target lead time give target service level.
def calculate_capacity():
    demand_satisfaction_capacity = (meanInPeriod * orderToShipping / basePeriod) \
                                 + (zNormalValue * numpy.math.sqrt(stdDevInPeriod * orderToShipping / basePeriod))
    return demand_satisfaction_capacity

ask_for_inputs()
capacity = calculate_capacity()
numberPeriods = int(round(365 / orderToShipping))
totalProductionYear = capacity * numberPeriods
meanInPeriod = 1000 * orderToShipping
stdDevInPeriod = 200 * orderToShipping ** (1/2)

print (capacity)

for j in range(numberScenarios): #sample comment

    totalDemandinPeriod = 0
    totalDemandFullfilledInOneYear = 0
    totalLoss = 0

    for i in range(numberPeriods):

        salesLoss = 0
        generatedPeriodDemand = 0

        generatedPeriodDemand = round(numpy.random.normal(meanInPeriod, stdDevInPeriod), 0)

        print("Generated period demand " + str(generatedPeriodDemand))
        if generatedPeriodDemand > capacity:

            salesLoss = generatedPeriodDemand - capacity
            demandFulfilled = capacity
            totalLoss += salesLoss                  # adding the loss for that day to the total loss of the year

        else:
            demandFulfilled = generatedPeriodDemand

        totalDemandinPeriod += generatedPeriodDemand                    # adding the total demand for the year
        totalDemandFullfilledInOneYear += demandFulfilled     # adding the total demand we were able to fulfill.

    demand.append(totalDemandinPeriod)
    amountFullfilledInAYear.append(totalDemandFullfilledInOneYear)
    loss.append(totalLoss)
    serviceLevel.append(totalDemandFullfilledInOneYear / totalDemandinPeriod)

write_to_file(demand, loss, serviceLevel)

# for k in demand:
#     print(k)
#
# for k in sales:
#     print(k)
#
# for k in loss:
#     print(k)
#
# for k in serviceLevel:
#     print(k)


import json
import datetime
import time
import requests
import argparse
import re

from bs4 import BeautifulSoup

CONFIGURATION = None
SESSION = None
TOKEN = None

class Configuration():
    def __init__(self, config_dict):
        valid = True

        self.permission_levels = {
            'Admin': 1,
            'Guest': 0
        }

        self.modem_ip = config_dict.get('modem_ip')

        if not self.modem_ip:
            print(f'Add modem ip to config file: "modem_ip": "[MODEM_IP]"')
            valid = False

        self.password_hash = config_dict.get('password_hash')

        if not self.password_hash:
            print(f'Add password hash to config file: "password_hash": "[PASSWORD_HASH]"')
            valid = False

        self.preferred_celltower = config_dict.get('preferred_celltower')

        if not self.preferred_celltower:
            print(f'Add prefered celltower ID to config file: "preferred_celltower": "[PREFERRED_CELLTOWER_ID]"')
            valid = False
        else:
            self.preferred_celltower = int(self.preferred_celltower)

        self.reboot_window = config_dict.get('reboot_window')

        if self.reboot_window and re.match(r'^\d{1,2}\:\d{1,2}$', self.reboot_window):
            window = self.reboot_window.split(':')

            if window[0] <= window[1]:
                self.reboot_window = window

        self.debug = config_dict.get('debug') or False

        self.auto_reboot = config_dict.get('auto_reboot') or False

        self.check_interval = config_dict.get('check_interval') or 300

        self.max_retries = config_dict.get('max_retries') or 3

        self.valid = valid

def print_current_config():
    global CONFIGURATION

    debug_print('Current configuration:')

    v = vars(CONFIGURATION)

    for key, val in v.items():
        debug_print(f'\t{key} ({type(val)}): {val}')

def get_current_permissions():
    global CONFIGURATION

    url = f'http://{CONFIGURATION.modem_ip}/api/session.json'

    request = SESSION.get(url)

    data = load_json_string(request.text)

    permission = data['session']['userRole']

    return permission

def login():
    global CONFIGURATION
    global TOKEN
    global SESSION

    TOKEN = get_token()

    url = f'http://{CONFIGURATION.modem_ip}/Forms/config?token={TOKEN}&ok_redirect=%2Findex.html&err_redirect=%2Findex.html&session.password={CONFIGURATION.password_hash}'

    _ = SESSION.get(url)

def requires_permission(permission):
    def decorator(func):
        def decorated(*args, **kwargs):
            global CONFIGURATION

            current_permission = get_current_permissions()

            if CONFIGURATION.permission_levels[current_permission] < CONFIGURATION.permission_levels[permission]:
                debug_print('Elevating permissions')

                login()

            return func(*args, **kwargs)

        return decorated

    return decorator

@requires_permission('Admin')
def get_model():
    global CONFIGURATION
    global SESSION

    url = f'http://{CONFIGURATION.modem_ip}/api/model.json'

    request = SESSION.get(url)

    data = load_json_string(request.text)

    return data

@requires_permission('Admin')
def reboot():
    global SESSION
    global CONFIGURATION
    global TOKEN

    url = f'http://{CONFIGURATION.modem_ip}/Forms/config?general.routerReset=1&err_redirect=/error.json&ok_redirect=/success.json&token={TOKEN}&general.shutdown=Restart'

    _ = SESSION.get(url)

def get_celltower_id():
    data = get_model()

    celltower_id = int(data['wwanadv']['cellId'])

    return celltower_id

def is_up():
    global CONFIGURATION
    global SESSION

    url = f'http://{CONFIGURATION.modem_ip}/index.html'

    try:
        _ = SESSION.get(url)
    except requests.exceptions.ConnectionError:
        return False
    except Exception as err:
        c = type(err).__name__
        m = str(err)
        debug_print(f'({c}) {m}')

        return False

    return True

def get_token():
    global CONFIGURATION
    global SESSION

    url = f'http://{CONFIGURATION.modem_ip}/index.html'

    request = SESSION.get(url)

    soup = BeautifulSoup(request.text, features="html.parser")

    token = soup.find_all('input', {'name': 'token'})[0].get('value')

    return token

def load_json_file(filename):
    with open(filename, 'r') as input_file:
        data = json.load(input_file)

        return data

def load_json_string(data):
    data = data.replace('\t', '')
    data = data.replace('\n', '')
    data = data.replace(' ', '')
    if data[0] != '{':
        return json.loads(''.join(['{', data, '}']))
    return json.loads(data)

def create_session():
    session = requests.Session()

    return session

def debug_print(message):
    global CONFIGURATION

    if CONFIGURATION.debug:
        timestamp_print(f'[DEBUG] {message}')

def get_current_hour():
    return datetime.datetime.now().hour

def timestamp_print(message):
    timestamp = datetime.datetime.fromtimestamp(time.time()).strftime('%H:%M:%S.%f')
    print(f'{timestamp} - {message}')

def is_in_range(val, rn):
    return rn[0] <= val <= rn[1]

def has_rebooted():
    debug_print('Checking if modem has rebooted')

    if is_up():
        debug_print('Modem has rebooted')
        return True

    return False

def handle_reboot():
    global CONFIGURATION

    max_retries = CONFIGURATION.max_retries
    current_retries = 1

    # Reboot loop
    while True:
        do_reboot = False
        auto = CONFIGURATION.auto_reboot

        if auto:
            debug_print('Auto reboot is enabled.')
            do_reboot = True
        else:
            debug_print('User chose to reboot.')
            valid = ['y', 'yes']
            answer = input('Do you want to reboot?').strip().lower()

            if answer in valid:
                do_reboot = True
            else:
                debug_print('User chose to not reboot.')
                break

        if do_reboot:
            if auto and CONFIGURATION.reboot_window:
                current_hour = get_current_hour()
                window = CONFIGURATION.reboot_window
                if not is_in_range(current_hour, window):
                    debug_print(f'Outside of auto reboot time window {current_hour} ({window[0]}:{window[1]})')
                    break

                debug_print(f'Rebooting. {current_retries} / {max_retries} attempts.')
                reboot()
            else:
                debug_print('Rebooting.')
                reboot()

            # Wakeup loop
            while not has_rebooted():
                time.sleep(5)

        celltower_id = get_celltower_id()

        if celltower_id == CONFIGURATION.preferred_celltower:
            debug_print('Celltower changed successfully.')
            break
        else:
            current_retries += 1

        if auto and current_retries >= max_retries:
            debug_print('Max retries exceeded')
            break

def run(config_filename):
    global CONFIGURATION
    global SESSION
    global TOKEN

    data = load_json_file(config_filename)

    CONFIGURATION = Configuration(data)

    if not CONFIGURATION.valid:
        return

    if CONFIGURATION.debug:
        print_current_config()

    SESSION = create_session()

    try:
        # Main loop
        while True:
            debug_print('Checking celltower')
            celltower_id = get_celltower_id()

            if celltower_id != CONFIGURATION.preferred_celltower:
                debug_print(f'Celltower has changed to {celltower_id}')

                handle_reboot()

            interval = CONFIGURATION.check_interval

            debug_print(f'Checking again in {interval} seconds')

            time.sleep(interval)

    except KeyboardInterrupt:
        debug_print('User stopped the monitor')
        return

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='LB2120 modem celltower watchdog')
    parser.add_argument('-c','--config', help='Configuration_file', required=True)
    args = vars(parser.parse_args())

    run(args['config'])


import json
from http import HTTPStatus

from .response import Response

class View:
    """
    A class that represents an HTTP application view.
    """

    HTTP_METHODS = ['OPTIONS', 'HEAD', 'GET', 'POST', 'PUT', 'DELETE']

    def __init__(self, context={}):
        self._context = context

    @property
    def allowed_methods(self):
        """
        A list of HTTP methods this view supports.
        """

        allowed_methods = []
        for method in self.HTTP_METHODS:
            handler_name = method.lower()
            if hasattr(self, handler_name):
                allowed_methods.append(method)

        return allowed_methods

    def options(self, request, **kwargs):
        """
        Default implementation of the HTTP OPTIONS method.
        """

        return Response(
            headers={'Allow': ','.join(self.allowed_methods)},
            status_code=HTTPStatus.OK)

    def handle_request(self, request):
        """
        Process a single HTTP request and return the response.
        """

        # check if the view supports the method
        handler_name = request.method.lower()
        if not hasattr(self, handler_name):
            return Response(HTTPStatus.METHOD_NOT_ALLOWED)

        # run the method handler
        handler = getattr(self, handler_name)
        response = handler(request, **self._context)

        # create an empty response if necessary
        if response is None:
            return Response()

        # return Response objects directly
        if isinstance(response, Response):
            return response

        # create the response from a status code
        if isinstance(response, HTTPStatus):
            return Response(response)

        # create the response from a tuple of status code and message
        if isinstance(response, tuple) and len(response) == 2:
            return Response(
                headers={'Content-Type': 'text/plain'},
                status_code=response[0],
                content=response[1])

        # create the response from JSON serialized data
        return Response(
            headers={'Content-Type': 'application/json'},
            status_code=HTTPStatus.OK,
            content=json.dumps(response))


import numpy as np
import cv2

def view_cam(cam_pos=0):
    cap = cv2.VideoCapture(cam_pos)
    font = cv2.FONT_HERSHEY_SIMPLEX

    while(True):
        ret, frame = cap.read()
        if ret:
            cv2.putText(frame,'press q to quit',(10,10), font, .5 ,(255,255,255),2,cv2.LINE_AA)
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            cv2.imshow('gray',gray)
            cv2.imshow('frame',frame)
        if cv2.waitKey(1) == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()

def record(path):
    cap = cv2.VideoCapture(0)
    fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
    out = cv2.VideoWriter(path,fourcc, 20.0, (640,480))
    font = cv2.FONT_HERSHEY_SIMPLEX
    while cap.isOpened():
        ret, frame = cap.read()
        if ret==True:
            out.write(frame)
            cv2.putText(frame,'press q to quit',(10,10), font, .5 ,(255,255,255),2,cv2.LINE_AA)
            cv2.imshow('frame',frame)
            if cv2.waitKey(1) == ord('q'):
                break
        else:
            break
    cap.release()
    out.release()
    cv2.destroyAllWindows()


import binascii
import serial
import time
import RPi.GPIO as GPIO
import qwiic_vl53l1x
import qwiic_tca9548a

sysRunning_flag = True

#LESSON: MUST PUT INTO SAFE MODE BEFORE STOP COMMAND
ser=serial.Serial(port='/dev/ttyUSB0',baudrate=115200)
ser.flushOutput()

# Arm setup
arm=serial.Serial(port='/dev/ttyS0',baudrate=9600,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS,timeout=1)

# TOF sensor setup
mux = qwiic_tca9548a.QwiicTCA9548A()
mux.disable_all()
mux.enable_channels(0)
ToF = qwiic_vl53l1x.QwiicVL53L1X()
ToF.sensor_init()
ToF.sensor_init()
if (ToF.sensor_init() == None):                  # Begin returns 0 on a good init
    print("Sensor front online!\n")
ToF.set_distance_mode(2)
mux.disable_all()
mux.enable_channels(6)
ToF.sensor_init()
if (ToF.sensor_init() == None):                  # Begin returns 0 on a good init
    print("Sensor 6 right online!\n")
ToF.set_distance_mode(2)
mux.disable_all()
mux.enable_channels(7)
ToF.sensor_init()
if (ToF.sensor_init() == None):                  # Begin returns 0 on a good init
    print("Sensor 7 left online!\n")
ToF.set_distance_mode(2)

def stop():
	ser.write(b'\x92\x00\x00\x00\x00')
	time.sleep(.1)

def rotate90(clockwise): # rotate 90 deg
	resetAngle()
	angleRotated = 0 # variable to calculated amount of rotation
	if clockwise == 0: # ccw
		print("Detected, rotate ccw")
		ser.write(b'\x92\x00\x1E\xFF\xE2') # rotate counterclockwise
		while angleRotated < 90:
			# Send commmand to get angle rotation
			ser.write(b'\x8E\x14') # number 20 = \x14 angle
			time.sleep(.2)
			while ser.inWaiting() != 0: # receive message from iRobot, instead of polling maybe use interrupts/callbacks
				response = []
				for i in range(2):
					response.append(hex(ord(ser.read())))
				angleRotated = angleRotated + int(response[1], 16)
				print(angleRotated)
	else: # cw
		print("Detected, rotate cw")
		ser.write(b'\x92\xFF\xE2\x00\x1E') # rotate clockwise
		while angleRotated < 90:
			# Send commmand to get angle rotation
			ser.write(b'\x8E\x14') # number 20 = \x14 angle
			time.sleep(.2)
			while ser.inWaiting() != 0: # receive message from iRobot, instead of polling maybe use interrupts/callbacks
				response = []
				for i in range(2):
					temp = ser.read()
					#print(temp)
					response.append(hex(ord(temp)))
				temp = int(response[1], 16)
				temp = temp ^ 0b11111111
				if temp != 255:
					angleRotated = angleRotated + temp + 1
				print(angleRotated)
	stop()

def forwardShort():
	global stopDisinfection
	startTime = time.time()
	ser.write(b'\x92\x00\x2f\x00\x2f') #wheel speed of 2f
        #print("move forward for some time")

	move = 1 # Variable on whether we should keep moving forward
	while (move):
		if (getBumpSensor() == 1 or getProxSensorAll() == 1): # If detect a table leg, we should stop disinfecting
		    stop()
		    move = 0
		    stopDisinfection = True

		elif (time.time() - startTime > 1.5): # If more than 1.5 sec
			stop()
			move = 0

def backwardShort():
	startTime = time.time()
	ser.write(b'\x92\xFF\xD8\xFF\xD8') # Move wheels backward

	move = 1 # Variable on whether we should keep moving forward

	while (move):
		if (time.time() - startTime > 2): # If more than 2 sec
			stop()
			move = 0

def forwardUntilSensor(): # move forward until outside table based on ToF
	global stopDisinfection
	print("Move forward until outside table based on ToF")

	ser.write(b'\x92\x00\x35\x00\x35') #wheel speed of 8f

	startTime2 = time.time() # Stopwatch to measure time since function first started

	move = 1 # Variable on whether we should keep moving forward
	underTable = 1 # Variable on whether robot is under a table or not
	for i in range(20): # throw first measurements
		getFrontTof()
	while (move):
		if (getBumpSensor() == 1 or getProxSensorAll() == 1): # If detect a table leg, we should stop disinfecting
			stop()
			move = 0
			stopDisinfection = True

		if (underTable == 1): # Table detected
			if getFrontTof() == 0: # If table no longer detected
				if (time.time() - startTime2 < 1): # If robot quit table before 1 second, we should stop disinfecting
					stop()
					move = 0
					stopDisinfection = True
				underTable = 0
				startTime = time.time() # Store time since table first not detected

		else: # No table detected
			if (time.time() - startTime > 2): # Has been out of table for 2sec
				stop()
				move = 0

def lookForLeg(): # Keep moving forward, and stop when leg is detected
	ser.write(b'\x92\x00\x2f\x00\x35') #The different in left and right wheel is because of the drift caused by uneven floor
	print("Looking for leg")
	move = 1 # Variable on whether we should keep moving forward
	while (move):
		if (getBumpSensor() == 1): # If leg is detected, stop
			stop()
			move = 0

		# Use ToF to maintain moving along table. RightToF = 1 and Left ToF = 0
		if (getRightTof() == 0 and getLeftTof() == 0): # If right ToF no longer detects table, slow down right wheel to steer right
			ser.write(b'\x92\x00\x20\x00\x2f')
		elif (getLeftTof() == 1 and getRightTof() == 1): # If left ToF detects table, slow down left wheel to steer left
			ser.write(b'\x92\x00\x2f\x00\x20')
		else:
			ser.write(b'\x92\x00\x2f\x00\x35')

def alignToTable(): # Align robot to table when facing to it using ToF sensor
	print("Align to table")
	global stopDisinfection
	for i in range(20):
		getRightTof()
		getLeftTof()
	move = 1 # Variable on whether we should keep moving forward
	while (move):
		if (getBumpSensor() == 1 or getProxSensorAll() == 1):
			stop()
			move = 0
			stopDisinfection = True
		if (getRightTof() == 1 and getLeftTof() == 1): # If both sensor detects table, stop as table is aligned
			stop()
			move = 0
		elif (getRightTof() == 1 and getLeftTof() == 0): # If right ToF detects table, move only left wheel
			ser.write(b'\x92\x00\x00\x00\x2f')
		elif (getRightTof() == 0 and getLeftTof() == 1): # If left ToF detects table, move only right wheel
			ser.write(b'\x92\x00\x2f\x00\x00')
		else: # no ToF sensor detects table yet
			ser.write(b'\x92\x00\x2f\x00\x2f')

def getFrontTof(): # Measure using forward ToF sensor, return 1 if Roomba is under table
	mux.disable_all()
	mux.enable_channels(0)
	ToF.start_ranging() # Write configuration bytes to initiate measurement
	time.sleep(.005)
	distance = ToF.get_distance() # Get the result of the measurement from the sensor
	time.sleep(.005)
	ToF.stop_ranging()
        #print(distance)
	if (distance < 1300): # may need to be adjusted
		return 1
	else:
		return 0

def getRightTof(): # Measure using right ToF sensor, return 1 if Roomba is under table
	mux.disable_all()
	mux.enable_channels(7)
	ToF.start_ranging() # Write configuration bytes to initiate measurement
	time.sleep(.005)
	distance = ToF.get_distance() # Get the result of the measurement from the sensor
	time.sleep(.005)
	ToF.stop_ranging()
        #print(distance)
	if (distance < 1300): # may need to be adjusted
		return 1
	else:
		return 0

def getLeftTof(): # Measure using left ToF sensor, return 1 if Roomba is under table
	mux.disable_all()
	mux.enable_channels(6)
	ToF.start_ranging() # Write configuration bytes to initiate measurement
	time.sleep(.005)
	distance = ToF.get_distance() # Get the result of the measurement from the sensor
	time.sleep(.005)
	ToF.stop_ranging()
        #print(distance)
	if (distance < 1300): # may need to be adjusted
		return 1
	else:
		return 0

def getProxSensorAll(): # Get all prox sensor reading, return 1 if object detected
	ser.write(b'\x8E\x2D') #45 = 2D all, 48 = \x30 center left, 49 = \x31 center right
	time.sleep(.2)
	while ser.inWaiting() != 0:
		response = []
		response.append(hex(ord(ser.read())))
		if int(response[0], 16) > 0 : # If object detected form one of the sensor
			return 1
		else:
			return 0

def getBumpSensor(): # Get bump and wheeldrop sensor, return 1 if something detected
	ser.write(b'\x8E\x07')
	time.sleep(.2)
	while ser.inWaiting() != 0:
		response = []
		response.append(hex(ord(ser.read())))
		if int(response[0], 16) > 0 : # If bump detected or robot lifted
			return 1
		else:
			return 0

def resetAngle(): # Reset angle calculation
	ser.write(b'\x8E\x14')
	time.sleep(.2)
	while ser.inWaiting() != 0:
		ser.read()

def resetDistance(): # Reset distance calculation
	ser.write(b'\x8E\x13')
	time.sleep(.2)
	while ser.inWaiting() != 0:
		ser.read()

# Set arm positions
def armUp():
	arm.write(b'\x55\x55\x05\x06\x01\x01\x00') # update position to ID number

def armDown():
	arm.write(b'\x55\x55\x05\x06\x02\x01\x00')

def armClean():
	arm.write(b'\x55\x55\x05\x06\x03\x01\x00')

def armRight():
	arm.write(b'\x55\x55\x05\x06\position\x01\x00')

def armStop():
	arm.write(b'\x55\x55\x02\x07')

GPIO.setmode(GPIO.BCM)   #set up GPIO pins

time.sleep(1)
ser.write(b'\x80') #start
print("started")
time.sleep(0.2)
ser.write(b'\x83')#safe mode
time.sleep(0.2)

# robotMode: 1 == wander and look for table, 2 == initial setup when detecting a table, 3 == disinfect table
robotMode = 1 # If True, robot will wander in cleanmode to look for table. if false will disinfect table

stopDisinfection = False # set to True if entire table has been disinfected, verified by bump and prox sensors, or if error is predicted
lastTableDisinfectTime = -10 # Set to -10s ago

try:
	while (sysRunning_flag):
		if robotMode == 1:
		# Set to clean mode
			armDown()
			ser.write(b'\x87') #clean mode
			time.sleep(0.2) # NEED TO MAKE THIOS LONGER TO ALLOW EXIT TABLE
			for i in range(100):
				getFrontTof() # Throw away early measurements
			while (robotMode == 1): # Continue to look for table
				if (getFrontTof() == 1) and (time.time() - lastTableDisinfectTime > 10): # If table detected, go to robotMode 2
					robotMode = 2

		elif robotMode == 2:
			# Set to safe mode
			ser.write(b'\x83')#safe mode
			time.sleep(0.2)
			armUp()
			armClean()

			# STEP 1: Align robot to table using ToF sensor
			backwardShort()
			alignToTable()

			# STEP 2: Rotate cccw
			print("initial ccw")
			rotate90(0) # Rotate ccw after aligning with table

			# STEP 3: Move along table while alinging using ToF sensor, will stop when table leg deteced
			print("move along table")
			lookForLeg() # Move along the table until table leg is detected
			backwardShort()

			# STEP 4: Rotate clockwise to face table again
			print("rotate cw towards table")
			rotate90(1) # rotate 90deg cw

			# STEP 5: Move back a bit to allow alignment in the next step
			backwardShort()

			# Next mode is to disninfect the table
			robotMode = 3

		elif robotMode == 3:
			while (robotMode == 3):
				# STEP 1: alignment to table first before continue
				alignToTable()
				if stopDisinfection == True:
					robotMode = 1
					break

				# STEP 2: Move forward until leaving table, table not detected by front ToF sensor
				forwardUntilSensor() # Should be moving forward until leaving table, but ToF not available yet
				if stopDisinfection == True:
					robotMode = 1
					break

				# STEP 3: Rotate to the next lane clockwise direction
				rotate90(1)
				forwardShort()
				if stopDisinfection == True:
					robotMode = 1
					break
				rotate90(1)

				# STEP 4: alignment to table first before continue (repeat of step 1)
				backwardShort()
				alignToTable()
				if stopDisinfection == True:
					robotMode = 1
					break

				# Step 5: Move forward until leaving table, table not detected by front ToF sensor (repeat of step 2)
				forwardUntilSensor()
				if stopDisinfection == True:
					robotMode = 1
					break

				# Step 6: Rotate to the next lane counter-clockwise direction (reverse direction of step 3)
				rotate90(0)
				forwardShort()
				if stopDisinfection == True:
					robotMode = 1
					break
				rotate90(0)

				# Loop around to align to table again and continue to disinfect table.
				# Disinfection will stop if entire table has been disinfected, verified by bump and prox sensors
			robotMode = 1 # Next state will be robotMode 1 to look for another table
			lastTableDisinfectTime = time.time()
			stopDisinfection = False # Reset stopDisinfection value

except KeyboardInterrupt:
	GPIO.cleanup() # clean up GPIO on CTRL+C exit
    	#safe mode then stop
	ser.write(b'\x83')
	time.sleep(0.2)
	#stop command when we are done working
	ser.write(b'\xAD')
	ser.close()

print("exit")
#safe mode then stop
time.sleep(0.2)
ser.write(b'\x83')#safe mode, must be in safe mode before stopping
time.sleep(0.2)
ser.write(b'\x92\x00\x00\00\00') #wheel speed of 0
time.sleep(0.2)
#stop command when we are done working
ser.write(b'\xAD') #stop
ser.close()
GPIO.cleanup()


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

##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2016 - now Bytebrand Outsourcing AG (<http://www.bytebrand.net>).
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Lesser General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Lesser General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from odoo import api, fields, models, SUPERUSER_ID, _
from dateutil import rrule, parser
import pytz
from datetime import datetime, date, timedelta
import calendar
import math

def migrate(cr, version):
    """
    This migration is made to calculate running time for each active employee and
    write it into last attendance, which has check out. It is important to
    companies that already use Employee Time Clock module.
    """
    cr.execute(
        """UPDATE hr_attendance  SET running = 0.0""")
    env = api.Environment(cr, SUPERUSER_ID, {})

    employee_ids = env['hr.employee'].search([('active', '=', True)])
    for employee_id in employee_ids:
        current_timesheet_id = env['hr_timesheet_sheet.sheet'].search([
            ('employee_id', '=', employee_id.id)], limit=1).sorted(
            key=lambda v: v.date_from)

        if current_timesheet_id:
            start_date = current_timesheet_id.date_from
            end_date = current_timesheet_id.date_to

            contract = env['hr.contract'].search([
                ('employee_id', '=', employee_id.id),
                ('date_start', '<=', current_timesheet_id.date_from),
                '|',
                ('date_end', '>=', current_timesheet_id.date_from),
                ('date_end', '=', False),
                ('state', '!=', 'cancel')])

            if contract:
                resource_calendar_id = contract.resource_calendar_id
            else:
                resource_calendar_id = employee_id.resource_calendar_id

            use_overtime = resource_calendar_id.use_overtime

            previous_month_diff = get_previous_month_diff(cr, employee_id,
                                                          current_timesheet_id)

            current_month_diff = previous_month_diff
            period = {'date_from': start_date,
                      'date_to': end_date
                      }
            dates = list(rrule.rrule(rrule.DAILY,
                                     dtstart=parser.parse(start_date),
                                     until=parser.parse(end_date)))
            work_current_month_diff = 0.0

            for date_line in dates:
                dh = calculate_duty_hours(cr,date_line, period, employee_id)

                worked_hours = 0.0
                bonus_hours = 0.0
                night_shift_hours = 0.0
                for att in current_timesheet_id.attendances_ids:
                    user_tz = pytz.timezone(
                        att.employee_id.user_id.tz or 'UTC')
                    att_name = fields.Datetime.from_string(
                        att.name).replace(
                        tzinfo=pytz.utc).astimezone(user_tz)
                    name = att_name.replace(tzinfo=None)
                    if name.strftime('%Y-%m-%d') == \
                            date_line.strftime('%Y-%m-%d'):
                        worked_hours += att.worked_hours
                        if not att.check_out:
                            tz = pytz.timezone('UTC')
                            t = datetime.now(tz=tz)
                            d = t - att_name
                            worked_hours += (d.total_seconds() / 3600)
                        if att.overtime_change:
                            bonus_hours += att.bonus_worked_hours
                            night_shift_hours += \
                                att.night_shift_worked_hours
                if use_overtime:
                    diff = (worked_hours + bonus_hours) - dh
                else:
                    diff = worked_hours - dh
                current_month_diff += diff
                work_current_month_diff += diff

                if date_line.date() == date.today():
                    today_current_month_diff = current_month_diff
                    last_attendance = env['hr.attendance'].search([
                        ('employee_id', '=', employee_id.id),
                        ('check_out', '!=', False)], limit=1).sorted(
                        key=lambda v: v.check_out)

                    if last_attendance:
                        last_attendance.write({
                            'running': today_current_month_diff,
                        })
                        break

def get_previous_month_diff(cr, employee_id, current_timesheet_id):
    """
    Calculates total hours of previous timesheet.
    :param employee_id: hr.employee object
    :param current_timesheet_id: hr_timesheet_sheet.sheet object
    :return: float
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    total_diff = employee_id.start_time_different
    prev_timesheet_ids = env['hr_timesheet_sheet.sheet'].search(
        [('employee_id', '=', employee_id.id)
         ]).filtered(
        lambda sheet: sheet.date_to < current_timesheet_id.date_from).sorted(
        key=lambda v: v.date_from)
    if prev_timesheet_ids:
        total_diff = prev_timesheet_ids[-1].calculate_diff_hours
    return total_diff

def calculate_duty_hours(cr, date_from, period, employee_id):
    """
    Calculates duty hours for employee on current date.
    :param date_from: datetime string
    :param period: {'date_from': hr_timesheet_sheet.sheet date_from,
                  'date_to': hr_timesheet_sheet.sheet date_to
                  }
    :param employee_id: hr.employee object
    :return: float
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    contract_obj = env['hr.contract']
    calendar_obj = env['resource.calendar']
    duty_hours = 0.0
    contract_ids = contract_obj.search(
        [('employee_id', '=', employee_id.id),
         ('date_start', '<=', date_from), '|',
         ('date_end', '>=', date_from),
         ('date_end', '=', None)])
    for contract in contract_ids:
        ctx = dict(env.context).copy()
        ctx.update(period)
        dh = calendar_obj.get_working_hours_of_date(
            cr=cr,
            uid=env.user.id,
            ids=contract.resource_calendar_id.id,
            start_dt=date_from,
            resource_id=employee_id.id,
            context=ctx)
        leave = count_leaves(cr, date_from, employee_id.id)
        public_holiday = count_public_holiday(cr, date_from)
        if contract.state != 'cancel':
            if leave[1] == 0 and not public_holiday:
                if not dh:
                    dh = 0.00
                duty_hours += dh
            elif public_holiday:
                dh = 0.00
                duty_hours += dh
            else:
                if not public_holiday and leave[1] != 0:
                    duty_hours += dh * (1 - leave[1])
        else:
            dh = 0.00
            duty_hours += dh
    return duty_hours

def take_holiday_status(cr):
    """
    Takes holiday types, which must change duty hours.
    :return: hr.holidays.status objects
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    holiday_status_ids = env['hr.holidays.status'].search(
        [('take_into_attendance', '=', True)])
    return holiday_status_ids

def count_leaves(cr, date_line, employee_id):
    """
    Checks if employee has any leave on current date.
    :param date_line: datetime
    :param employee_id: hr.employee object's id
    :return: list [hr.holidays objects, float]
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    holiday_obj = env['hr.holidays']
    holiday_ids = holiday_obj.search([
        ('employee_id', '=', employee_id),
        ('state', '=', 'validate'),
        ('type', '=', 'remove'),
        ('holiday_status_id', 'in', take_holiday_status(cr).ids),
        ('date_from', '<', str(date_line + timedelta(days=1))),
        ('date_to', '>', str(date_line))])
    number_of_days = 0
    if holiday_ids:
        for holiday_id in holiday_ids:
            date_from = fields.Datetime.from_string(holiday_id.date_from)
            date_to = fields.Datetime.from_string(holiday_id.date_to)

            contracts = env['hr.contract'].search([
                ('employee_id', '=', employee_id),
                ('date_start', '<=', holiday_id.date_from),
                ('state', '!=', 'cancel')])
            if contracts:
                contract = contracts[-1]
                day_of_week = calendar.weekday(date_line.year,
                                               date_line.month,
                                               date_line.day)
                calendar_attendance_ids = \
                    env['resource.calendar.attendance'].search([
                        ('calendar_id', '=',
                         contract.resource_calendar_id.id),
                        ('dayofweek', '=', day_of_week)])

                default_duty_hours = 0
                real_duty_hours = 0
                if calendar_attendance_ids:
                    for calendar_attendance_id in calendar_attendance_ids:
                        default_duty_hours += \
                            calendar_attendance_id.hour_to - \
                            calendar_attendance_id.hour_from
                        temp_duty_hours = calendar_attendance_id.hour_to - \
                                          calendar_attendance_id.hour_from

                        default_date_from = get_timezone_time(
                            cr, calendar_attendance_id.hour_from, date_line)

                        default_date_to = get_timezone_time(
                            cr, calendar_attendance_id.hour_to, date_line)
                        date_from_calc = default_date_from
                        date_to_calc = default_date_to

                        if date_line.date() == date_from.date():
                            if date_from.time() > default_date_from.time():
                                date_from_calc = default_date_from.replace(
                                    hour=date_from.hour,
                                    minute=date_from.minute,
                                    second=date_from.second)

                        if date_line.date() == date_to.date():
                            if date_to.time() < default_date_to.time():
                                date_to_calc = default_date_to.replace(
                                    hour=date_to.hour,
                                    minute=date_to.minute,
                                    second=date_to.second)
                        if date_to_calc < date_from_calc:
                            date_from_calc = date_to_calc
                        real_duty_hours += \
                            (date_to_calc - date_from_calc) \
                            / timedelta(days=temp_duty_hours / 24) \
                            * temp_duty_hours
                    number_of_days += real_duty_hours / default_duty_hours

    return [holiday_ids, number_of_days]

def count_public_holiday(cr, date_from):
    """
    Checks if there is any public holiday on current date.
    :param date_from: datetime string
    :return: hr.holidays.public.line objects
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    public_holidays = []
    model = env['ir.model'].search(
        [('model', '=', 'hr.holidays.public.line')])
    if model:
        holiday_obj = env['hr.holidays.public.line']
        public_holidays = holiday_obj.search(
            [('date', '=', date_from)])
    return public_holidays

def get_timezone_time(cr, time_without_tz, date_line):
    """
    Is used to transform hours/minutes, wrote in database in int/float type, into
    datetime object without timezone info. For example 8.5 -> 2018-08-28 05:30:00
    (including user timezone).
    :param time_without_tz: float
    :return: datetime
    """
    env = api.Environment(cr, SUPERUSER_ID, {})
    fl_part, int_part = math.modf(time_without_tz)
    local_tz = pytz.timezone(env.user.tz or 'UTC')
    default_date_without_tzinfo = datetime.combine(
        date_line.date(), datetime.strptime(
            str(int(int_part)) + ':' + str(int(fl_part * 60)) +
            ':00', '%H:%M:%S').time())
    default_date = local_tz.localize(
        default_date_without_tzinfo).astimezone(
        pytz.utc)
    default_date = default_date.replace(
        tzinfo=None)
    return default_date


import json
import time

from Collections.ProfileInstance import ProfileInstance
from Collections.HardwareStatusInstance import HardwareStatusInstance
from ThreadControls.ThreadCollectionInstance import ThreadCollectionInstance
import ThreadControls.ThreadHelperFunctions
import Collections.ProfileHelperFunctions as ProfileHelperFunctions
import ThreadControls.controlStubs.HelperFuctions.dutyCycleFunctions as dutyCycleFunctions

from Logging.Logging import Logging, insert_into_sql

def get_tvac_status():
    hw = HardwareStatusInstance.getInstance()
    pi = ProfileInstance.getInstance()
    out = {
        "recordData": pi.record_data,
        "OperationalVacuum": hw.operational_vacuum,
        "activeProfile": pi.active_profile,
        "vacuumWanted": pi.vacuum_wanted,
        "currentSetpoint": pi.current_setpoint,
        "inRamp": pi.in_ramp,
        "inHold": pi.in_hold,
        "inPause": False,
        'inCryoPumpRegen': hw.shi_cryopump.is_regen_active(),
        'CryoPressure': hw.pfeiffer_gauges.get_cryopump_pressure(),
        'ChamberPressure': hw.pfeiffer_gauges.get_chamber_pressure(),
        'RoughingPressure': hw.pfeiffer_gauges.get_roughpump_pressure(),
        "VacuumState": hw.vacuum_state,
        }
    if not pi.active_profile:
        out["inRamp"] = None
    return json.dumps(out)

def get_vacuum_state():
    return json.dumps({"VacuumState": HardwareStatusInstance.getInstance().vacuum_state})

def abort_regen_cycle():
    try:
        hw = HardwareStatusInstance.getInstance()
        if hw.shi_cryopump.is_regen_active():
            hw.shi_mcc_cmds.append(['Start_Regen', 0])
            return "{'result':'success'}"
        else:
            return "{'result':'Cryopump not generating so Can't abort regeneration cycle.'}"
    except Exception as e:
        return "{'error':'{}'}".format(e)

def do_regen_cycle():
    try:
        hw = HardwareStatusInstance.getInstance()
        if not hw.shi_cryopump.is_regen_active():
            hw.shi_mcc_cmds.append(['Start_Regen', 1])
            return "{'result':'success'}"
        else:
            return "{'result':'Cryopump not generating so Can't abort regeneration cycle.'}"
    except Exception as e:
        return "{'error':'{}'}".format(e)

def stop_recording_data():
    ProfileInstance.getInstance().record_data = False

def record_data():
    ProfileInstance.getInstance().record_data = True

def run_profile():
    # Does not allow the system to start a profile until the door is closed
    if not HardwareStatusInstance.getInstance().pc_104.digital_in.chamber_closed:
        return "{'error':'{}'}".format("Door is open")

    # If the shroud heaters are on, turn them off when starting a profile
    HardwareStatusInstance.getInstance().tdk_lambda_cmds.append(['Shroud Duty Cycle', 0])
    HardwareStatusInstance.getInstance().tdk_lambda_cmds.append(['Disable Shroud Output'])
    pi = ProfileInstance.getInstance()
    result = ProfileHelperFunctions.run_profile(pi,first_start = True)
    return result

def get_zone_temps():
    pi = ProfileInstance.getInstance()
    temps=dict(ZoneTemps=[])

    for i in range(1,10):
        zone = pi.zone_dict["zone{}".format(i)]
        if zone.active_zone_profile:
            # If it errors out, add a nan
            try:
                temps['ZoneTemps'].append(zone.getTemp())
            except AttributeError as e:
                temps['ZoneTemps'].append(float('nan'))

            try:
                temps['ZoneTemps'].append(zone.pid.SetPoint)
            except AttributeError as e:
                temps['ZoneTemps'].append(float('nan'))
        else:
            temps['ZoneTemps'].append(float('nan'))
            temps['ZoneTemps'].append(float('nan'))
    buff=json.dumps(temps)
    return buff

def get_pressure_gauges():
    gauges = HardwareStatusInstance.getInstance().pfeiffer_gauges
    resp = {'CryoPressure': gauges.get_cryopump_pressure(),
            'ChamberPressure': gauges.get_chamber_pressure(),
            'RoughingPressure': gauges.get_roughpump_pressure()}
    return json.dumps(resp)

def get_pc104_analog():
    pins = HardwareStatusInstance.getInstance().pc_104
    return '{"out":%s,"in":%s}' % (pins.analog_out.getJson(),
                                   pins.analog_in.getJson())

def get_pc104_switches():
    pins = HardwareStatusInstance.getInstance().pc_104
    return '{"in sw":%s,"sw wf":%s}' % (
        pins.digital_in.getJson_Switches(),
        pins.digital_in.getJson_Switches_WF())

def get_pc104_digital():
    pins = HardwareStatusInstance.getInstance().pc_104
    return '{"out":%s,"in bits":%s,"in sw":%s,"sw wf":%s}' % (
        pins.digital_out.getJson(),
        pins.digital_in.getJson_bits(),
        pins.digital_in.getJson_Switches(),
        pins.digital_in.getJson_Switches_WF()
    )

def get_cryopump_plots():
    return HardwareStatusInstance.getInstance().shi_cryopump.get_json_plots()

def get_cryopump_params():
    return HardwareStatusInstance.getInstance().shi_cryopump.getJson_Params()

def get_cryopump_status():
    return HardwareStatusInstance.getInstance().shi_cryopump.getJson_Status()

def get_event_list():
    # data unused
    Logging.debug_print(2, "Calling: Get Event List")
    event_list = ProfileInstance.getInstance().system_status_queue
    temp_event_list = dict(time=[],category=[],message=[])
    for i, event in enumerate(event_list):
        temp_event_list['time'].append(event['time'])
        temp_event_list['category'].append(event['category'])
        temp_event_list['message'].append(event['message'])

        event_list.pop(i)
    Logging.debug_print(2, "Events :" + str(event_list))

    return json.dumps(temp_event_list)

def get_shi_temps():
    return HardwareStatusInstance.getInstance().shi_cryopump.mcc_status.get_json_plots()

def hard_stop():
    hw = HardwareStatusInstance.getInstance()
    pi = ProfileInstance.getInstance()
    try:
        if not pi.active_profile:
            return {'result': '{}'.format("You can not stop a profile while a profile is not running.")}

        Logging.debug_print(1, "Hard stop has been called")
        d_out = hw.pc_104.digital_out
        pi.active_profile = False
        d_out.update({"IR Lamp 1 PWM DC": 0})
        d_out.update({"IR Lamp 2 PWM DC": 0})
        d_out.update({"IR Lamp 3 PWM DC": 0})
        d_out.update({"IR Lamp 4 PWM DC": 0})
        d_out.update({"IR Lamp 5 PWM DC": 0})
        d_out.update({"IR Lamp 6 PWM DC": 0})
        d_out.update({"IR Lamp 7 PWM DC": 0})
        d_out.update({"IR Lamp 8 PWM DC": 0})
        d_out.update({"IR Lamp 9 PWM DC": 0})
        d_out.update({"IR Lamp 10 PWM DC": 0})
        d_out.update({"IR Lamp 11 PWM DC": 0})
        d_out.update({"IR Lamp 12 PWM DC": 0})
        d_out.update({"IR Lamp 13 PWM DC": 0})
        d_out.update({"IR Lamp 14 PWM DC": 0})
        d_out.update({"IR Lamp 15 PWM DC": 0})
        d_out.update({"IR Lamp 16 PWM DC": 0})

        hw.tdk_lambda_cmds.append(['Disable All Output'])
        hw.tdk_lambda_cmds.append(['Platen Duty Cycle', 0])
        hw.tdk_lambda_cmds.append(['Shroud Duty Cycle', 0])
        Logging.logEvent("Event","Profile",
            {"message": "Profile {} Halted:".format(pi.name),
            "ProfileInstance": ProfileInstance.getInstance()})
        dutyCycleFunctions.ending_active_profile(already_logged=True)
        return {'result':'success'}
    except Exception as e:
        return {'result':'{}'.format(e)}

def get_last_error():
    Logging.debug_print(2, "Calling: Get Last Err")  #Todo Change to logEvent()
    error_list = ProfileInstance.getInstance().error_list
    temp_error_list = dict(time=[],event=[],item=[],itemID=[],details=[],actions=[])
    for i, error in enumerate(error_list):
        temp_error_list['time'].append(error['time'])
        temp_error_list['event'].append(error['event'])
        temp_error_list['item'].append(error['item'])
        temp_error_list['itemID'].append(error['itemID'])
        temp_error_list['details'].append(error['details'])
        temp_error_list['actions'].append(error['actions'])

        error_list.pop(i)

    return json.dumps(temp_error_list)

def get_all_zone_data():
    pass
    # TODO: If this doesn't work, why is it here?
    # This doesn't work...
    # Logging.debugPrint(2, "Calling: getAllZoneData")  #Todo Change to logEvent()
    # profile_instance = ProfileInstance.getInstance()
    # zones = profile_instance.zoneProfiles.zoneDict
    # json = "{"
    # for zone in zones:
    #     print(zones[zone].getJson())
    # return "{'result':'success'}"

def stop_roughing_pump():
    try:
        profile = ProfileInstance.getInstance()
        if not profile.active_profile:
            pins = HardwareStatusInstance.getInstance().pc_104.digital_out
            pins.update({'RoughP GateValve': False})
            # wait here until the valve is closed
            time.sleep(5)
            pins.update({'RoughP Pwr Relay': False})
            # TODO: Fix type in PurgeGass (EVERYWHERE)
            pins.update({'RoughP PurgeGass': False})
            return "{'result':'success'}"
        else:
            return "{'result':'Not Changed: Active Profile Running.'}"
    except Exception as e:
        return "{'error':'{}'}".format(e)

def stop_cryopump():
    pi = ProfileInstance.getInstance()
    hw = HardwareStatusInstance.getInstance()
    try:
        if not pi.active_profile:
            ProfileHelperFunctions.set_vacuum_wanted(pi, False)

            hw.pc_104.digital_out.update({'CryoP GateValve': False})

            time.sleep(5)
            # TODO: Wait until gate is closed
            hw.shi_mcc_cmds.append(['Turn_CryoPumpOff'])
            hw.shi_compressor_cmds.append('off')
            return "{'result':'success'}"
        else:
            return "{'result':'Not Changed: Active Profile Running.'}"
    except Exception as e:
        Logging.debug_print(1, "Error in ThreadCollection, holdThread: {}".format(str(e)))
        return "{'error':'{}'}".format(e)

def stop_cryopumping_chamber():
    pi = ProfileInstance.getInstance()
    hw = HardwareStatusInstance.getInstance()
    try:
        if not pi.active_profile:
            ProfileHelperFunctions.set_vacuum_wanted(pi, False)
            hw.pc_104.digital_out.update({'CryoP GateValve': False})

            return "{'result':'success'}"
        else:
            return "{'result':'Not Changed: Active Profile Running.'}"
    except Exception as e:
        Logging.debug_print(1, "Error in ThreadCollection, holdThread: {}".format(str(e)))
        return "{'error':'{}'}".format(e)

def vacuum_not_needed():
    pi = ProfileInstance.getInstance()
    try:
        if not pi.active_profile:
            ProfileHelperFunctions.set_vacuum_wanted(pi, False)
            return "{'result':'success'}"
        else:
            return "{'result':'Not Changed: Active Profile Running.'}"
    except Exception as e:
        Logging.debug_print(1, "Error in ThreadCollection, holdThread: {}".format(str(e)))
        return "{'error':'{}'}".format(e)

def un_hold_all_zones():
    try:

        ThreadControls.ThreadHelperFunctions.release_hold()
    except Exception as e:
        return "{'error':'{}'}".format(e)
    return "{'result':'success'}"

def resume_all_zones():
    try:
        thread_instance = ThreadCollectionInstance.getInstance()
        thread_instance.threadCollection.holdThread()
        return "{'result':'success'}"
    except Exception as e:
        return "{'error':'{}'}".format(e)

def hold_all_zones():
    try:
        thread_instance = ThreadCollectionInstance.getInstance()
        thread_instance.threadCollection.holdThread()
        return "{'result':'success'}"
    except Exception as e:
        return "{'error':'{}'}".format(e)

def get_all_thermocouple_data():
    Logging.debug_print(2, "Calling: getAllThermoCoupleData")
    hardware_status_instance = HardwareStatusInstance.getInstance()
    tc_json = hardware_status_instance.thermocouples.getJson('K')
    return tc_json

def put_under_vacuum():
    pi = ProfileInstance.getInstance()
    hw = HardwareStatusInstance.getInstance()
    if hw.pc_104.digital_in.chamber_closed:
        try:
            ProfileHelperFunctions.set_vacuum_wanted(pi, True)
            return "{'result':'success'}"
        except Exception as e:
            Logging.debug_print(1, "Error in ThreadCollection, holdThread: {}".format(str(e)))
            return "{'error':'{}'}".format(e)
    else:
        return "{'result':'Error: Door is open'}"

def chamber_door_status():
    try:
        chamber_closed = HardwareStatusInstance.getInstance().pc_104.digital_in.chamber_closed

        return "{'result':'" + str(chamber_closed) + "'}"
    except Exception as e:
        return "{'error':'{}'}".format(e)

def get_system_power():
    message = {
        "pfeiffer_gauge": HardwareStatusInstance.getInstance().pfeiffer_gauge_power,
        "shi_compressor": HardwareStatusInstance.getInstance().shi_compressor_power,
        "shi_mcc": HardwareStatusInstance.getInstance().shi_mcc_power,
        "tdk_lambda": HardwareStatusInstance.getInstance().tdk_lambda_power,
        "thermocouple": HardwareStatusInstance.getInstance().thermocouple_power,
        "pc_104": HardwareStatusInstance.getInstance().pc_104_power,
        }

    return json.dumps(message)

def get_interlock_status():

    message = {
        "door_interlock":  HardwareStatusInstance.getInstance().pc_104.digital_in.chamber_closed ,
        "overTemp" : not HardwareStatusInstance.getInstance().overheated_tc,
        "overPressure": HardwareStatusInstance.getInstance().operational_vacuum,
        "roughing_pump_valve_open": HardwareStatusInstance.getInstance().pc_104.digital_out.getVal('RoughP GateValve'),
        "roughing_pump_valve_closed": not HardwareStatusInstance.getInstance().pc_104.digital_out.getVal('RoughP GateValve'),
        "thermocouple": HardwareStatusInstance.getInstance().thermocouple_power,
        "pfeiffer_gauge": HardwareStatusInstance.getInstance().pfeiffer_gauge_power,
        "ln2_shroud_open": HardwareStatusInstance.getInstance().pc_104.digital_in.getVal('LN2_S_Sol_Open'),
        "ln2_shroud_closed": HardwareStatusInstance.getInstance().pc_104.digital_in.getVal('LN2_S_Sol_Closed'),
        "ln2_platen_open": HardwareStatusInstance.getInstance().pc_104.digital_in.getVal('LN2_P_Sol_Open'),
        "ln2_platen_closed": HardwareStatusInstance.getInstance().pc_104.digital_in.getVal('LN2_P_Sol_Closed'),
        "cryopump_valve_open": HardwareStatusInstance.getInstance().pc_104.digital_in.getVal('CryoP_GV_Open'),
        "cryopump_valve_closed": HardwareStatusInstance.getInstance().pc_104.digital_in.getVal('CryoP_GV_Closed'),
        "tdk_lambda": HardwareStatusInstance.getInstance().tdk_lambda_power,
        "pc_104": HardwareStatusInstance.getInstance().pc_104_power,
        "shi_compressor": HardwareStatusInstance.getInstance().shi_compressor_power,
        "shi_mcc": HardwareStatusInstance.getInstance().shi_mcc_power,
    }
    return json.dumps(message)

def get_sql_data():
    sql_list = HardwareStatusInstance.getInstance().sql_list[:]
    HardwareStatusInstance.getInstance().sql_list = []
    return {"sql_data":sql_list}


from top_config import *
from ROOT import *
from Jawa import *

selection = TCut("mu_cpt_0.50 < 5000 && e_cpt_0.50 < 5000 && mu_IP_OWNPV < 0.04 && e_IP_OWNPV < 0.04 && min(mu_ETA,e_ETA) > 2.0 && max(mu_ETA, e_ETA) < 4.5 && mu_PT > 20000 && e_PT > 20000")
selection_antiiso = TCut("mu_cpt_0.50 > 2000 && e_cpt_0.50 > 2000 && mu_IP_OWNPV < 0.04 && e_IP_OWNPV < 0.04")

#zmumu_selection = TCut("muplus_cpt_0.50 < 2000 && muminus_cpt_0.50 < 2000 && muplus_IP_OWNPV < 0.04 && muminus_IP_OWNPV < 0.04")
zmumu_selection = TCut('boson_M > 60000 && boson_M < 120000 && muplus_PT > 20000 && muminus_PT > 20000 && min(muplus_ETA, muminus_ETA) > 2.0 && max(muplus_ETA, muminus_ETA) < 4.5')

mupem_fwdasy = TCut("mu_ID == -13 && mu_ETA > e_ETA")
mupem_bwdasy = TCut("mu_ID == -13 && mu_ETA < e_ETA")

mumep_fwdasy = TCut("mu_ID == 13 && mu_ETA < e_ETA")
mumep_bwdasy = TCut("mu_ID == 13 && mu_ETA > e_ETA")

jet_notag       = TCut("ttbar_jet_PT > 20000")
zmumu_jet_notag = TCut("boson_jet_PT > 20000")

jet       = TCut("ttbar_jet_PT > 20000 && ttbar_jet_BDTTag == 1")
zmumu_jet = TCut("boson_jet_PT > 20000 && boson_jet_BDTTag == 1")

jet_notag       = TCut("ttbar_jet_PT > 20000")
zmumu_jet_notag = TCut("boson_jet_PT > 20000")

pi = '3.14159265959'
dPhi  = "abs(<A>_PHI - <B>_PHI) + (abs(<A>_PHI - <B>_PHI) > <pi>)*2*(<pi> - abs(<A>_PHI - <B>_PHI) )".replace('<pi>', pi)

vars = [
    ["mu_pt"        , "mu_PT/1000.0"           , 10  , 20 , 100 ],
    ["e_pt"         , "e_PT/1000.0"            , 10  , 20 , 100 ],
    ["mu_eta"       , "mu_ETA"     , 10, 2, 4.5],
    ["e_eta"        , "e_ETA"      , 10, 2, 4.5],
    ["ptj"          , "ttbar_jet_PT/1000"       , 15, 20, 170],
    ['dphi'         , dPhi.replace('<A>', 'mu').replace('<B>', 'e'), 10, 0, TMath.Pi()],
    ["etaj"         , "ttbar_jet_ETA"   , 10, 2.2, 4.2 ]
    ]

zvars = [
    ["m"            , "boson_M/1000.0",            50, 60,120],
    ["mup_pt"       , "muplus_PT/1000.0"           , 10  , 20 , 100 ],
    ["mum_pt"       , "muminus_PT/1000.0"            , 10  , 20 , 100 ],
    ["mup_eta"      , "muplus_ETA"     , 10, 2, 4.5],
    ["mum_eta"      , "muminus_ETA"      , 10, 2, 4.5],
    ['dphi'         , dPhi.replace('<A>', 'muplus').replace('<B>', 'muminus'), 10, 0, TMath.Pi()],
    ["ptj"          , "boson_jet_PT/1000"       , 15, 20, 170],
    ["etaj"         , "boson_jet_ETA"   , 10, 2.2, 4.2 ]
    ]

zmumuj_data = Template("zmumuj_data")
zmumuj_data.SetSelCut(ztrigger + zmumu_selection + zmumu_jet)
zmumuj_data.AddTrees(zmumuj_2016.trees())
zmumuj_data.AddTrees(zmumuj_2015.trees())
zmumuj_data.AddVars(zvars)
zmumuj_data.Run()
zmumuj_data.SaveToFile("/user2/sfarry/workspaces/top/tuples/zmumuj_data.root")

zmumu_xsec = 1.693e-6*1e9 # in pb
zmumu_acc  = 10000.0/26298.0
zmumu_evts = zmumu_mc2016.MU.Get("TotEvts").GetVal() + zmumu_mc2016.MD.Get("TotEvts").GetVal()
zmumu_sf   = zmumu_xsec * zmumu_acc / zmumu_evts

zmumuj_xsec = 2.263e-6*1e9 # in pb
zmumuj_acc  = 10000.0/30454.0
zmumuj_evts = zmumuj_mc2016.MU.Get("TotEvts").GetVal() + zmumuj_mc2016.MD.Get("TotEvts").GetVal()
zmumuj_sf   = zmumuj_xsec * zmumuj_acc / zmumuj_evts

ztautau_xsec = 1.689e-6*1e9 # in pb
ztautau_acc  = 10000.0/26554.0
ztautau_evts = ztautau_mc2016.MU.Get("TotEvts").GetVal() + ztautau_mc2016.MD.Get("TotEvts").GetVal()
ztautau_sf   = ztautau_xsec * ztautau_acc / ztautau_evts

ztautauj_xsec = 2.260e-6*1e9 # in pb
ztautauj_acc  = 10000.0/26428.0
ztautauj_evts = ztautauj_mc2016.MU.Get("TotEvts").GetVal() + ztautauj_mc2016.MD.Get("TotEvts").GetVal()
ztautauj_sf   = ztautauj_xsec * ztautauj_acc / ztautauj_evts

zmumu_mc2016_t = Template("zmumu_mc2016")
zmumu_mc2016_t.SetSelCut(zmumu_selection)
zmumu_mc2016_t.AddTrees(zmumu_mc2016.trees())
zmumu_mc2016_t.AddVars(zvars)
zmumu_mc2016_t.Run()
zmumu_mc2016_t.Scale(zmumu_sf)
zmumu_mc2016_t.SaveToFile("/user2/sfarry/workspaces/top/tuples/zmumu_mc2016.root")

ztautau_mc2016_t = Template("ztautau_mc2016")
ztautau_mc2016_t.SetSelCut(trigger + selection)
ztautau_mc2016_t.AddTrees(ztautau_mc2016.trees())
ztautau_mc2016_t.AddVars(vars)
ztautau_mc2016_t.Run()
ztautau_mc2016_t.Scale(ztautau_sf)
ztautau_mc2016_t.SaveToFile("/user2/sfarry/workspaces/top/tuples/ztautau_mc2016.root")

zmumuj_mc2016_t = Template("zmumuj_mc2016")
zmumuj_mc2016_t.SetSelCut(zmumu_selection + zmumu_jet_notag)
zmumuj_mc2016_t.AddTrees(zmumuj_mc2016.trees())
zmumuj_mc2016_t.AddVars(zvars)
zmumuj_mc2016_t.Run()
zmumuj_mc2016_t.Scale(zmumuj_sf)
zmumuj_mc2016_t.SaveToFile("/user2/sfarry/workspaces/top/tuples/zmumuj_mc2016.root")

ztautauj_mc2016_t = Template("ztautauj_mc2016")
ztautauj_mc2016_t.SetSelCut(trigger + selection + jet_notag)
ztautauj_mc2016_t.AddTrees(ztautau_mc2016.trees())
ztautauj_mc2016_t.AddVars(vars)
ztautauj_mc2016_t.Run()
ztautauj_mc2016_t.Scale(ztautauj_sf)
ztautauj_mc2016_t.SaveToFile("/user2/sfarry/workspaces/top/tuples/ztautauj_mc2016.root")

zmumuj_mc2016_tag_t = Template("zmumuj_mc2016_tag")
zmumuj_mc2016_tag_t.SetSelCut(zmumu_selection + zmumu_jet)
zmumuj_mc2016_tag_t.AddTrees(zmumuj_mc2016.trees())
zmumuj_mc2016_tag_t.AddVars(zvars)
zmumuj_mc2016_tag_t.Run()
zmumuj_mc2016_tag_t.Scale(zmumuj_sf)
zmumuj_mc2016_tag_t.SaveToFile("/user2/sfarry/workspaces/top/tuples/zmumuj_mc2016_tag.root")

ztautauj_mc2016_tag_t = Template("ztautauj_mc2016_tag")
ztautauj_mc2016_tag_t.SetSelCut(trigger + selection + jet )
ztautauj_mc2016_tag_t.AddTrees(ztautau_mc2016.trees())
ztautauj_mc2016_tag_t.AddVars(vars)
ztautauj_mc2016_tag_t.Run()
ztautauj_mc2016_tag_t.Scale(ztautauj_sf)
ztautauj_mc2016_tag_t.SaveToFile("/user2/sfarry/workspaces/top/tuples/ztautauj_tag_mc2016.root")

Nztautauj = zmumuj_data.GetEvts() * (ztautauj_mc2016_t.GetNormEvts() / zmumuj_mc2016_t.GetNormEvts())
Nztautauj_mc = ztautauj_mc2016_t.GetNormEvts()


import subprocess
import tempfile
import sys
from beat import playHeartBeat
from arduino import myHeartBeat
from threading import Thread
import datetime

import socket
import threading

#otherBeat = 68
otherDevice = "stormy-fortress-18687"

def playBeat():
    while True:
        playHeartBeat(68)

HOST = '138.16.161.123'
PORT = 65432

def connect():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, PORT))
        s.listen()
        th=[]
#         conn, addr = s.accept()

#         with conn:
    #         print('Connected by', addr)

        while True:
            conn, addr = s.accept()

            # send over heartrate here
            th.append(Thread(target=listener, args = (conn,addr)).start())

            data = conn.recv(1024)
            if not data:
                continue
            #conn.sendall(b'server says hi')

            otherBeat = data.decode('utf-8')
            otherBeat.split(" ")
#             print('other beat: ' + otherBeat)
            if otherBeat != '':
                otherBeat = int(otherBeat)

                if otherBeat > 0:
                    playHeartBeat(otherBeat)

        s.close()

#                 if not data:
#                     break

def listener(client, address):
    while True:
#         data = client.recv(1024)

#         if not data:
#             break

        beat = str(myHeartBeat())
#         print('my beat: ' + beat)
        client.send(beat.encode())

connect()
#playBeat()
# x= threading.Thread(target=playBeat)
# x.start()
# y = threading.Thread(target=connect)
# y.start()


DEAL_TYPES = [
    ('partnership','partnership'),
    ('syndicated', 'syndicated'),
    ('managed_account', 'managed account'),
    ('signle owner', 'single owner')
    ]

BLDG_TYPES  = [
    ("Industrial", "Industrial"),
    ("Retail", "Retail"),
    ("Mixed Use", "Mixed Use"),
    ("Multi-family", "Multi-family") ,
    ("Other", "Other")
]

PURCH_STATUS = [
    ("LOI", "Letter of Intent"),
    ('Contract', 'Contract'),
    ('Owned', "Owned"),
    ('Sell Contract', "Sell Contract"),
    ("Sold", "Sold")
]

MORT_TYPE = [
    ("No Loan", "No Loan"),
    ("Fixed", "Fixed"),
    ('Variable', 'Variable'),
]

XACTION = [
    ('Capital Contribution', 'Capital Contribution'),
    ('Return of Equity', 'Return of Equity'),
    ('Distribution', 'Distribution'),
    ('Management Fee', 'Management Fee')
]

CAP_GROUPS = [
    ('Improvements', 'Improvements'),
    ('Reserves', 'Reserves'),
]


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

from corelib.store import (Document, StringField, DateTimeField, ListField, IntField,
        DecimalField, BooleanField, SequenceField)

from category.consts import TYPE_RECOMMEND
from category.exceptions import CategoryNotExsit

class Category(Document):

    title = StringField()
    name = StringField()
    loc_id = IntField()
    description = StringField()
    parent_name = StringField()
    create_at = DateTimeField()
    type = IntField()
    version = IntField()
    seq_id = SequenceField(required=True)
    top = BooleanField()
    active = BooleanField()

    meta = {
        'indexes': [
            {
                'fields': ['name'],
                'unique': True,
            },
            {
                'fields': ['title'],
                'unique': True,
            },
        ],
    }

    def __unicode__(self):
        return '%s, %s' % ('version: %s' % self.version, ','.join([self.title, self.name]))

    @classmethod
    def get_all(cls):
        categories = cls.objects().order_by('id')
        # categories = cls.objects(parent_name__ne=None).order_by('id')
        return categories

    @classmethod
    def get_sorted(cls):
        categories = cls.objects(loc_id__ne=None).order_by('loc_id').only('title', 'name')
        return categories

    @classmethod
    def get_categories_tree(cls):
        parent_categories = cls.objects(parent_name=None).order_by('id')
        cates = []
        for pc in parent_categories:
            ccs = cls.objects(parent_name=pc.name)
            if ccs:
                ccs = ccs.order_by('id')
            cates.append((pc, ccs))
        return cates

    @classmethod
    def get_recommend_cates(cls):
        categories = cls.objects(parent_name=None, type=TYPE_RECOMMEND).order_by('id')
        return categories

    def get_subcategories(self):
        if self.parent_name:
            return []
        return Category.objects(parent_name=self.name).order_by('id')

    @classmethod
    def get_by_name(cls, name):
        try:
            category = cls.objects.get(name=name)
        except cls.DoesNotExist:
            raise CategoryNotExsit
        return category