from model.model import LeNet, AlexNet, AlexNet_CBAM, New_AlexNet_CBAM
import torch
from torchvision.datasets import mnist
from tqdm import tqdm
from torch.utils.data import DataLoader
from torchvision.transforms import ToTensor
from torch.nn import CrossEntropyLoss
import json

def test(test_dataloader, model, criterion):                # 测试每个epoch
    print("testing:")
    model.eval()                                            # 测试时候需要 model.eval()
    total_correct = 0
    total_num = 0
    total_loss = 0.0
    iters = 0
    with torch.no_grad():                                   # 测试时候不记录梯度,节省算力，加快速度
        for data in tqdm(test_dataloader):                  # 下面就跟训练是一样了
            imgs, labels = data
            bs = imgs.shape[0]

            outputs = model(imgs)
            loss = criterion(outputs, labels)
            _, predicted = torch.max(outputs.data, 1)
            total_correct += (predicted == labels).sum().item()
            total_num += bs
            total_loss += loss.item()
            iters += 1

        test_acc = total_correct/total_num
        test_loss = total_loss/iters

        return test_acc, test_loss

if __name__ == '__main__':
    batch_size = 128  # 一个一个测

    train_dataset = mnist.MNIST(root='./DataSet', train=True, transform=ToTensor(),download=True)
    test_dataset = mnist.MNIST(root='./DataSet', train=False, transform=ToTensor(),download=True)
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False,drop_last=False)
    test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False,drop_last=False)
    cross_error = CrossEntropyLoss()

    model_name = 'alexnet'

    model = AlexNet()
    # if model_name == 'lenet':
    #     model = LeNet()
    # elif model_name == 'alexnet':
    #     model = AlexNet()
    # elif model_name == 'alexnet_cbam':
    #     model = AlexNet_CBAM()
    # elif model_name == 'new_alexnet_cbam':
    #     model = New_AlexNet_CBAM()

    model.load_state_dict(torch.load(f'model/{model_name}.pth'))

    alexnet_train_acc, train_loss = test(train_loader, model, cross_error)
    alexnet_train_loss, test_loss = test(test_loader, model, cross_error)       # 测试
    # ---------------------------------------------------------------------------------------------
    model_name = 'alexnet_cbam'

    model = AlexNet_CBAM()

    model.load_state_dict(torch.load(f'model/{model_name}.pth'))

    alexnet_cbam_train_acc, train_loss = test(train_loader, model, cross_error)
    alexnet_cbam_train_loss, test_loss = test(test_loader, model, cross_error)       # 测试

    # ---------------------------------------------------------------------------------------------
    model_name = 'alexnet_cbam'

    model = AlexNet_CBAM()

    model.load_state_dict(torch.load(f'model/{model_name}.pth'))

    new_alexnet_cbam_train_acc, train_loss = test(train_loader, model, cross_error)
    new_alexnet_cbam_train_loss, test_loss = test(test_loader, model, cross_error)       # 测试

    train_json = {
        "alexnet_train_acc" : alexnet_train_acc * 100,
        "alexnet_train_loss" : alexnet_train_loss * 100,
        "alexnet_cbam_train_acc" : alexnet_cbam_train_acc * 100,
        "alexnet_cbam_train_loss" : alexnet_cbam_train_loss * 100,
        "new_alexnet_cbam_train_acc" : new_alexnet_cbam_train_acc * 100,
        "new_alexnet_cbam_train_loss" : new_alexnet_cbam_train_loss * 100
    }
    with open("train.json", "w") as json_file:
        json.dump(train_json, json_file)

    print(f'【{model_name}】：',end='\n')
    # print(f'train acc: {train_acc}, train loss: {train_loss}')
    # print(f'test acc: {test_acc}, test loss: {test_loss}')


import unittest
import sys
from datetime import datetime
from indicators.queries import IPTTIndicator
from indicators.models import Indicator
from factories.indicators_models import (
    IndicatorFactory,
    LevelFactory,
    PeriodicTargetFactory,
    ResultFactory
    )
from factories.workflow_models import ProgramFactory
from django import test
from django.core.exceptions import ValidationError

from django.conf import settings
from django.db import connection

""" cases
    - cumulative number +
        - lop target: last target
        - lop result: sum of all data
        - period result: sum of data to date
    - cumulative number - (this one might change) (flip bad and good)
        - same as above
    - cumulative percent +
        - lop target: last target
        - lop result: last result
        - period result: last result
    - cumulative percent -
        - same as above (flip bad and good)
    - noncumulative number +
        - lop target: sum of targets
        - lop result: sum of all data
        - period result: sum of data in period
    - noncumulative number -
        - same as above (flip bad and good)
    - noncumulative percent + : no results
    - noncumulative precent - : no results
"""

dates = {
    'program_start': (1, 1),
    'program_end': (12, 31),
    'targets': [
        ((1, 1), (4, 30)),
        ((5, 1), (8, 31)),
        ((9, 1), (12, 31))
    ],
    'collects': [
        ((2, 1),),
        ((6, 1), (8, 10)),
        ((10, 20),)
    ]
}

def get_date(date_tuple):
    return datetime(2016, date_tuple[0], date_tuple[1])

scenarios = [
    {
        'desc': 'cumulative number positive',
        'cumulative': True,
        'number': True,
        'positive': True,
        'targets': [50, 100, 150],
        'data': [(40,), (30, 30), (45,)],
        'blank': False,
        'lop_target': 150,
        'lop_sum': 145,
        'lop_met': '97%',
        'results': [40, 100, 145],
        'mets': ['80%', '100%', '97%'],
        'over_under': [-1, 0, 0],
        'semi_annual_results': [70, 145],
    },
    {
        'desc': 'cumulative number negative',
        'cumulative': True,
        'number': True,
        'positive': False,
        'targets': [300, 200, 100],
        'data': [(100,), (50, 80), (40,)],
        'blank': False,
        'lop_target': 100,
        'lop_sum': 270,
        'lop_met': '270%',
        'results': [100, 230, 270],
        'mets': ['33%', '115%', '270%'],
        'over_under': [1, 0, -1],
        'semi_annual_results': [150, 270]
    },
    {
        'desc': 'cumulative percent positive',
        'cumulative': True,
        'number': False,
        'positive': True,
        'targets': [50, 60, 70],
        'data': [(40,), (50, 55,), (75,)],
        'blank': False,
        'lop_target': 70,
        'lop_sum': 75,
        'lop_met': '107%',
        'results': [40, 55, 75],
        'mets': ['80%', '92%', '107%'],
        'over_under': [-1, 0, 0],
        'semi_annual_results': [50, 75]
    },
    {
        'desc': 'cumulative percent negative',
        'cumulative': True,
        'number': False,
        'positive': False,
        'targets': [80, 40, 20],
        'data': [(75,), (35, 55), (25,)],
        'blank': False,
        'lop_target': 20,
        'lop_sum': 25,
        'lop_met': '125%',
        'results': [75, 55, 25],
        'mets': ['94%', '138%', '125%'],
        'over_under': [0, -1, -1],
        'semi_annual_results': [35, 25]
    },
    {
        'desc': 'noncumulative number positive',
        'cumulative': False,
        'number': True,
        'positive': True,
        'targets': [100, 100, 100],
        'data': [(80,), (40, 70), (90,)],
        'blank': False,
        'lop_target': 300,
        'lop_sum': 280,
        'lop_met': '93%',
        'results': [80, 110, 90],
        'mets': ['80%', '110%', '90%'],
        'over_under': [-1, 0, 0],
        'semi_annual_results': [120, 160]
    },
    {
        'desc': 'noncumulative number negative',
        'cumulative': False,
        'number': True,
        'positive': False,
        'targets': [500, 500, 500],
        'data': [(400,), (300, 200), (600,)],
        'blank': False,
        'lop_target': 1500,
        'lop_sum': 1500,
        'lop_met': '100%',
        'results': [400, 500, 600],
        'mets': ['80%', '100%', '120%'],
        'over_under': [1, 0, -1],
        'semi_annual_results': [700, 800]
    },
    {
        'desc': 'noncumulative percent positive',
        'cumulative': False,
        'number': False,
        'positive': True,
        'targets': [30, 30, 30],
        'data': [(30,), (20,), (50,)],
        'blank': True
    },
    {
        'desc': 'noncumulative percent negative',
        'cumulative': False,
        'number': False,
        'positive': False,
        'targets': [30, 30, 30],
        'data': [(30,), (20,), (50,)],
        'blank': True
    }
]

class TestIndicatorInstance(test.TestCase):

    def setUp(self):
        self.level = LevelFactory()
        self.indicator = IndicatorFactory(name="testname", level=self.level)

    def tearDown(self):
        self.indicator.delete()
        self.level.delete()

    def test_instances(self):
        indicators = IPTTIndicator.timeperiods.all()
        self.assertEqual(len(indicators), 1)
        self.assertEqual(indicators[0].name, self.indicator.name)

    def test_level(self):
        indicator = IPTTIndicator.timeperiods.first()
        self.assertEqual(indicator.levelname, self.level.name)

@unittest.skip('needs updating')
class TestIndicatorScenarios(test.TestCase):
    def setUp(self):
        self.program = ProgramFactory(reporting_period_start=get_date(dates['program_start']),
                                      reporting_period_end=get_date(dates['program_end']))
        self.indicator = None
        self.targets = []
        self.data = []

    def tearDown(self):
        for data in self.data:
            data.delete()
        for target in self.targets:
            target.delete()
        if self.indicator is not None:
            self.indicator.delete()
        self.program.delete()

    def indicator_for_scenario(self, scenario):
        indicator = IndicatorFactory(
            target_frequency=Indicator.TRI_ANNUAL,
            is_cumulative=scenario['cumulative'],
            direction_of_change=(
                Indicator.DIRECTION_OF_CHANGE_POSITIVE
                if scenario['positive'] else Indicator.DIRECTION_OF_CHANGE_NEGATIVE
            ),
            unit_of_measure_type=(
                Indicator.NUMBER if scenario['number'] else Indicator.PERCENTAGE
            ),
            program=self.program
        )
        for target_number, period in zip(scenario['targets'], dates['targets']):
            self.targets.append(PeriodicTargetFactory(
                indicator=indicator,
                target=target_number,
                start_date=get_date(period[0]),
                end_date=get_date(period[1])
            ))
        for c, data_set in enumerate(scenario['data']):
            date_set = dates['collects'][c]
            for data, date in zip(data_set, date_set):
                self.data.append(ResultFactory(
                    periodic_target=self.targets[c],
                    achieved=data,
                    indicator=indicator,
                    program=self.program,
                    date_collected=get_date(date)
                ))
        return indicator

    def get_scenarios(self):
        for scenario in scenarios:
            try:
                self.indicator = self.indicator_for_scenario(scenario)
            except ValidationError:
                exc_type, exc_value = sys.exc_info()[:2]
                self.fail("{0} in {1} scenario: {2}".format(exc_type.__name__, scenario['desc'], ",".join(exc_value)))
            self.assertEqual(self.indicator.program, self.program)
            yield scenario

    @unittest.skip('not implemented - IPTT Indicator not currently in use, fix tests when it is reintroduced')
    def test_scenario_totals_targetperiods(self):
        settings.DEBUG = True
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            iptt_indicator = IPTTIndicator.withtargets.get(pk=self.indicator.pk)
            expected_queries = 2
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} query to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            # make sure iptt indicator proxy loaded correctly:
            self.assertEqual(iptt_indicator.pk, self.indicator.pk)
            if scenario['blank']:
                # no tests on a blank (unsupported for annotations) indicator:
                return
            self.assertEqual(
                iptt_indicator.lop_target_calculated,
                scenario['lop_target'],
                "In scenario {desc}: calculated lop_target_sum should be {1}, got {0}".format(
                    iptt_indicator.lop_target_calculated, scenario['lop_target'],
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_actual,
                scenario['lop_sum'],
                "In scenario {desc}: calculated lop sum should be {0}, got {1}".format(
                    scenario['lop_sum'], iptt_indicator.lop_actual,
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_percent_met,
                scenario['lop_met'],
                "In scenarios {desc}: lop met_target should be {0}, got {1}".format(
                    scenario['lop_met'], iptt_indicator.lop_percent_met,
                    desc=scenario['desc']))
        settings.DEBUG = False

    @unittest.skip('not implemented - IPTT Indicator not currently in use, fix tests when it is reintroduced')
    def test_scenario_totals_timeperiods(self):
        settings.DEBUG = True
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            iptt_indicator = IPTTIndicator.notargets.get(pk=self.indicator.pk)
            expected_queries = 1
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} query to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            # make sure iptt indicator proxy loaded correctly:
            self.assertEqual(iptt_indicator.pk, self.indicator.pk)
            if scenario['blank']:
                # no tests on a blank (unsupported for annotations) indicator:
                return
            self.assertEqual(
                iptt_indicator.lop_target_sum,
                scenario['lop_target'],
                "In scenario {desc}: calculated lop_target_sum should be {1}, got {0}".format(
                    iptt_indicator.lop_target_sum, scenario['lop_target'],
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_actual_sum,
                scenario['lop_sum'],
                "In scenario {desc}: calculated lop sum should be {0}, got {1}".format(
                    scenario['lop_sum'], iptt_indicator.lop_actual_sum,
                    desc=scenario['desc']))
            self.assertEqual(
                iptt_indicator.lop_met_target,
                scenario['lop_met'],
                "In scenarios {desc}: lop met_target should be {0}, got {1}".format(
                    scenario['lop_met'], iptt_indicator.lop_met_target,
                    desc=scenario['desc']))
        settings.DEBUG = False

    def test_periodic_target_scenarios(self):
        settings.DEBUG = True
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            indicator = IPTTIndicator.tva.get(pk=self.indicator.pk)
            expected_queries = 2 #one for each target
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} queries to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            if scenario['blank']:
                return
            self.assertEqual(
                len(indicator.data_targets),
                len(scenario['targets']),
                "In {desc}: expecting {0} indicator targets, got {1}".format(
                    len(scenario['targets']), len(indicator.data_targets),
                    desc=scenario['desc']))
            for c, target_actual in enumerate(indicator.data_targets):
                self.assertEqual(
                    target_actual.target,
                    scenario['targets'][c],
                    "In {desc}: expected {0} for target {1}, but got {2}".format(
                        scenario['targets'][c], c, target_actual.target,
                        desc=scenario['desc']))
                self.assertEqual(
                    target_actual.data_sum,
                    scenario['results'][c],
                    "In {desc}: expected a sum of {0} for target {1}, but got {2}".format(
                        scenario['results'][c], c, target_actual.data_sum,
                        desc=scenario['desc']))
                self.assertEqual(
                    target_actual.met,
                    scenario['mets'][c],
                    "In {desc}: expected a met of {0} for target {1}, but got {2}".format(
                        scenario['mets'][c], c, target_actual.met,
                        desc=scenario['desc']))
                self.assertEqual(
                    target_actual.within_target_range,
                    scenario['over_under'][c],
                    "In {desc}: expected {0} within_target_range to be {1}, got {2}".format(
                        c, scenario['over_under'][c], target_actual.within_target_range,
                        desc=scenario['desc']))
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "(after all tests): expecting {0} queries to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            settings.DEBUG = False

    def test_timeperiod_scenarios(self):
        settings.DEBUG = True
        periods = [
            {
                'start_date': datetime(2016, 1, 1),
                'end_date': datetime(2016, 6, 30),
            },
            {
                'start_date': datetime(2016, 7, 1),
                'end_date': datetime(2016, 12, 31)
            }]
        for scenario in self.get_scenarios():
            created = len(connection.queries)
            indicator = IPTTIndicator.timeperiods.with_frequency_annotations(Indicator.SEMI_ANNUAL,
                                                                             self.indicator.program.reporting_period_start,
                                                                             self.indicator.program.reporting_period_end).get(pk=self.indicator.id)
            expected_queries = 1
            self.assertLessEqual(
                len(connection.queries)-created, expected_queries,
                "Expecting {0} queries to fetch indicator, but it took {1}".format(
                    expected_queries, len(connection.queries)-created))
            expected_results = len(scenario['semi_annual_results']) if not scenario['blank'] else 0
            self.assertEqual(
                len([p for p in indicator.timeperiods]),
                expected_results,
                "In {desc}, expected {0} timeperiods, got {1}".format(
                    expected_results, len([p for p in indicator.timeperiods]),
                    desc=scenario['desc']))
            for c, period in enumerate(periods):
                if scenario['blank'] != True:
                    period_sum = getattr(indicator, "{0}-{1}".format(
                        period['start_date'].strftime('%Y-%m-%d'),
                        period['end_date'].strftime('%Y-%m-%d')))
                    self.assertEqual(
                        period_sum, scenario['semi_annual_results'][c],
                        "{desc} expected {0} for sum of semi annual period {1}, got {2}".format(
                            scenario['semi_annual_results'][c], c, period_sum,
                            desc=scenario['desc']))
        settings.DEBUG = False


import os
import numpy as np
import tqdm
import torch
import matplotlib.pyplot as plt

from torch.utils.data import DataLoader
from pathlib import Path
from time import time

import Utils.DataLoader as dloader
import Utils.Rendering as render
import Model.VanillaModel as NerfModel
import Model.FullyFusedModel as NerfFFModel

def training(model, nerf_render, data_loader, optimizer, scheduler, tn, tf, num_epochs, device='cpu'):
    """
    A function for training the fully fused MLP or Vanilla NeRF model.
    :param model: a torch nn.Module object that defines the model object to be used for training
    :param nerf_render: an object that contains methods for rendering the NeRF
    :param data_loader: a torch dataloader object that will be used for training the model
    :param optimizer: a torch object that defines the optimizer for training the model
    :param scheduler: a torch object that defines a scheduler
    :param tn: the lower bound of the point sampling interval (see eq (1) in NeRF paper)
    :param tf: the upper bound of the point sampling interval (see eq (1) in NeRF paper)
    :param num_epochs: an int parameter defining the amount of epochs for training the model
    :param device: a string parameter that defines the device to be used for training: 'cpu' or 'cuda'
    :return: numpy array with training losses and a trained torch model
    """
    t1 = time()

    training_loss = []
    for e in tqdm.trange(num_epochs):
        for batch in data_loader:
            o = batch[:, :3].to(device)
            d = batch[:, 3:6].to(device)
            target = batch[:, 6:].to(device)

            prediction = nerf_render.render_view(model, o, d, tn, tf)
            loss = ((prediction - target)**2).mean()

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            training_loss.append(loss.detach().cpu().numpy())
        print("[INFO] Training loss: ", loss.detach().cpu().numpy())
        scheduler.step()

    t2 = time()
    print("It took: ", (t2-t1)/60, ' min')

    return training_loss, model

if __name__ == '__main__':
    root = Path(os.path.realpath(__file__)).parent

    data = r"D:\Myfiles\Projects\OnlineCourses\Udemy_nerf\dataset\fox"
    camera_intrinsic_train_data = data + "/train/intrinsics"
    camera_positional_train_data = data + "/train/pose"
    imgs_train_data = data + "/imgs/train"
    model_name = "nerf_model_v2"
    loss_file = "train_loss_nerf"

    nerf_model = "FullyFusedMLP"  # Vanilla, FullyFusedMLP
    device = "cuda"
    batch_size = 1024
    img_res_x = 400
    img_res_y = 400
    num_bins = 100
    num_epochs = 5
    chunk_size = 10
    learning_rate = 0.001
    tn = 8
    tf = 12
    train = False

    # loading camera parameters (transforms + intrinsics), images for training the model
    print("[INFO] Loading training data.")
    loader = dloader.DataSetLoader()
    train_imgs = loader.load_img_dataset(imgs_train_data)
    train_cam_intr = loader.load_cameras_intrinsics(camera_intrinsic_train_data)
    train_cam_poses = loader.load_cameras_positions(camera_positional_train_data)
    print("[INFO] images arr., shape:            ", train_imgs.shape)
    print("[INFO] cameras intr. arr., shape:     ", train_cam_intr.shape)
    print("[INFO] cameras transform arr., shape: ", train_cam_poses.shape)
    print("[INFO] Done.\n")

    # preparing data that will be used for training NeRF: arrays with cameras rays origins and direction vectors;
    # pixel arrays generated from input images
    print("[INFO] Generating camera rays: origins & directions arrays.")
    NerfRender = render.NerfRender(img_res_x, img_res_y, num_bins, device)
    train_rays_o, train_rays_d = NerfRender.generate_rays(train_cam_poses, train_cam_intr)
    train_target_px_vals = NerfRender.generate_target_pixel_arr(train_imgs)
    print("[INFO] Done.\n")

    model_dir = root / "torch_models"
    model_dir.mkdir(exist_ok=True)

    loss_dir = root / "loss"
    loss_dir.mkdir(exist_ok=True)

    if train:
        print("[INFO] Start training of the model.")
        # preparing training data loaders
        data_tensor = torch.cat((train_rays_o.reshape(-1, 3), train_rays_d.reshape(-1, 3), train_target_px_vals.reshape(-1, 3)), dim=1)
        dataloader = DataLoader(data_tensor, batch_size=batch_size, shuffle=True)

        # training warm up: training only the middle part of the dataset, synthetic data only
        data_tensor_warmup = torch.cat((train_rays_o.reshape(90, 400, 400, 3)[:, 100:300, 100:300, :].reshape(-1, 3),
                                        train_rays_d.reshape(90, 400, 400, 3)[:, 100:300, 100:300, :].reshape(-1, 3),
                                        train_target_px_vals.reshape(90, 400, 400, 3)[:, 100:300, 100:300, :].reshape(-1, 3)), dim=1)
        dataloader_warmup = DataLoader(data_tensor_warmup, batch_size=batch_size, shuffle=True)

        # Setting up the model and it's training
        if nerf_model == "Vanilla":
            model = NerfModel.VanillaNerfModel().to(device)
        elif nerf_model == "FullyFusedMLP":
            model = NerfFFModel.FFNerfModel().to(device)
        else:
            raise ValueError("Unknown model name has been specified!")

        optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
        scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[5, 10], gamma=0.5)

        _, model = training(model, NerfRender, dataloader_warmup, optimizer, scheduler, tn, tf, 1, device=device)
        training_loss, model = training(model, NerfRender, dataloader, optimizer, scheduler, tn, tf, num_epochs, device=device)

        print("[INFO] Done. \n")
        print("[INFO] Saving data.")

        torch.save(model.state_dict(), model_dir.as_posix() + "/" + model_name + ".pt")
        np.savetxt(loss_dir.as_posix() + "/" + loss_file + ".csv", training_loss, delimiter=',')
        print("[INFO] Done. \n")
    else:
        print("[INFO] Loading model: ", model_name + ".pt")

        if nerf_model == "Vanilla":
            model = NerfModel.VanillaNerfModel().to(device)
        elif nerf_model == "FullyFusedMLP":
            model = NerfFFModel.FFNerfModel().to(device)
        else:
            raise ValueError("Unknown model name has been specified!")

        model.load_state_dict(torch.load(model_dir.as_posix() + "/" + model_name + ".pt"))
        training_loss = np.loadtxt(loss_dir.as_posix() + "/" + loss_file + ".csv", delimiter=',')
        print("[INFO] Done. \n")

    # rendering the nerf model after training
    img = NerfRender.generate_view(model, train_rays_o[34], train_rays_d[34], tn, tf, chunk_size)
    plt.imshow(img)
    plt.show()


# You have to do a lot of preprocessing
# NLTK will only help pull apart and analyze text. Tag part of speech.
# It can also do stop words

# Stop words are you end the analyzing and leave
# Or words you don't need they are fluff: EX: "A, The, And, etc" Filler words

from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize

example_sentence = "This is an example showing off stop word filtration."
stop_words = set(stopwords.words("english"))

words = word_tokenize(example_sentence)

filtered_sentence = []

for w in words:
    if w not in stop_words:
        filtered_sentence.append(w)

# One liner: filterd_sentence = [w for w in words if not w in stop_words]

print(filtered_sentence)

# Useful for pulling database and processing times. Don't waste time on words that do not matter.


from sklearn.feature_selection import chi2, SelectKBest
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.preprocessing import LabelBinarizer, PolynomialFeatures
from sklearn_pandas import DataFrameMapper
from sklearn2pmml import sklearn2pmml
from sklearn2pmml.decoration import Alias, CategoricalDomain, ContinuousDomain
from sklearn2pmml.pipeline import PMMLPipeline
from sklearn2pmml.preprocessing import CutTransformer, ExpressionTransformer
from xgboost import XGBClassifier

import pandas
import sys

audit_df = pandas.read_csv("csv/Audit.csv")
#print(audit_df.head(5))

audit_X = audit_df[audit_df.columns.difference(["Adjusted"])]
audit_y = audit_df["Adjusted"]

scalar_mapper = DataFrameMapper([
	("Education", [CategoricalDomain(), LabelBinarizer(), SelectKBest(chi2, k = 3)]),
	("Employment", [CategoricalDomain(), LabelBinarizer(), SelectKBest(chi2, k = 3)]),
	("Occupation", [CategoricalDomain(), LabelBinarizer(), SelectKBest(chi2, k = 3)]),
	("Age", [ContinuousDomain(), CutTransformer(bins = [17, 28, 37, 47, 83], labels = ["q1", "q2", "q3", "q4"]), LabelBinarizer()]),
	("Hours", ContinuousDomain()),
	("Income", ContinuousDomain()),
	(["Hours", "Income"], Alias(ExpressionTransformer("X[1] / (X[0] * 52)"), "Hourly_Income"))
])
interaction_mapper = DataFrameMapper([
	("Gender", [CategoricalDomain(), LabelBinarizer()]),
	("Marital", [CategoricalDomain(), LabelBinarizer()])
])
classifier = XGBClassifier()

pipeline = PMMLPipeline([
	("mapper", FeatureUnion([
		("scalar_mapper", scalar_mapper),
		("interaction", Pipeline([
			("interaction_mapper", interaction_mapper),
			("polynomial", PolynomialFeatures())
		]))
	])),
	("classifier", classifier)
])
pipeline.fit(audit_X, audit_y)

pipeline.configure(compact = True)
pipeline.verify(audit_X.sample(100), zeroThreshold = 1e-6, precision = 1e-6)

sklearn2pmml(pipeline, "pmml/XGBoostAudit.pmml")

if "--deploy" in sys.argv:
	from openscoring import Openscoring

	os = Openscoring("http://localhost:8080/openscoring")
	os.deployFile("XGBoostAudit", "pmml/XGBoostAudit.pmml")


from flask_login import current_user

import time
import uuid

try:
  from commercial.events import EVENT_HANDLERS
except ModuleNotFoundError:
  EVENT_HANDLERS = None

def enqueue_event(org_id, event_type, object_type, object_data, timestamp=None, user=None):
  if not EVENT_HANDLERS:
    return

  event_timestamp = timestamp or time.time()
  event_id = uuid.uuid4().hex

  object_data['object'] = object_type

  event_object = {'id': event_id,
                  'type': event_type,
                  'created': event_timestamp,
                  'organization': org_id,
                  'data': {'object': object_data}}

  user_for_event = user or current_user

  if user_for_event and hasattr(user_for_event, 'email'):
    event_object['data']['user'] = {'object': 'user',
                                    'email': user_for_event.email}

  for handler in EVENT_HANDLERS:
    handler(event_object)


from pydantic import BaseModel
from enum import Enum
from typing import List

class Resolution(BaseModel):
    width: int
    height: int

class SessionAction(BaseModel):
    ip: str
    resolution: Resolution

class SessionLocationDetails(BaseModel):
    country: str
    country_code: str
    region: str
    city: str
    latitude: float
    longitude: float

class SessionActionType(str, Enum):
    login = "login"
    logout = "logout"
    buy = "buy"
    review = "review"
    shopping_cart = "shopping-cart"

class ResponseModel(BaseModel):
    action: SessionActionType
    info: SessionAction
    location: SessionLocationDetails
    action_date: str

class FailedResponseModel(BaseModel):
    errors: List[dict]


#!/usr/bin/env python3

import os, sys
import json
import time
import threading

from ipaddress import IPv4Address

HOME_DIR = os.environ['HOME_DIR']
sys.path.insert(0, HOME_DIR)

from dnx_configure.dnx_constants import * # pylint: disable=unused-wildcard-import
from dnx_configure.dnx_system_info import Interface
from dnx_iptools.dnx_standard_tools import looper, dynamic_looper, Initialize
from dnx_configure.dnx_file_operations import load_configuration, cfg_read_poller
from dnx_configure.dnx_iptables import IPTableManager
from dnx_ips.dnx_ips_log import Log

class Configuration:
    _setup = False

    def __init__(self, name):
        self.initialize  = Initialize(Log, name)
        self._cfg_change = threading.Event()

    @classmethod
    def setup(cls, IPS):
        if (cls._setup):
            raise RuntimeError('configuration setup should only be called once.')
        cls._setup = True

        self = cls(IPS.__name__)
        self.IPS = IPS

        self._load_interfaces()
        self._manage_ip_tables()
        threading.Thread(target=self._get_settings).start()
        threading.Thread(target=self._get_open_ports).start()
        threading.Thread(target=self._update_system_vars).start()

        self.initialize.wait_for_threads(count=3)

        threading.Thread(target=self._clear_ip_tables).start()

    def _manage_ip_tables(self):
        IPTableManager.purge_proxy_rules(table='mangle', chain='IPS')

    def _load_interfaces(self):
        dnx_settings = load_configuration('config')['settings']

        wan_ident = dnx_settings['interfaces']['wan']['ident']

        self.IPS.broadcast = Interface.broadcast_address(wan_ident)

    @cfg_read_poller('ips')
    def _get_settings(self, cfg_file):
        ips = load_configuration(cfg_file)['ips']

        self.IPS.ids_mode = ips['ids_mode']

        self.IPS.ddos_prevention = ips['ddos']['enabled']
        # ddos CPS configured thresholds
        self.IPS.connection_limits = {
            PROTO.ICMP: ips['ddos']['limits']['source']['icmp'],
            PROTO.TCP:  ips['ddos']['limits']['source']['tcp'],
            PROTO.UDP:  ips['ddos']['limits']['source']['udp']
        }

        self.IPS.portscan_prevention = ips['port_scan']['enabled']
        self.IPS.portscan_reject = ips['port_scan']['reject']

        # checking length(hours) to leave IP Table Rules in place for hosts part of ddos attacks
        if (self.IPS.ddos_prevention and not self.IPS.ids_mode):

            # NOTE: this will provide a simple way to ensure very recently blocked hosts do not get their
            # rule removed if passive blocking is disabled.
            if (not self.IPS.block_length):
                self.IPS.block_length = FIVE_MIN

            else:
                self.IPS.block_length = ips['passive_block_ttl'] * ONE_HOUR

        # if ddos engine is disabled
        else:
            self.IPS.block_length = 0

        # src ips that will not trigger ips
        self.IPS.ip_whitelist = set([IPv4Address(ip) for ip in ips['whitelist']['ip_whitelist']])

        self._cfg_change.set()
        self.initialize.done()

    # NOTE: determine whether default sleep timer is acceptible for this method. if not, figure out how to override
    # the setting set in the decorator or remove the decorator entirely.
    @cfg_read_poller('ips')
    def _get_open_ports(self, cfg_file):
        ips = load_configuration(cfg_file)['ips']

        self.IPS.open_ports = {
            PROTO.TCP: {
                int(local_port): int(wan_port) for wan_port, local_port in ips['open_protocols']['tcp'].items()
            },
            PROTO.UDP: {
                int(local_port): int(wan_port) for wan_port, local_port in ips['open_protocols']['udp'].items()
            }
        }

        self._cfg_change.set()
        self.initialize.done()

    @looper(NO_DELAY)
    def _update_system_vars(self):
        # waiting for any thread to report a change in configuration.
        self._cfg_change.wait()

        #resetting the config change event.
        self._cfg_change.clear()

        open_ports = self.IPS.open_ports[PROTO.TCP] or self.IPS.open_ports[PROTO.UDP]
        if (self.IPS.ddos_prevention or (self.IPS.portscan_prevention and open_ports)):
            self.IPS.ins_engine_enabled = True
        else:
            self.IPS.ins_engine_enabled = False

        if (self.IPS.portscan_prevention and open_ports):
            self.IPS.ps_engine_enabled = True
        else:
            self.IPS.ps_engine_enabled = False

        if (self.IPS.ddos_prevention):
            self.IPS.ddos_engine_enabled = True
        else:
            self.IPS.ddos_engine_enabled = False

        self.initialize.done()

    @looper(THIRTY_MIN)
    # TODO: consider making this work off of a thread event. then we can convert the dynamic looper
    # to a standard looper and method will block until an actual host has been blocked.
    def _clear_ip_tables(self):
        # quick check to see if any firewall rules exist
        firewall_rules = self.IPS.fw_rules
        if (not firewall_rules): return

        block_length, now = self.IPS.block_length, fast_time()

        # TODO: look into a method that isnt linearly complext to clear firewall rules. its expected to be
        # somewhat small so its not terrible as is.
        with IPTableManager() as iptables:
            for tracked_ip, insertion_time in list(firewall_rules.items()):
                if (now - insertion_time > block_length) and firewall_rules.pop(tracked_ip, None):
                    iptables.proxy_del_rule(tracked_ip, table='mangle', chain='IPS')


import pickle
import socket
import select
from ww import f
import zmq
import argparse
import logging
import shlex
import time
from pprint import pprint
import cv2
import numpy as np
import sys
from tf_pose.estimator import TfPoseEstimator
from tf_pose.networks import get_graph_path, model_wh
import math
import pyttsx3
import textwrap
import subprocess

HEADERSIZE = 10
PORT = "1234"
context = zmq.Context()
socket = context.socket(zmq.PAIR)
socket.bind("tcp://*:%s" % PORT)

bicep_curl = True
hip_abductor = False

def euclidian(point1, point2):
    return math.sqrt((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2)

def angle_calc(p0, p1, p2):
        # p1 is center point from where we measured angle between p0 and p2
    try:
        a = (p1[0] - p0[0]) ** 2 + (p1[1] - p0[1]) ** 2
        b = (p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2
        c = (p2[0] - p0[0]) ** 2 + (p2[1] - p0[1]) ** 2
        angle = math.acos((a + b - c) / math.sqrt(4 * a * b)) * 180 / math.pi
    except:
        return 0
    return int(angle)

def find_point(human, p): # (pose,p)
    #for point in pose:
    try:
        body_part = human.body_parts[p]
        return (int(body_part.x * width + 0.5), (int(body_part.y * height + 0.5)))
    except:
        return (0, 0)

    # return (0, 0)

def points_exists(human, p1, p2, p3): # (humans, p1, p2, p3)
    # for human in humans:
    sum = 0
    try:
        body_part1 = human.body_parts[p1]
        sum += 1
    except:
        # print("Body part not found")
        # return False
        pass
    try:
        body_part2 = human.body_parts[p2]
        sum += 1
    except:
        # print("Body part not found")
        # return False
        pass
    try:
        body_part3 = human.body_parts[p3]
        sum += 1
    except:
        # print("Body part not found")
        # return False
        pass
    return sum

def give_feedback(diffArmAngle, shoulderPosition, complete):
    feedback = ""
    if complete:
        return "You have completed this exercise with good form! Weight was lifted fully up and upper arm/shoulder" \
               "did not move significantly"
    if diffArmAngle > 70:
        feedback += "You are not curling the weight all the way to the top, up to your shoulders. Try to curl " \
                   "your arm completely so that your forearm is parallel with your torso. It may help to use " \
                   "lighter weight."
    if diffArmAngle < 50:
        feedback += "You are not lowering the weight all the way back to the starting position. Remember to lower the " \
                   "weight till your forearm is parallel with your torso in order to get full range of motion" \
                   "and the best results."
    if not shoulderPosition:
        feedback += "Your upper arm shows significant rotation around the shoulder when curling. Try holding your" \
                   " upper arm still, parallel to your chest, and concentrate on rotating around your elbow only."

    return feedback

def find_correct_arm(pose_seq):
    # TODO: add a condition that finds the correct arm when both arms have all points showing.
    right_present = points_exists(pose_seq, 4, 3, 2) # 1 if ... else 0
    left_present = points_exists(pose_seq, 7, 6, 5) # 1 if ... else 0
    right_count = right_present
    left_count = left_present
    if right_count == left_count:
        return "Both arms detected, further analysis required.."
    side = 'right' if right_count > left_count else 'left'

    return side

def find_correct_arm_v2(human):
    try:
        lhand = human.body_parts[7]  # left hand point
        rhand = human.body_parts[4]  # right hand point
        rhip = human.body_parts[8]
        lhip = human.body_parts[11]
        y_rhip = rhip.y * height
        y_lhip = lhip.y * height
        y_lhand = lhand.y * height
        y_rhand = rhand.y * height
        diffRight = abs(y_rhand - y_rhip)
        diffLeft = abs(y_lhand - y_lhip)
        # print("Difference in left side (hand - hip): ", diffLeft)
        # print("Difference in right side (hand - hip): ", diffRight)
        if diffRight > 19:
            return "Right"
        elif diffLeft > 19:
            return "Left"
        return "Can't identify correct arm."
    except:
        pass

def find_correct_arm_angle(angleCorrectArm, correctArm):
    prevArmAngle = -1
    if correctArm == 'right':
        prevArmAngle = angleCorrectArm
        angleCorrectArm = angle_calc(find_point(human, 4), find_point(human, 3), find_point(human, 2))  # right arm

    elif correctArm == 'left':
        prevArmAngle = angleCorrectArm
        angleCorrectArm = angle_calc(find_point(human, 7), find_point(human, 6), find_point(human, 5))  # left arm

    return angleCorrectArm, prevArmAngle

def find_correct_human(humans):
    correct_human = None
    max_human = -1
    for human in humans:
        tempSum = human.get_max_score()
        if tempSum > max_human:
            max_human = tempSum
            correct_human = human

    return correct_human

def shoulder_movement(max, min):
    if max[-2] - min[-2] > 20:
        return True
    return False

def update_Min_Max_values(x, y, bodyPart):
    if x < 0 or y < 0:
        return bodyPart
    newMinMax = bodyPart
    if x < bodyPart[0]:
        newMinMax[0] = x
    if x > bodyPart[1]:
        newMinMax[1] = x
    if y < bodyPart[2]:
        newMinMax[2] = y
    if y > bodyPart[3]:
        newMinMax[3] = y
    return newMinMax

def find_correct_hip(rightAngle, leftAngle):

    if rightAngle > 11:
        return 'Right'
    elif leftAngle > 11:
        return 'Left'

    return "Can't identify correct hip."

def find_hip_angle():
    rightAngle = angle_calc(find_point(human, 9), find_point(human, 8), find_point(human, 11)) - 90 # right hip
    leftAngle = angle_calc(find_point(human, 12), find_point(human, 11), find_point(human, 8)) - 90 # left hip

    return rightAngle, leftAngle

def find_correct_hip_angle(angleCorrectHip, correctHip):
    prevHipAngle = -1
    if correctHip == "Right":
        prevHipAngle = angleCorrectHip
        angleCorrectHip = angle_calc(find_point(human, 9), find_point(human, 8), find_point(human, 11)) - 90 # right hip

    elif correctHip == "Left":
        prevHipAngle = angleCorrectHip
        angleCorrectHip = angle_calc(find_point(human, 12), find_point(human, 11), find_point(human, 8)) - 90 # left hip

    elif correctHip == "Can't identify correct hip.":
        prevHipAngle = -1
        angleCorrectHip = -1

    return angleCorrectHip, prevHipAngle

def is_lean_too_far(shoulder):
    lean = False

    x_diff = abs(shoulder[1] - shoulder[0])
    y_diff = abs(shoulder[3] - shoulder[2])
    # print(x_diff)
    # print(y_diff)
    if x_diff > 30 and y_diff > 30:
        lean = True

    return lean

count = 0
i = 0
frm = 0
elbow_Min_max = [100000, -1, 100000, -1]  # [xMin, xMax, yMin, yMax]
hip_Min_Max = [100000, -1, 100000, -1]  # [xMin, xMax, yMin, yMax]
global height, width
average_ratio = 0
bicepCount = 0
correctArm = "No arm detected"
prevCorrectArm = ""
maxArmAngle = -1
minArmAngle = 361
down = False
up = True
state = "start"
prevState = "start"
human = None
spokeUp = False
spokeDown = True
feedback = ""
angleCorrectArm = -1
prevArmAngle = -1
badForm = False
badFormCount = 0
fps_time = 0
# w, h = model_wh("432x368")
w, h = model_wh("256x256")
# e = TfPoseEstimator(get_graph_path('mobilenet_thin'), target_size=(432, 368))
e = TfPoseEstimator(get_graph_path('mobilenet_thin'), target_size=(256, 256))

shoulder_Min_max = [100000, -1, 100000, -1]  # [xMin, xMax, yMin, yMax]
correctHip = "No hip detected"
prevCorrectHip = ""
angleCorrectHip = -1
prevHipAngle = -1
abductorCount = 0
prevHipCoords = (-1, -1)
correctHipCoords = (-1, -1)

while True:

    print("Receiving frame... ")
    data = socket.recv()
    np_data = np.fromstring(data, np.uint8)
    image = cv2.imdecode(np_data, cv2.IMREAD_COLOR)

    print("Processing image... ")

    # turn the encoded data back into an image and then do all the processing that you need to do on it.
    i = 1
    humans = e.inference(image, resize_to_default=(w > 0 and h > 0), upsample_size=4.0)
    pose = humans
    human = find_correct_human(humans)
    image = TfPoseEstimator.draw_humans(image, human, imgcopy=False)
    height, width = image.shape[0], image.shape[1]
    isCurling = False
    inPos = int(width * .75)
    shoulderPosition = False
    y = 0
    correctTorsoVector = -1
    correctArmVector = -1
    x_elbow = -1
    y_elbow = -1
    x_hip = -1
    y_hip = -1
    x_shoulder = -1
    y_shoulder = -1

    # This is the part of the loop that recognizes whether a bicep curl is being performed or not
    if bicep_curl:
        if len(pose) > 0:
            if state == "start":
                correctArm = find_correct_arm(human)
                correctArmv2 = find_correct_arm_v2(human)

            if prevCorrectArm is not correctArm:
                print('Exercise arm detected as: {}.'.format(correctArm))
                prevCorrectArm = correctArm

            angleCorrectArm, prevArmAngle = find_correct_arm_angle(angleCorrectArm, correctArm)

            # if i don't include the stuff before continue the video will freeze for a couple milliseconds
            # before resuming
            if angleCorrectArm == 0 or abs(angleCorrectArm - prevArmAngle) > 40:
                cv2.putText(image, "Rep Number: {}".format(bicepCount), (10, 130),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
                cv2.putText(image, "Instructions: ", ((inPos - 120), 110), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                            (0, 255, 0), thickness=2)
                cv2.putText(image, "{} Arm angle:".format(correctArm), (10, 90),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
                cv2.putText(image, "Curling: {}".format(state), (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                            (0, 255, 0), thickness=2)
                if state is "bicep_curl_start" or state is "bicep_curling_up":
                    cv2.arrowedLine(image, (inPos, 140), (inPos, 80), (0, 0, 255), 2, 8, 0, 0.3)
                    if spokeUp is False:
                        spokeUp = True
                        spokeDown = False
                elif state is "bicep_curl_top" or state is "bicep_curling_down":
                    cv2.arrowedLine(image, (inPos, 80), (inPos, 140), (0, 0, 255), 2, 8, 0, 0.3)
                    if spokeDown is False:
                        spokeDown = True
                        spokeUp = False

            if angleCorrectArm > maxArmAngle and state is "bicep_curl_top" or state is "bicep_curl_up" or state is \
                    "bicep_curl_start":
                maxArmAngle = angleCorrectArm
            if angleCorrectArm < minArmAngle and state is "bicep_curl_start":
                minArmAngle = angleCorrectArm

            if state is "bicep_curl_start" or state is "bicep_curling_up":
                cv2.arrowedLine(image, (inPos, 140), (inPos, 80), (0, 0, 255), 2, 8, 0, 0.3)
                if spokeUp is False:
                    spokeUp = True
                    spokeDown = False
            elif state is "bicep_curl_top" or state is "bicep_curling_down":
                cv2.arrowedLine(image, (inPos, 80), (inPos, 140), (0, 0, 255), 2, 8, 0, 0.3)
                if spokeDown is False:
                    spokeDown = True
                    spokeUp = False

            try:
                elbow = human.body_parts[3 if correctArm == 'right' else 6]  # elbow point
                hip = human.body_parts[8 if correctArm == 'right' else 11]
                x_elbow = elbow.x * width
                y_elbow = elbow.y * height
                x_hip = hip.x * width
                y_hip = hip.y * width
            except:
                pass

            # start -> bicep_curl_start
            if 166 > angleCorrectArm > 145 and state == "start" and correctArm != "Both arms detected, further analysis required..":
                prevState = state
                state = "bicep_curl_start"
                instructions = "While keeping upper arm parallel to torso, " \
                               "contract bicep to raise hand towards shoulder"
                update_Min_Max_values(x_elbow, y_elbow, elbow_Min_max)
                update_Min_Max_values(x_hip, y_hip, hip_Min_Max)
                badForm = False

                print(state)

            # bicep_curl_start -> start
            elif angleCorrectArm > 166 and state == "bicep_curl_start":
                prevState = state
                state = "start"
                instructions = "You returned to starting position and restarted exercise. Please follow instructions" \
                               "to correctly complete exercise."
                badForm = False
                print(state)

            # bicep_curl_start -> bicep_curling_up
            elif 145 > angleCorrectArm > 65 and state == "bicep_curl_start":
                prevState = state
                state = "bicep_curling_up"
                instructions = "Continue raising hand towards shoulder"
                badForm = False
                print("Curl Up")
                print(state)

            # bicep_curling_up -> bicep_curling_top or bicep_curling_up -> bicep_curl_finish
            elif angleCorrectArm < 65 and state == "bicep_curling_up":
                prevState = state
                state = "bicep_curl_top"
                instructions = "Once you feel you can't raise your hand further towards your shoulder, reverse your " \
                               "previous motion and while keeping upper arm parallel to torso, slowly lower your hand" \
                               "back towards your hip"
                down = True
                up = False
                badForm = False
                print("Curl Down")
                print(state)
                update_Min_Max_values(x_elbow, y_elbow, elbow_Min_max)
                update_Min_Max_values(x_hip, y_hip, hip_Min_Max)

            elif angleCorrectArm < 60 and state == "bicep_curling_down":
                prevState = state
                state = "bicep_curl_top"
                badForm = True
                if prevState == "bicep_curling_down":
                    diffArmAngle = 30
                    feedback = "You have not lowered the weight all the way down to the original position. Remember " \
                               "to lower the weight all the way down to the point your forearm is parallel with your " \
                               "torso in order to have correct form and get the best results."
                    print(feedback)
                down = True
                up = False
                print(state)
                update_Min_Max_values(x_elbow, y_elbow, elbow_Min_max)
                update_Min_Max_values(x_hip, y_hip, hip_Min_Max)

            # bicep_curl_top -> bicep_curling_down
            elif 145 > angleCorrectArm > 65 and down and state == "bicep_curl_top":
                prevState = state
                state = "bicep_curling_down"
                instructions = "Continue lowering hand towards hip"
                badForm = False
                print(state)

            # bicep_curling_down -> bicep_curl_top or bicep_curling_down -> bicep_curl_finish
            elif 166 > angleCorrectArm > 145 and state == "bicep_curling_up":
                prevState = state
                state = "bicep_curl_finish"
                instructions = "Bad form, please try again."
                if prevState == "bicep_curling_up":
                    feedback = "You are not curling the weight all the way to the top, up to your shoulders." \
                               " Try to curl your arm completely so that your forearm is parallel with your torso." \
                               " It may help to use lighter weight."
                    print(feedback)
                badForm = True
                print(state)

            elif 166 > angleCorrectArm > 145 and state == "bicep_curling_down":
                prevState = state
                state = "bicep_curl_finish"
                instructions = "Congratulations! You have successfully completed a bicep curl!"
                update_Min_Max_values(x_elbow, y_elbow, elbow_Min_max)
                update_Min_Max_values(x_hip, y_hip, hip_Min_Max)
                aveXHip = (hip_Min_Max[1] + hip_Min_Max[0]) / 2
                maxElbowDist = abs(elbow_Min_max[1] - aveXHip)
                minElbowDist = abs(elbow_Min_max[0] - aveXHip)
                bicepCount += 1
                if maxElbowDist > 45 or minElbowDist > 45:
                    feedback = "Your upper arm shows significant rotation around the shoulder when curling." \
                               " Try holding your upper arm still, parallel to your chest, and concentrate on " \
                               "rotating around your elbow only."
                    badForm = True
                else:
                    feedback = "You have completed this exercise with good form! Weight was lifted fully up and" \
                               " upper arm/shoulder did not move significantly."
                    badFormCount = 0
                    badForm = False
                print(state)
                print(feedback)

            # bicep_curl_finish -> start or bicep_curl_finish -> bicep_curling_up
            elif state == "bicep_curl_finish":
                state = "start"
                instructions = ""
                elbow_Min_max = [100000, -1, 100000, -1]
                hip_Min_Max = [100000, -1, 100000, -1]
                # print(feedback)
                badForm = False
                badFormCount = 0
                print(state)

            cv2.putText(image, "Curling: {}".format(state), (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                        (0, 255, 0), thickness=2)
            cv2.putText(image,
                        "Instructions: ", ((inPos - 120), 110), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
            cv2.putText(image, "{} Arm angle: {}".format(correctArm, angleCorrectArm), (10, 90),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
            cv2.putText(image, "Rep Number: {}".format(bicepCount), (10, 130),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)

            if badForm and badFormCount < 25:
                cv2.putText(image, "BAD FORM!", (int(width / 2), int(height * (1 / 8))), cv2.FONT_HERSHEY_SIMPLEX, 1,
                            (0, 0, 255), thickness=2)
                badFormCount += 1

            if badFormCount == 25:
                badFormCount = 0

        else:
            state = "start"
            prevState = "start"
            spokeUp = False
            spokeDown = True
            bicepCount = 0
            elbow_Min_max = [100000, -1, 100000, -1]
            hip_Min_Max = [100000, -1, 100000, -1]
            text = "Please stand with curling arm facing camera. Please ensure the upper half of your body can be " \
                   "seen by the camera."
            wrapped_text = textwrap.wrap(text, width=35)
            j = 0
            for line in wrapped_text:
                textsize = cv2.getTextSize(line, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)[0]
                gap = textsize[1] + 10

                y = int((image.shape[0] + textsize[1]) / 2 - 110) + j * gap
                x = int((image.shape[1] - textsize[0]) / 2)

                cv2.putText(image, line, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2,
                            lineType=cv2.LINE_AA)  # (int(width/2) - 150, 110)

                j += 1

        cv2.putText(image, "FPS: %f" % (1.0 / (time.time() - fps_time)), (10, 10),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

        fps_time = time.time()

    elif hip_abductor:
        if len(pose) > 0:
            rightHipAngle, leftHipAngle = find_hip_angle()
            print("Left Hip Angle: ", leftHipAngle)
            print("Right Hip Angle: ", rightHipAngle)

            # if not right and not left:
            if state == "start":
                correctHip = find_correct_hip(rightHipAngle, leftHipAngle)

            if (prevCorrectHip is not correctHip) and state == "start":
                prevState = state
                state = "start"
                if correctHip != "Can't identify correct hip.":
                    print('{} hip detected.'.format(correctHip))
                else:
                    print('{}'.format(correctHip))
                prevCorrectHip = correctHip

            if correctHip == "Right":
                prevHipCoords = correctHipCoords
                correctHipCoords = (find_point(human, 9)[0], find_point(human, 9)[1])
                if (state is "abductor_start" or state is "abductor_up") and up:
                    cv2.arrowedLine(image, (correctHipCoords[0] - 10, correctHipCoords[1] - 10),
                                    (correctHipCoords[0] - 70, correctHipCoords[1] - 70), (0, 0, 255), 2, 8, 0, 0.3)
                elif (state is "abductor_top" or state is "abductor_down") and down:
                    cv2.arrowedLine(image, (correctHipCoords[0] + 10, correctHipCoords[1] + 10),
                                    (correctHipCoords[0] + 70, correctHipCoords[1] + 70), (0, 0, 255), 2, 8, 0, 0.3)
            elif correctHip == "Left":
                prevHipCoords = correctHipCoords
                correctHipCoords = (find_point(human, 12)[0], find_point(human, 12)[1])
                print("Correct Hip Coordinates: ", correctHipCoords)
                if state is "abductor_start" or state is "abductor_up" and up:
                    cv2.arrowedLine(image, (correctHipCoords[0] + 10, correctHipCoords[1] - 10),
                                    (correctHipCoords[0] + 70, correctHipCoords[1] - 70), (0, 0, 255), 2, 8, 0, 0.3)
                elif state is "abductor_top" or state is "abductor_down" or state is "abductor_finished" and down:
                    if abs(prevHipCoords[1] - correctHipCoords[1]) > 50:
                        correctHipCoords[0] = prevHipCoords[0] - 10
                        correctHipCoords[1] = prevHipCoords[1] + 10
                    cv2.arrowedLine(image, (correctHipCoords[0] - 10, correctHipCoords[1] + 10),
                                    (correctHipCoords[0] - 70, correctHipCoords[1] + 70), (0, 0, 255), 2, 8, 0, 0.3)

            angleCorrectHip, prevHipAngle = find_correct_hip_angle(angleCorrectHip, correctHip)
            if prevHipAngle is not angleCorrectHip:
                prevHipAngle = angleCorrectHip

            if angleCorrectHip > 70 or abs(angleCorrectHip - prevHipAngle) > 40:
                cv2.putText(image, "FPS: %f" % (1.0 / (time.time() - fps_time)), (10, 10),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

            try:
                shoulder = human.body_parts[2 if correctHip == "Left" else 5]  # left hand point
                x_shoulder = shoulder.x * width
                y_shoulder = shoulder.y * height
            except:
                pass

            # Either left or right leg has been identified as the correct leg and exercise can start
            if state == "start":  # and correctHip != "Can't identify correct hip."
                prevState = state
                state = "abductor_start"
                shoulder_Min_max = update_Min_Max_values(x_shoulder, y_shoulder, shoulder_Min_max)
                up = True
                print(state)

            # abductor_start -> start
            elif angleCorrectHip < 11 and state == "abductor_start":
                prevState = state
                state = "start"
                print(state)

            # abductor_start -> abductor_up
            elif 40 > angleCorrectHip > 15 and state == "abductor_start":
                prevState = state
                state = "abductor_up"
                print(state)

            # abductor_up -> abductor_top
            elif angleCorrectHip > 40 and state == "abductor_up":
                prevState = state
                state = "abductor_top"
                up = False
                down = True
                update_Min_Max_values(x_shoulder, y_shoulder, shoulder_Min_max)
                print(state)

            # abductor_up -> abductor_finished
            elif angleCorrectHip < 15 and state == "abductor_up":
                prevState = state
                state = "abductor_finished"
                up = False
                down = False
                feedback = "You did not raise your leg to the top of your range of motion. Please try and lift your" \
                           " leg as high as you can and try again."
                print(state)
                print(feedback)

            # abductor_top -> abductor_down
            elif 40 > angleCorrectHip > 20 and state == "abductor_top":
                prevState = state
                state = "abductor_down"
                is_lean_too_far(shoulder_Min_max)
                print(state)

            # abductor_down -> abductor_top
            elif angleCorrectHip > 45 and state == "abductor_down":
                prevState = state
                state = "abductor_top"
                feedback = "You did not lower your leg back to the bottom of your range of motion. Remember to lower " \
                           "your leg back to its starting position before starting another repetition."
                update_Min_Max_values(x_shoulder, y_shoulder, shoulder_Min_max)
                print(state)
                print(feedback)

            # abductor_down -> abductor_finished
            elif angleCorrectHip < 20 and state == "abductor_down":
                prevState = state
                state = "abductor_finished"
                abductorCount += 1
                up = False
                down = False
                update_Min_Max_values(x_shoulder, y_shoulder, shoulder_Min_max)
                lean = is_lean_too_far(shoulder_Min_max)
                if not lean:
                    feedback = "You have successfully completed a hip abuctor with good form. Next time try and lift " \
                               "your leg even higher than you did the last time."
                else:
                    feedback = "You have successfully completed a hip abductor, however during the exercise you were " \
                               "leaning too far to one side. Try and keep your torso stable and still as much as " \
                               "possible throughout the exercise."
                print(state)
                print(feedback)

            elif state == "abductor_finished":
                prevState = state
                state = "start"
                up = False
                down = False
                shoulder_Min_max = [100000, -1, 100000, -1]  # [xMin, xMax, yMin, yMax]
                print(state)

            cv2.putText(image, "Hip Abduction State: {}".format(state), (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                        (0, 255, 0), thickness=2)
            cv2.putText(image, "Rep Number: {}".format(abductorCount), (10, 130),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
            if correctHip != "Can't identify correct hip.":
                cv2.putText(image, "{} Hip angle: {}".format(correctHip, angleCorrectHip), (10, 90),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)
            else:
                cv2.putText(image, "{}".format(correctHip), (10, 90),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=2)

        else:
            abductorCount = 0
            shoulder_Min_max = [100000, -1, 100000, -1]  # [xMin, xMax, yMin, yMax]
            text = "Please stand facing camera with your entire body in full view of the camera."
            wrapped_text = textwrap.wrap(text, width=35)
            j = 0
            for line in wrapped_text:
                textsize = cv2.getTextSize(line, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2)[0]
                gap = textsize[1] + 10

                y = int((image.shape[0] + textsize[1]) / 2 - 110) + j * gap
                x = int((image.shape[1] - textsize[0]) / 2)

                cv2.putText(image, line, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), thickness=2,
                            lineType=cv2.LINE_AA)  # (int(width/2) - 150, 110)

                j += 1

        cv2.putText(image, "FPS: %f" % (1.0 / (time.time() - fps_time)), (10, 10),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

        fps_time = time.time()

    print ("Finished processing image, sending processed image back to the client...")
    _, newdata = cv2.imencode('.jpg', image)
    socket.send(newdata.tostring())


"""
Tests for signal handlers
"""
from django.test import TestCase

from journals.apps.core.tests.factories import (
    UserFactory,
)

class TestUserPostSave(TestCase):
    """
    Test Cases for post_save signal of User model.
    """

    def test_notification_settings_disabled_on_user_creation(self):
        """
        Test user's notifications preferences are disabled at the time of user creation
        """
        user = UserFactory()
        self.assertFalse(user.wagtail_userprofile.submitted_notifications)
        self.assertFalse(user.wagtail_userprofile.approved_notifications)
        self.assertFalse(user.wagtail_userprofile.rejected_notifications)

    def test_notification_settings_can_be_enbled(self):
        """
        Test user's notifications preferences can be enabled by updating user's profile
        """
        user = UserFactory()
        userprofile = user.wagtail_userprofile
        userprofile.submitted_notifications = True
        userprofile.approved_notifications = True
        userprofile.rejected_notifications = True
        userprofile.save()
        self.assertTrue(user.wagtail_userprofile.submitted_notifications)
        self.assertTrue(user.wagtail_userprofile.approved_notifications)
        self.assertTrue(user.wagtail_userprofile.rejected_notifications)


from enum import Enum, unique
import numpy as np
import math

import msgs.scene_msg as scene_m
from obj_state import ego_vehicle as ego_v
from obj_state import road_obj as road_o
from msgs.log import logger

# safety degree #
@unique
class safety_degree(Enum):
    """describ the safety degree
    """
    safe = 0
    attentive = 1
    dangerous = 2

@unique
class relation_state(Enum):
    """describ the relation between ego vehicle and other obj
    Params:
        none_overlap:
        overlap_in_x:
        overlap_in_y:
        all_overlap:
    """
    none_overlap = 0
    overlap_in_x = 1
    overlap_in_y = 2
    all_overlap = 3

## use to calculate theshold of safety degree ##
weather_effect_ratio = {scene_m.weather.clear: 1.,
                        scene_m.weather.fog: 1.4,
                        scene_m.weather.rain: 1.2,
                        scene_m.weather.dust: 1.3}
road_state_ratio = {scene_m.road_state.normal: 1.,
                    scene_m.road_state.wetness: 0.83,
                    scene_m.road_state.snow: 0.67,
                    scene_m.road_state.leaves: 0.91,}

COMFORTABLE_DECELERATION = 1.8 ###m/s/s
AVG_DECELERATION = 4 ### m/s/s
MAX_DECELERATION = 13 ### m/s/s

##ploy used to calculate the std and max of gaussian##
# x = np.arange(0, 70, 10)
# std_y = np.array([0.5, 0.9, 1.3, 1.7, 2.1, 2.5, 2.9])
# max_y = np.array([7., 8., 10., 15., 20., 25., 30.])
# z_std = np.polyfit(x, std_y, 4)
# p_std = np.poly1d(z_std)
# z_max = np.polyfit(x, max_y, 3)
# p_max = np.poly1d(z_max)

def gaussian_1d(x, mean, for_what, std, max):
    """produce a val respresents the socre according the gaussian distribution.
    Args:
        x: a input val.
        mean: the mean of gaussian distribution.
        std: the std of gaussian distribution.
        for_what: should be one of safety_degree
    """

    def norm(x, mu, sigma):
        """normal gaussian function
        """
        #print(sigma)
        pdf = math.exp(-((x - mu) ** 2) / (2 * sigma ** 2)) / (sigma * math.sqrt(2 * np.pi))
        return max*pdf

    assert for_what in list(safety_degree.__members__.values())

    if for_what is safety_degree.dangerous:
        if x < mean:
            score = norm(x=mean, mu=mean, sigma=std)
        else:
            score = norm(x=x, mu=mean, sigma=std)
    elif for_what is safety_degree.attentive:
        score = norm(x=x, mu=mean, sigma=std)
    elif for_what is safety_degree.safe:
        if x > mean:
            score = norm(x=mean, mu=mean, sigma=std)
        else:
            score = norm(x=x, mu=mean, sigma=std)
    else:
        raise ValueError("Error")

    return score

def comfort_level_scores(acc):
    """ assess the comfort level
    Args:
        acc: the acc val for host vehicle
    """
    def norm(x, mu, sigma):
        """normal gaussian function
        """
        pdf = math.exp(-((x - mu) ** 2) / (2 * sigma ** 2))
        return pdf

    def gaussian_1d(x, sigma=5):
        if x >= 0:
            return np.array([0., 0., 1.])
        else:
            x = math.fabs(x)
            # print(x)
            if x >= MAX_DECELERATION:
                return np.array([1, norm(x, AVG_DECELERATION, 6), norm(x, COMFORTABLE_DECELERATION, 1.5)])
            elif x <= COMFORTABLE_DECELERATION:
                return np.array([norm(x, MAX_DECELERATION, 6), norm(x, AVG_DECELERATION, 1.5), 1])
            else:
                return np.array([norm(x, MAX_DECELERATION, 6), norm(x, AVG_DECELERATION, 1.5), norm(x, COMFORTABLE_DECELERATION, 1.5)])

    return gaussian_1d(acc)

def _score_2_threat_degree(score):
    if np.argmax(score) == 0:
        return safety_degree.dangerous
    elif np.argmax(score) == 1:
        return safety_degree.attentive
    else:
        return safety_degree.safe

def _assess_one_obj_threat_score(ego_vehicle, road_obj, weather_type=scene_m.weather.clear,
                           road_state=scene_m.road_state.normal):
    """assess the road object safety degree for ego vehicle
    Args:
        ego_vehicle: a class in obj_state.ego_vehicel
        road_obj: a class in obj_state.road_obj
        weather_type: when in unclear weather, it is more likely to be dangerous
        road_state: when road is wetness, snow or leaves, it is more likely to be dangerous
    Return:
        [Dangerous score, attentive score, safe score]
    """

    def judge_relation_state(ego_pos, ego_size, other_pos, other_size):
        """jugde the relation between ego vehicle and other obj
        Args:
            ego_pos: a tuple describe ego position, (x, y, z)
            ego_size: a tuple describe ego geometry size, (h, w, d)
            other_pos: a tuple describe other vehicle position, (x, y, z)
            other_size: a tuple describe other vehicle geometry size, (h, w, d)
        Return:
            a relation_state
        """
        ego_vehicle_radius = math.sqrt((ego_size[1]/2)**2 + (ego_size[2]/2)**2)
        other_obj_radius = math.sqrt((other_size[1]/2)**2 + (other_size[2]/2)**2)
        length = ego_vehicle_radius + other_obj_radius

        m_distance_x = math.fabs(ego_pos[0] - other_pos[0])
        m_distance_y = math.fabs(ego_pos[1] - other_pos[1])

        if m_distance_x <= length or m_distance_y <= length:
            if m_distance_x <= length and m_distance_y <= length:
                return relation_state.all_overlap
            elif m_distance_x <= length:
                return relation_state.overlap_in_x
            else:
                return relation_state.overlap_in_y
        else:
            return relation_state.none_overlap

    def time_to_collision_2d(ego_pos, ego_linear, ego_size, other_pos, other_linear, other_size, dim='xy'):
        """calculate TTC in x,y dimention, TTC is calculated as: TTC = (x[i-1] - x[i] - L) / (v[i] - v[i-1]),
            where i indicates the vehicle position which has the bigger velocity. When TTC > 0, it indicates
            that the remaining time in which two vehicles would collide. When TTC < 0, in indicates that it's
            impossible for two cars to collide.
        Args:
            ego_pos: a tuple describe ego position, (x, y, z)
            ego_linear: a tuple describe ego linear velocity in (x, y, z) dimention
            ego_size: a tuple describe ego geometry size, (h, w, d)
            other_pos: a tuple describe other vehicle position, (x, y, z)
            other_linear: a tuple describe ego linear velocity in (x, y, z) dimention
            other_size: a tuple describe other vehicle geometry size, (h, w, d)
            dim: a string indicates calculate TTC in which dimention, must be 'xy', 'y' or 'x'.
        Return:
            ttc_in_x: the remaining time in which two vehicles would collide in x dimention.
            ttc_in_y: the remaining time in which two vehicles would collide in y dimention.
        """
        ## assert ##
        assert dim in ['xy', 'x', 'y']

        ## length ##
        ego_vehicle_radius = math.sqrt((ego_size[1] / 2) ** 2 + (ego_size[2] / 2) ** 2)
        other_obj_radius = math.sqrt((other_size[1] / 2) ** 2 + (other_size[2] / 2) ** 2)
        length = ego_vehicle_radius + other_obj_radius

        ## 1e-5 is to avoid zero-divide error ##
        if ego_linear[0] >= other_linear[0]:
            ttc_in_x = (other_pos[0] - ego_pos[0] - length) / (ego_linear[0] - other_linear[0] + 1e-5)
        else:
            ttc_in_x = (ego_pos[0] - other_pos[0] - length) / (other_linear[0] - ego_linear[0] + 1e-5)

        if ego_linear[1] >= other_linear[1]:
            ttc_in_y = (other_pos[1] - ego_pos[1] - length) / (ego_linear[1] - other_linear[1] + 1e-5)
        else:
            ttc_in_y = (ego_pos[1] - other_pos[1] - length) / (other_linear[1] - ego_linear[1] + 1e-5)

        if dim == 'xy':
            return ttc_in_x, ttc_in_y
        elif dim == 'x':
            return ttc_in_x
        elif dim == 'y':
            return ttc_in_y
        else:
            raise ValueError("Imposible get here!!! Something must wrong!!!")

    def much_bigger(val, bigger_than):
        """ normally, the val should be a abs of subtraction value
        Return:
            if val >= bigger_than: True
            else: False
        """
        if val >= bigger_than:
            return True
        else:
            return False

    def time_to_escape_2d(ego_pos, ego_linear, ego_size, other_pos, other_linear, other_size, dim='xy'):
        """When the relation between ego vehicle and other obj is overlap, we need this indicator to
            indicate the remaining time in which the ego(or other obj) can escape the overlap area.
        Args:
            ego_pos: a tuple describe ego position, (x, y, z)
            ego_linear: a tuple describe ego linear velocity in (x, y, z) dimention
            ego_size: a tuple describe ego geometry size, (h, w, d)
            other_pos: a tuple describe other vehicle position, (x, y, z)
            other_linear: a tuple describe ego linear velocity in (x, y, z) dimention
            other_size: a tuple describe other vehicle geometry size, (h, w, d)
            dim: a string indicates calculate TTE in which dimention, must be 'xy', 'y' or 'x'.
        Return:
            TTE in specific dimention
        """
        ## length ##
        ego_vehicle_radius = math.sqrt((ego_size[1] / 2) ** 2 + (ego_size[2] / 2) ** 2)
        other_obj_radius = math.sqrt((other_size[1] / 2) ** 2 + (other_size[2] / 2) ** 2)
        length = ego_vehicle_radius + other_obj_radius

        ## 1e-8 is to avoid zero-divide error ##
        if ego_linear[0] >= other_linear[0]:
            tte_in_x = (other_pos[0] - ego_pos[0] + length) / (ego_linear[0] - other_linear[0] + 1e-8)
        else:
            tte_in_x = (ego_pos[0] - other_pos[0] + length) / (other_linear[0] - ego_linear[0] + 1e-8)

        if ego_linear[1] >= other_linear[1]:
            tte_in_y = (other_pos[1] - ego_pos[1] + length) / (ego_linear[1] - other_linear[1] + 1e-8)
        else:
            tte_in_y = (ego_pos[1] - other_pos[1] + length) / (other_linear[1] - ego_linear[1] + 1e-8)

        if dim == 'xy':
            return tte_in_x, tte_in_y
        elif dim == 'x':
            return tte_in_x
        elif dim == 'y':
            return tte_in_y
        else:
            raise ValueError("Imposible get here!!! Something must wrong!!!")

    def softmax(x):
        return np.exp(x) / np.expand_dims(np.sum(np.exp(x), axis=-1),axis=-1)

    def cos_vec(vec1, vec2):
        """caculate the cos val between two vec
        Args:
            vec1: ndarray with a shape [n_dim]
            vec2: ndarray with a shape [n_dim]
        """
        cos = np.sum(vec1*vec2) / (np.sqrt(np.sum(np.square(vec1)))*np.sqrt(np.sum(np.square(vec2))) +1e-6)
        return cos

    ## assert ##
    assert type(ego_vehicle) == ego_v.ego_vehicle
    assert type(road_obj) == road_o.road_obj
    assert weather_type in list(scene_m.weather.__members__.values())
    assert road_state in list(scene_m.road_state.__members__.values())

    ## init safety degree score ##
    safety_score = np.zeros(shape=[len(list(safety_degree.__members__.values()))]) ##

    ego_pos = ego_vehicle.get_position()
    ego_linear = ego_vehicle.get_linear()
    ego_size = ego_vehicle.get_size()
    other_pos = road_obj.get_position()
    other_size = road_obj.get_size()
    other_linear = road_obj.get_linear()

    r_state = judge_relation_state(ego_pos, ego_size, other_pos, other_size)
    # print('r_state:', r_state)

    if r_state is relation_state.none_overlap:
        ttc_in_x, ttc_in_y = time_to_collision_2d(ego_pos, ego_linear, ego_size,
                                                  other_pos, other_linear, other_size)

        if ttc_in_x < 0. or ttc_in_y < 0.:
            ## safe ##
            return np.array([0., 0., 1.]), -1.
        else:
            vel_m_s = math.sqrt(ego_linear[0] ** 2 + ego_linear[1] ** 2)  ## m/s
            vel_m_s_x = math.fabs(ego_linear[0])
            vel_m_s_y = math.fabs(ego_linear[1])

            c_t_thresh = (vel_m_s/COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[weather_type]
            c_t_thresh_x = (vel_m_s_x / COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                weather_type]
            c_t_thresh_y = (vel_m_s_y / COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                weather_type]

            ## maybe unsafe ##
            if much_bigger(math.fabs(ttc_in_y - ttc_in_x), c_t_thresh):
                ## safe ##
                return np.array([0., 0., 1.]), -1.
            elif much_bigger(ttc_in_x, c_t_thresh_x) and much_bigger(ttc_in_y, c_t_thresh_y):
                ## safe ##
                return np.array([0., 0., 1.]), -1.
            else:
                ## safe, attentive or dagerous ##
                ##  we use the speedofometer to calculate the threshold of different safety degree
                # vel_km_h = vel_m_s / .27777 ## km/h
                # thresh_w = np.array(safe_distance_config[weather_type])*vel_km_h/vel_m_s  ##add safe_distance factor and weatehr factor
                # thresh_ratio_r = road_state_ratio[road_state]       ##add road
                # thresh = thresh_w*thresh_ratio_r  ##add

                thresh = [(vel_m_s/(MAX_DECELERATION*road_state_ratio[road_state])*weather_effect_ratio[weather_type]),
                          (vel_m_s/(AVG_DECELERATION*road_state_ratio[road_state])*weather_effect_ratio[weather_type]),
                          (vel_m_s/(COMFORTABLE_DECELERATION*road_state_ratio[road_state])*weather_effect_ratio[weather_type])]

                dangerous_score = gaussian_1d(x=max(ttc_in_y, ttc_in_x),
                                              mean=thresh[0], for_what=safety_degree.dangerous,
                                              std=(vel_m_s*3.6)/20, max=(vel_m_s*3.6)/2)
                attentive_score = gaussian_1d(x=max(ttc_in_y, ttc_in_x),
                                              mean=thresh[1], for_what=safety_degree.attentive,
                                              std=(vel_m_s*3.6)/20, max=(vel_m_s*3.6)/2)
                safe_score = gaussian_1d(x=max(ttc_in_y, ttc_in_x),
                                         mean=thresh[2], for_what=safety_degree.safe,
                                         std=(vel_m_s*3.6)/20, max=(vel_m_s*3.6)/2)

                return softmax(np.array([dangerous_score, attentive_score, safe_score])), -1.

    elif r_state is relation_state.overlap_in_x:
        tte_in_x = time_to_escape_2d(ego_pos, ego_linear, ego_size,
                                     other_pos, other_linear, other_size, dim='x')
        ttc_in_y = time_to_collision_2d(ego_pos, ego_linear, ego_size,
                                     other_pos, other_linear, other_size, dim='y')

        try:
            assert tte_in_x >= 0.
        except AssertionError:
            logger.warning('Get a negative tte val(%f) in x, something may be wrong!'%(tte_in_x))

        if ttc_in_y < 0.:
            ## safe ##
            return np.array([0., 0., 1.]), -1.
        else:
            vel_m_s = math.sqrt(ego_linear[0] ** 2 + ego_linear[1] ** 2)  ## m/s
            vel_m_s_x = math.fabs(ego_linear[0] - other_linear[1])
            vel_m_s_y = math.fabs(ego_linear[1] - other_linear[1])

            # if much_bigger(ttc_in_y - tte_in_x,
            #                vel_m_s/(COMFORTABLE_DECELERATION*road_state_ratio[road_state]*weather_effect_ratio[weather_type])):
            #     ## safe ##
            #     return np.array([0., 0., 1.])
            if tte_in_x >= ttc_in_y:
                if much_bigger(ttc_in_y,
                               (vel_m_s_y / ((COMFORTABLE_DECELERATION * road_state_ratio[road_state])) \
                                            * weather_effect_ratio[weather_type])):
                    ## safe ##
                    return np.array([0., 0., 1.]), ttc_in_y
                else:
                    ## safe, attentive, dangerous ##
                    thresh = [
                        (vel_m_s_y / (MAX_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[weather_type]),
                        (vel_m_s_y / (AVG_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[weather_type]),
                        (vel_m_s_y / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                            weather_type])]

                    dangerous_score = gaussian_1d(x=ttc_in_y,
                                                  mean=thresh[0], for_what=safety_degree.dangerous,
                                                  std=(vel_m_s_y * 3.6) / 20, max=(vel_m_s_y * 3.6) / 2)
                    attentive_score = gaussian_1d(x=ttc_in_y,
                                                  mean=thresh[1], for_what=safety_degree.attentive,
                                                  std=(vel_m_s_y * 3.6) / 20, max=(vel_m_s_y * 3.6)  / 2)
                    safe_score = gaussian_1d(x=ttc_in_y,
                                             mean=thresh[2], for_what=safety_degree.safe,
                                             std=(vel_m_s_y * 3.6) / 20, max=(vel_m_s_y * 3.6)  / 2)

                    return softmax(np.array([dangerous_score, attentive_score, safe_score])), ttc_in_y
            else:
                if much_bigger(ttc_in_y,
                               (vel_m_s_y / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) \
                                            * weather_effect_ratio[weather_type])):
                    ## safe ##
                    return np.array([0., 0., 1.]), ttc_in_y
                else:
                    thresh = [(vel_m_s / (
                                MAX_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[weather_type]),
                              (vel_m_s / (AVG_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                                  weather_type]),
                              (vel_m_s / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                                  weather_type])]

                    ## safe attentive dangerous ##
                    dangerous_score = gaussian_1d(x=math.fabs(ttc_in_y - tte_in_x),
                                                  mean=thresh[0], for_what=safety_degree.dangerous,
                                                  std=(vel_m_s * 3.6) / 20, max=(vel_m_s * 3.6) / 2)
                    attentive_score = gaussian_1d(x=math.fabs(ttc_in_y - tte_in_x),
                                                  mean=thresh[1], for_what=safety_degree.attentive,
                                                  std=(vel_m_s * 3.6) / 20, max=(vel_m_s * 3.6) / 2)
                    safe_score = gaussian_1d(x=math.fabs(ttc_in_y - tte_in_x),
                                             mean=thresh[2], for_what=safety_degree.safe,
                                             std=(vel_m_s * 3.6) / 20, max=(vel_m_s * 3.6) / 2)
                    return softmax(np.array([dangerous_score, attentive_score, safe_score])), ttc_in_y

    elif r_state is relation_state.overlap_in_y:
        tte_in_y = time_to_escape_2d(ego_pos, ego_linear, ego_size,
                                     other_pos, other_linear, other_size, dim='y')
        ttc_in_x = time_to_collision_2d(ego_pos, ego_linear, ego_size,
                                        other_pos, other_linear, other_size, dim='x')

        try:
            assert tte_in_y >= 0.
        except AssertionError:
            logger.warning('Get a negative tte val(%f) in y, something may be wrong!' % (tte_in_y))

        if ttc_in_x < 0.:
            ## safe ##
            return np.array([0., 0., 1.]), ttc_in_x
        else:
            vel_m_s = math.sqrt(ego_linear[0] ** 2 + ego_linear[1] ** 2)  ## m/s
            vel_m_s_x = math.fabs(ego_linear[0] - other_linear[0])
            vel_m_s_y = math.fabs(ego_linear[1] - other_linear[0])

            # if much_bigger(ttc_in_y - tte_in_x,
            #                vel_m_s/(COMFORTABLE_DECELERATION*road_state_ratio[road_state]*weather_effect_ratio[weather_type])):
            #     ## safe ##
            #     return np.array([0., 0., 1.])
            if tte_in_y >= ttc_in_x:
                if much_bigger(ttc_in_x,
                               (vel_m_s_x / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) \
                                            * weather_effect_ratio[weather_type])):
                    ## safe ##
                    return np.array([0., 0., 1.]), ttc_in_x
                else:
                    ## safe, attentive, dangerous ##
                    thresh = [
                        (vel_m_s_x / (MAX_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                            weather_type]),
                        (vel_m_s_x / (AVG_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                            weather_type]),
                        (vel_m_s_x / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                            weather_type])]

                    dangerous_score = gaussian_1d(x=ttc_in_x,
                                                  mean=thresh[0], for_what=safety_degree.dangerous,
                                                  std=(vel_m_s_x * 3.6) / 20, max=(vel_m_s_x * 3.6) / 2)
                    attentive_score = gaussian_1d(x=ttc_in_x,
                                                  mean=thresh[1], for_what=safety_degree.attentive,
                                                  std=(vel_m_s_x * 3.6) / 20, max=(vel_m_s_x * 3.6) / 2)
                    safe_score = gaussian_1d(x=ttc_in_x,
                                             mean=thresh[2], for_what=safety_degree.safe,
                                             std=(vel_m_s_x * 3.6) / 20, max=(vel_m_s_x * 3.6) / 2)

                    return softmax(np.array([dangerous_score, attentive_score, safe_score])), ttc_in_x
            else:
                if much_bigger(ttc_in_x,
                               (vel_m_s_x / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) \
                                            * weather_effect_ratio[weather_type])):
                    ## safe ##
                    return np.array([0., 0., 1.]),ttc_in_x
                else:
                    thresh = [(vel_m_s / (
                            MAX_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[weather_type]),
                              (vel_m_s / (AVG_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                                  weather_type]),
                              (vel_m_s / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                                  weather_type])]

                    ## safe attentive dangerous ##
                    dangerous_score = gaussian_1d(x=math.fabs(ttc_in_x - tte_in_y),
                                                  mean=thresh[0], for_what=safety_degree.dangerous,
                                                  std=(vel_m_s * 3.6) / 20, max=(vel_m_s * 3.6) / 2)
                    attentive_score = gaussian_1d(x=math.fabs(ttc_in_x - tte_in_y),
                                                  mean=thresh[1], for_what=safety_degree.attentive,
                                                  std=(vel_m_s * 3.6) / 20, max=(vel_m_s * 3.6) / 2)
                    safe_score = gaussian_1d(x=math.fabs(ttc_in_x - tte_in_y),
                                             mean=thresh[2], for_what=safety_degree.safe,
                                             std=(vel_m_s * 3.6) / 20, max=(vel_m_s * 3.6) / 2)
                    return softmax(np.array([dangerous_score, attentive_score, safe_score])), ttc_in_x
    elif r_state is relation_state.all_overlap:
        ## length ##
        ego_vehicle_radius = math.sqrt((ego_size[1] / 2) ** 2 + (ego_size[2] / 2) ** 2)
        other_obj_radius = math.sqrt((other_size[1] / 2) ** 2 + (other_size[2] / 2) ** 2)
        length = ego_vehicle_radius + other_obj_radius

        ## pos_vec ##
        ego_pos_vec = np.array([ego_pos[0], ego_pos[1]])
        other_pos_vec = np.array([other_pos[0], other_pos[1]])
        pos_vec = ego_pos_vec - other_pos_vec
        pos_scalar = np.sqrt(np.sum(np.square(pos_vec)))

        ## vel_vec ##
        ego_vel_vec = np.array([ego_linear[0], ego_linear[1]])
        other_vel_vec = np.array([other_linear[0], other_linear[1]])
        vel_vex =  other_vel_vec - ego_vel_vec
        vel_scalar = np.sqrt(np.sum(np.square(vel_vex)))
        ego_vel_scalar = np.sqrt(np.sum(np.square(ego_vel_vec))) ## m/s

        cos = cos_vec(pos_vec, vel_vex)

        if cos <= 0.:
            ##means the distance between ego and other obj would increase.
            return np.array([0., 0., 1.]), -1
        else:
            ##means the distance between ego and other obj would decrease.
            vel_rel = vel_scalar * cos
            ttc = (pos_scalar - length) / vel_rel

            ## safe, attentive, dangerous ##
            thresh = [
                (ego_vel_scalar / (MAX_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                    weather_type]),
                (ego_vel_scalar / (AVG_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                    weather_type]),
                (ego_vel_scalar / (COMFORTABLE_DECELERATION * road_state_ratio[road_state]) * weather_effect_ratio[
                    weather_type])]

            dangerous_score = gaussian_1d(x=ttc,
                                          mean=thresh[0], for_what=safety_degree.dangerous,
                                          std=(ego_vel_scalar * 3.6) / 20, max=(ego_vel_scalar * 3.6) / 2)
            attentive_score = gaussian_1d(x=ttc,
                                          mean=thresh[1], for_what=safety_degree.attentive,
                                          std=(ego_vel_scalar * 3.6) / 20, max=(ego_vel_scalar * 3.6) / 2)
            safe_score = gaussian_1d(x=ttc,
                                     mean=thresh[2], for_what=safety_degree.safe,
                                     std=(ego_vel_scalar * 3.6) / 20, max=(ego_vel_scalar * 3.6) / 2)

            return softmax(np.array([dangerous_score, attentive_score, safe_score])), ttc

    else:
        raise ValueError("Imposible get here!!! Something must wrong!!!")

if __name__ == '__main__':
    ego_v_state = ego_v.ego_vehicle()
    ego_v_state.set_position(position=(0., 0., 0.))
    ego_v_state.set_linear(linear=(15., 1., 0.))
    ego_v_state.set_size(size=(1.3, 1.5, 4.))

    road_obj_state = road_o.road_obj()
    road_obj_state.set_position(position=(50., 0., 0.))
    road_obj_state.set_linear(linear=(1., 0., 0.))
    road_obj_state.set_size(size=(1.3, 1.5, 4.))

    safety_degree, ttc = _assess_one_obj_safety(ego_vehicle=ego_v_state, road_obj=road_obj_state)
    pass


import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import os

def plot_data(data, value="MeanReward100Episodes", save_name="results", title="Pong"):
    if isinstance(data, list):
        data = pd.concat(data, ignore_index=True)

    plt.figure()
    # sns.set(style="darkgrid", font_scale=1.5)
    sns.tsplot(data=data, time="Timestep", value=value, unit="Unit", condition="Condition", lw=2, alpha=1)
    plt.xlabel('Time step', fontsize=20)
    plt.ylabel('Average score', fontsize=20)
    plt.legend(loc='best', fontsize=15).draggable()
    plt.title(title, fontsize=20)
    ax = plt.axes()
    ax.tick_params(direction='in')
    plt.grid()
    plt.tick_params(labelsize=15)
    plt.show()
    plt.savefig(
        os.path.join('results/final', save_name + '.pdf'),
        bbox_inches='tight',
        transparent=True,
        pad_inches=0.1
    )

def get_datasets(fpath, condition=None):
    unit = 0
    datasets = []
    for root, dir, files in os.walk(fpath):
        if 'log.txt' in files:
            log_path = os.path.join(root, 'log.txt')
            experiment_data = pd.read_csv(log_path, sep='\t')

            experiment_data.insert(
                len(experiment_data.columns),
                'Unit',
                unit
                )
            experiment_data.insert(
                len(experiment_data.columns),
                'Condition',
                condition
                )

            datasets.append(experiment_data)
            unit += 1

    return datasets

def main():
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('logdir', nargs='*')
    parser.add_argument('--legend', nargs='*')
    parser.add_argument('--value', default='MeanReward100Episodes', nargs='*')
    parser.add_argument('--save_name', type=str, default='results')
    parser.add_argument('--title', type=str, default='Pong')
    args = parser.parse_args()

    if not os.path.exists('results'):
        os.makedirs('results')

    use_legend = False
    if args.legend is not None:
        assert len(args.legend) == len(args.logdir), \
            "Must give a legend title for each set of experiments."
        use_legend = True

    data = []
    if use_legend:
        for logdir, legend_title in zip(args.logdir, args.legend):
            data += get_datasets(logdir, legend_title)
    else:
        for logdir in args.logdir:
            data += get_datasets(logdir)

    if isinstance(args.value, list):
        values = args.value
    else:
        values = [args.value]
    for value in values:
        plot_data(data, value=value, save_name=args.save_name, title=args.title)

if __name__ == "__main__":
    main()


#!/usr/bin/env python
# coding: utf-8

# In[2]:

import pandas as pd
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00267/data_banknote_authentication.txt'
columns = ['variance of Wavelet Transformed image', 'skewness of Wavelet Transformed image', 'curtosis of Wavelet Transformed image', 'entropy of image', 'target']
data = pd.read_csv(url, names = columns)
data

# In[3]:

data[data['target'] == 0].describe()

# In[12]:

from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
x = data.iloc[:,:-1]
y = data.iloc[:, -1]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, random_state = 1)

# In[18]:

svm = SVC(kernel='rbf')
svm.fit(x_train, y_train)
prediction = svm.predict(x_test)

# In[22]:

from sklearn.metrics import plot_confusion_matrix, classification_report
import matplotlib.pyplot as plt
color = 'black'
cm = plot_confusion_matrix(svm, x_test, y_test, cmap=plt.cm.Blues)
plt.title("Confusion matrix")
plt.xlabel("Predicted value", color = color)
plt.ylabel("Actual value", color = color)
plt.show()

# In[24]:

cm = confusion_matrix(y_test, prediction)
cm

# In[21]:

report = classification_report(y_test, prediction)
print(report)

# In[26]:

from sklearn.metrics import roc_curve, roc_auc_score, auc

# 在各種『決策門檻』（decision threshold）下，計算 『真陽率』（True Positive Rate；TPR）與『假陽率』（False Positive Rate；FPR）
fpr, tpr, threshold = roc_curve(y_test, prediction)

auc = auc(fpr, tpr)
## Plot the result
plt.title('Receiver Operating Characteristic')
plt.plot(fpr, tpr, color = 'orange', label = 'AUC = %0.2f' % auc)
plt.legend(loc = 'lower right')
plt.plot([0, 1], [0, 1],'r--')
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show()

# In[ ]: