from botorch.models import SingleTaskGP
from gpytorch.mlls import ExactMarginalLogLikelihood
from botorch.fit import fit_gpytorch_model
import torch
import pytest
from greattunes._best_response import _find_max_response_value, _update_max_response_value

def test_find_max_response_value_unit(custom_models_simple_training_data_4elements,
                                      tmp_best_response_class):
    """
    test finding of max response value
    """

    # data -- max at 4th element
    train_X = custom_models_simple_training_data_4elements[0]
    train_Y = custom_models_simple_training_data_4elements[1]

    # test class for all methods from greattunes._best_response --- only an infrastructure ease for
    # _find_max_reponse_value since it's a static method
    cls = tmp_best_response_class

    max_X, max_Y = cls._find_max_response_value(train_X, train_Y)

    # assert that max value is at index 3 (4th element)
    maxit = 3
    assert max_X[0].item() == train_X[maxit].item()
    assert max_Y[0].item() == train_Y[maxit].item()

def test_find_max_response_value_multivariate(training_data_covar_complex, tmp_best_response_class):
    """
    test that the right covariate row tensor is returned when train_X is multivariate
    """

    # data
    train_X = training_data_covar_complex[1]
    train_Y = training_data_covar_complex[2]

    # test class for all methods from greattunes._best_response --- only an infrastructure ease for
    # _find_max_reponse_value since it's a static method
    cls = tmp_best_response_class

    max_X, max_Y = cls._find_max_response_value(train_X, train_Y)

    # assert that max value is at index 1 (2nd element)
    maxit = 1
    for it in range(train_X.shape[1]):
        assert max_X[0,it].item() == train_X[maxit, it].item()
    assert max_Y[0].item() == train_Y[maxit].item()

def test_update_max_response_value_unit(custom_models_simple_training_data_4elements, tmp_best_response_class,
                                        custom_models_simple_training_data_4elements_covar_details, monkeypatch):
    """
    test that _update_max_response_value works for univariate data
    """

    # data -- max at 4th element
    train_X = custom_models_simple_training_data_4elements[0]
    train_Y = custom_models_simple_training_data_4elements[1]
    covar_details = custom_models_simple_training_data_4elements_covar_details[0]
    covar_mapped_names = custom_models_simple_training_data_4elements_covar_details[1]

    # test class
    cls = tmp_best_response_class

    # set some attributes
    cls.train_X = train_X
    cls.proposed_X = train_X
    cls.train_Y = train_Y
    cls.covar_details = covar_details
    cls.covar_mapped_names = covar_mapped_names

    # monkeypatch
    max_X = 1.0
    max_Y = 2.0
    def mock_find_max_response_value(train_X, train_Y):
        mX = torch.tensor([[max_X]], dtype=torch.double,
                          device=torch.device("cuda" if torch.cuda.is_available() else "cpu"))
        mY = torch.tensor([[max_Y]], dtype=torch.double,
                          device=torch.device("cuda" if torch.cuda.is_available() else "cpu"))
        return mX, mY
    monkeypatch.setattr(
        cls, "_find_max_response_value", mock_find_max_response_value
    )

    # test that it initializes storage of best results when none present
    cls._update_max_response_value()

    assert cls.covars_best_response_value[0].item() == max_X
    assert cls.best_response_value[0].item() == max_Y

@pytest.mark.parametrize(
    "response_sampled_iter, covars_best, response_best",
    [
        [3, [1.1], [2.2]],
        [10, [3.5, 12.2], [1.7]]
    ]
)
def test_current_best_univariate_unit(tmp_best_response_class, response_sampled_iter, covars_best, response_best):
    """
    test that current_best works for univariate and multivariate covariate data
    """

    # class used for test
    cls = tmp_best_response_class

    # set attribute
    cls.model = {"response_sampled_iter": response_sampled_iter}
    cls.covar_details = {}
    for i in range(len(covars_best)):
        tmp_key = "covar" + str(i)
        cls.covar_details[tmp_key] = {"guess": covars_best[i],
                                      "min": covars_best[i]-1,
                                      "max": covars_best[i]+1,
                                      "type": float,
                                      "columns": i,
                                      "pandas_column": i}
    cls.total_num_covars = len(covars_best)

    # set best response variables
    cls.covars_best_response_value = torch.tensor([covars_best], dtype=torch.double)
    cls.best_response_value = torch.tensor([response_best], dtype=torch.double)

    # run method
    cls.current_best()

    # assert
    for it in range(len(covars_best)):
        assert cls.best["covars"].values[0][it] == covars_best[it]
    assert cls.best["response"].values[0][0] == response_best[0]
    assert cls.best["iteration_when_recorded"] == response_sampled_iter

@pytest.mark.parametrize(
    "candidate, proposed_X, covars_sampled_iter",
    [
        [torch.tensor([[1]], dtype=torch.double), torch.tensor([[0.9]], dtype=torch.double), 1],  # univariate
        [torch.tensor([[1, 2, 3]], dtype=torch.double), torch.tensor([[0.9, 1.1, 200]], dtype=torch.double), 1],  # multivariate
        [torch.tensor([[1, 2, 3]], dtype=torch.double), torch.tensor([[0.9, 1.1, 200], [0, 1, 2]], dtype=torch.double), 2]  # multivariate and several historical entries
    ]
)
def test_update_proposed_data_works(tmp_best_response_class, candidate, proposed_X, covars_sampled_iter):
    """
    positive tests for the cases where "_update_proposed_data" works
    :param: tmp_best_response_class (test class defined in conftest.py)
    """

    # initialize temp class for running test
    cls = tmp_best_response_class

    # update attributes to run test
    cls.model = {
        "covars_sampled_iter": covars_sampled_iter,
        "covars_proposed_iter": 0,  # can initialize at any value, is updated relative to "covars_sampled_iter"
    }
    cls.proposed_X = proposed_X

    # run the test
    cls._update_proposed_data(candidate=candidate)

    # assert that cls.proposed_X has grown by one row
    assert cls.proposed_X.size()[0] == proposed_X.size()[0] + 1

    # assert content of that last row of cls.proposed_X
    for i in range(cls.proposed_X.size()[1]):
        assert cls.proposed_X[-1, i].item() == candidate[0, i].item()

@pytest.mark.parametrize(
    "candidate, proposed_X, covars_sampled_iter",
    [
        [torch.tensor([[1]], dtype=torch.double), torch.tensor([[0.9], [1.1]], dtype=torch.double), 1],
        [torch.tensor([[1, 2, 3]], dtype=torch.double), torch.tensor([[0.9, 1.1, 200], [1.1, 2.2, 4.3]], dtype=torch.double), 1],
        [torch.tensor([[1, 2, 3]], dtype=torch.double), torch.tensor([[0.9, 1.1, 200], [1.1, 2.2, 4.3], [0, 1, 2]], dtype=torch.double), 2]
    ]
)
def test_update_proposed_data_overwrite(tmp_best_response_class, candidate, proposed_X, covars_sampled_iter):
    """
    positive tests for the cases where "_update_proposed_data" overwrites last entry in self.proposed_X
    :param: tmp_best_response_class (test class defined in conftest.py)
    """

    # initialize temp class for running test
    cls = tmp_best_response_class

    # update attributes to run test
    cls.model = {
        "covars_sampled_iter": covars_sampled_iter,
        "covars_proposed_iter": 0,  # can initialize at any value, is updated relative to "covars_sampled_iter"
    }
    cls.proposed_X = proposed_X

    # run the test
    cls._update_proposed_data(candidate=candidate)

    # assert that cls.proposed_X has NOT grown
    assert cls.proposed_X.size()[0] == proposed_X.size()[0]

    # assert content of that last row of cls.proposed_X
    for i in range(cls.proposed_X.size()[1]):
        assert cls.proposed_X[-1, i].item() == candidate[0, i].item()

@pytest.mark.parametrize(
    "candidate", [torch.tensor([[1]], dtype=torch.double), torch.tensor([[1, 2, 3]], dtype=torch.double)]
)
def test_update_proposed_data_works_firs_entry(tmp_best_response_class, candidate):
    """
    positive tests for the cases where "_update_proposed_data" stores first set of candidate datapoints
    :param: tmp_best_response_class (test class defined in conftest.py)
    """

    # initialize temp class for running test
    cls = tmp_best_response_class

    # update attributes to run test. self.proposed_X is initialized as 'None' in conftest.py
    cls.model = {
        "covars_sampled_iter": 0,  # initialize at 0 for the test to work (no data points sampled yet)
        "covars_proposed_iter": 1,  # initialize at 1 since one datapoint has been proposed (can actually initialize at any value for _update_proposed_data to work)
    }

    # run the test
    cls._update_proposed_data(candidate=candidate)

    # assert that only a single candidate datapoint has been added
    assert cls.proposed_X.size()[0] == 1

    # assert that it's the right datapoint by comparing to 'candidate'
    for i in range(cls.proposed_X.size()[1]):
        assert cls.proposed_X[-1, i].item() == candidate[0, i].item()

@pytest.mark.parametrize(
    "candidate, proposed_X, error_msg",
    [
        [None, torch.tensor([[0.9], [1.1]], dtype=torch.double), "greattunes._best_response._update_proposed_data: provided variable is not 'candidate' of type torch.DoubleTensor (type of 'candidate' is " + str(type(None)) + ")"],
        [[1.1], torch.tensor([[0.9], [1.1]], dtype=torch.double), "greattunes._best_response._update_proposed_data: provided variable is not 'candidate' of type torch.DoubleTensor (type of 'candidate' is " + str(type([1.1])) + ")"],
        [torch.tensor([[1, 2]], dtype=torch.double), torch.tensor([[0.9], [1.1]], dtype=torch.double), "greattunes._best_response._update_proposed_data: wrong number of covariates provided in 'candidate'. Expected 1, but got 2"],
        [torch.tensor([[1, 2]], dtype=torch.double), torch.tensor([[0.9, 1.1, 200], [1.1, 2.2, 4.3], [0, 1, 2]], dtype=torch.double), "greattunes._best_response._update_proposed_data: wrong number of covariates provided in 'candidate'. Expected 3, but got 2"]
    ]
)
def test_update_proposed_data_fails(tmp_best_response_class, candidate, proposed_X, error_msg):
    """
    negative tests for "_update_proposed_data": wrong data types, wrong number of candidates
    :param: tmp_best_response_class (test class defined in conftest.py)
    """

    # initialize temp class for running test
    cls = tmp_best_response_class

    # update attributes to run test. self.proposed_X is initialized as 'None' in conftest.py
    cls.model = {
        "covars_sampled_iter": 0,  # initialize at 0 for the test to work (no data points sampled yet)
        "covars_proposed_iter": 1,
        # initialize at 1 since one datapoint has been proposed (can actually initialize at any value for _update_proposed_data to work)
    }
    cls.proposed_X = proposed_X

    # run the test
    with pytest.raises(AssertionError) as e:
        cls._update_proposed_data(candidate=candidate)
    assert str(e.value) == error_msg

@pytest.mark.parametrize(
    "maximize, result",
    [
        [True, 1],
        [False, 0],
    ]
)
def test_find_best_predicted_1d(tmp_best_response_class, monkeypatch, maximize, result):
    """"
    tests that the optimizing method indeed finds the maximum. Monkeypatching the function evaluation step
    """

    # defines temporary class
    tmp = tmp_best_response_class

    # add some attributes to tmp
    tmp.dtype = torch.double
    tmp.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # add function to maximize
    def f(type, x):
        return torch.sin(x**2)

    monkeypatch.setattr(tmp, "_evaluate_model", f)

    # initialize x and type (ignore type in this test)
    #x_init = torch.rand(1).requires_grad_(True)
    x_init = torch.rand(1, 1, dtype=torch.double)
    type = "mean"

    # run the method
    y, x = tmp._find_best_predicted(x_init, type, maximize=maximize, max_iter=20)

    # assert
    assert abs(y-result) < 1e-3

@pytest.mark.parametrize(
    "maximize, result",
    [
        [True, 1],
        [False, 0],
    ]
)
def test_find_best_predicted_2d(tmp_best_response_class, monkeypatch, maximize, result):
    """"
    tests that the optimizing method indeed finds the maximum. Monkeypatching the function evaluation step
    """

    # defines temporary class
    tmp = tmp_best_response_class

    # add some attributes to tmp
    tmp.dtype = torch.double
    tmp.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    # add function to maximize
    def f(type, x):
        return (torch.sin(x[:,0]**2)*torch.sin(x[:,1]**2)).unsqueeze(1)

    monkeypatch.setattr(tmp, "_evaluate_model", f)

    # initialize x and type (ignore type in this test)
    #x_init = torch.rand(1, 2).requires_grad_(True)
    x_init = torch.rand(1, 2, dtype=torch.double)
    type = "mean"

    # run the method
    y,x = tmp._find_best_predicted(x_init, type, maximize=maximize, max_iter=20)

    # assert
    assert abs(y-result) < 1e-3

@pytest.mark.parametrize(
    "type, result",
    [
        ["mean", 1.2500295534805428],
        ["lower_conf", -2.440432203804888],
    ]
)
def test_evaluate_model(type, result):
    """
    test that method '_evaluate_model' works
    """

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    dtype = torch.double

    # define training data
    train_X = torch.tensor([[1, 2, 3], [1, 2, 3]], dtype=dtype, device=device)
    train_Y = torch.tensor([[1], [1.5]], dtype=dtype, device=device)

    # define and train model
    mymodel = SingleTaskGP(train_X=train_X, train_Y=train_Y)
    ll = ExactMarginalLogLikelihood(mymodel.likelihood, mymodel)
    fit_gpytorch_model(ll)

    # construct temporary class
    class Tmp:
        def __init__(self, mymodel):
            self.model = {"model": mymodel}

        from greattunes._best_response import _evaluate_model

    cls = Tmp(mymodel)

    # data point to investigate
    x = torch.tensor([[2, 3, 4]], dtype=dtype, device=device)

    # run method
    y = cls._evaluate_model(type=type, x=x)

    # assert
    assert isinstance(y, torch.DoubleTensor)

    ERROR_LIM = 1e-8
    assert abs(y.item()-result) <= ERROR_LIM


import datetime

from django.contrib import admin

from aerolinksma.shuttle.models import Place, Client, Reservation

@admin.register(Place)
class PlaceAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'get_display_time', 'display_price', 'enabled')
    list_filter = ('enabled',)
    actions = ('make_enabled', 'make_disabled')

    def get_display_time(self, obj):
        """Display time readibly."""
        return '{} ({})'.format(datetime.timedelta(hours=obj.time),
                                obj.time)
    get_display_time.short_description = 'Time (h:m:s)'
    get_display_time.admin_order_field = 'time'

    def make_enabled(self, request, queryset):
        rows_updated = queryset.update(enabled=True)

        if rows_updated == 1:
            message_bit = '1 place was'
        else:
            message_bit = '{} places were'.format(rows_updated)
        self.message_user(request, '{} enabled'.format(message_bit))
    make_enabled.short_description = 'Enable selected places'
    make_enabled.allowed_permissions = ('change',)

    def make_disabled(self, request, queryset):
        rows_updated = queryset.update(enabled=False)

        if rows_updated == 1:
            message_bit = '1 place was'
        else:
            message_bit = '{} places were'.format(rows_updated)
        self.message_user(request, '{} disabled'.format(message_bit))
    make_disabled.short_description = 'Disable selected places'
    make_disabled.allowed_permissions = ('change',)

@admin.register(Reservation)
class ReservationAdmin(admin.ModelAdmin):
    list_display = ('id', 'place', 'direction', 'fare_type',
                    'paid', 'pickup_date', 'created_at')

@admin.register(Client)
class ClientAdmin(admin.ModelAdmin):
    list_display = ('id', 'first_name', 'last_name', 'email',
                    'phone_number')


""" syndat.py """
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

np.random.seed(1234)
X=np.pi*(2* np.random.random (size =(400 ,2)) -1)
y=(np.cos(X[: ,0])*np.sin(X[: ,1]) >=0)
X_train , X_test , y_train , y_test = train_test_split(X, y, test_size =0.5)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter( X_train [ y_train ==0 ,0] , X_train [ y_train ==0 ,1] , c='g',
marker ='o',alpha =0.5)
ax.scatter ( X_train [ y_train ==1 ,0] , X_train [ y_train ==1 ,1] , c='b',
marker ='o',alpha =0.5)
ax.scatter ( X_test [ y_test ==0 ,0] , X_test [ y_test ==0 ,1] , c='g',
marker ='s',alpha =0.5)
ax.scatter ( X_test [ y_test ==1 ,0] , X_test [ y_test ==1 ,1] , c='b',
marker ='s',alpha =0.5)

# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
plt.savefig('sklearntraintest.pdf ',format ='pdf')
plt.show()
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


from rest_framework import serializers
from .models import *
from lyka_products.serializers import ProductRetriveSerializer, ColorSerializer, VariationsSerializer
from lyka_address.serializers import CustomerAddressRetriveSerializer, SellerStoreAddressRetriveSerializer
from lyka_payment.serializers import CouponSerializer
import uuid
from lyka_seller.serializers import SellerBusinessNameSerializer

class OrderCredentialsSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderCredentials
        fields = "__all__"

class OrderItemsSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderItems
        fields = "__all__"

class OrderSerializer(serializers.ModelSerializer):
    order_id = serializers.UUIDField(read_only=True)
    class Meta:
        model = Order
        fields = ["order_id"]

    def create(self, validated_data):
        o_id = uuid.uuid1()
        order = Order.objects.create(order_id=o_id)
        return order

class OrderGroupSerializer(serializers.ModelSerializer):
    order_list_id = serializers.UUIDField(read_only=True)
    class Meta:
        model = OrderGroup
        fields = ["order_list_id"]

    def create(self, validated_data):
        o_l_id = uuid.uuid1()
        order = OrderGroup.objects.create(order_list_id=o_l_id)
        return order

class OrderItemsRetriveSerializer(serializers.ModelSerializer):
    product = ProductRetriveSerializer()
    product_color = ColorSerializer()
    product_variant = VariationsSerializer()
    class Meta:
        model = OrderItems
        fields = "__all__"

class OrderRetriveSerializer(serializers.ModelSerializer):
    item = OrderItemsRetriveSerializer()
    shipping_address = CustomerAddressRetriveSerializer()
    seller = SellerBusinessNameSerializer()
    class Meta:
        model = Order
        fields = "__all__"

class OrderDetailsRetriveSerializer(serializers.ModelSerializer):
    item = OrderItemsRetriveSerializer()
    shipping_address = CustomerAddressRetriveSerializer()
    billing_address = CustomerAddressRetriveSerializer()
    credentials = OrderCredentialsSerializer()
    pickup_address = SellerStoreAddressRetriveSerializer()
    applied_coupon = CouponSerializer()
    class Meta:
        model = Order
        fields = "__all__"

class OrderGroupPriceSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderGroup
        fields = ["total_price", "total_selling_price", "additional_charges", "coupon_discount", "discount", "total_shipping_charge"]

class OrderItemsPriceSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderItems
        fields = ["product_price", "selling_price", "coupon_discount", "discount", "additional_charges"]

class OrderPriceSerializer(serializers.ModelSerializer):
    item = OrderItemsPriceSerializer()
    class Meta:
        model = Order
        fields = ["item", "shipping_charge"]


from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms

writer = SummaryWriter("logs")
img = Image.open("dataset/train/ants/460874319_0a45ab4d05.jpg")

# ToTensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
writer.add_image("Totensor", img_tensor)

# Normalize
print(img_tensor[0][0][0])
trans_norm = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
img_norm = trans_norm(img_tensor)
print(img_norm[0][0][0])
writer.add_image("normalize", img_norm)

# Resize
print(img.size)
trans_resize = transforms.Resize((512,512))
img_resize = trans_resize(img)
img_resize = trans_totensor(img_resize)
writer.add_image("resize", img_resize,0)

# compose
trans_compose = transforms.Compose([trans_resize,trans_totensor])
img_resize_2 = trans_compose(img)
writer.add_image("resize", img_resize_2, 1)

# randomcrop
trans_randomcrop = transforms.RandomCrop((300,200))
trans_compose_2 = transforms.Compose([trans_randomcrop, trans_totensor])
for i in range(10):
    img_crop = trans_compose_2(img)
    writer.add_image("randomHW", img_crop, i)

writer.close()


from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
from pysnmp.smi import builder, view, compiler, rfc1902
from discord_webhook import DiscordEmbed, DiscordWebhook
from datetime import datetime
from pymongo import MongoClient
import os
from dotenv import load_dotenv

# Assemble MIB browser
from pysmi import debug as pysmi_debug
pysmi_debug.setLogger(pysmi_debug.Debug('compiler'))

mibBuilder = builder.MibBuilder()
mibViewController = view.MibViewController(mibBuilder)
compiler.addMibCompiler(mibBuilder, sources=["file://./mibs",
                                             'http://mibs.snmplabs.com/asn1/@mib@',
                                             "http://mibs.snmplabs.com/pysnmp/fulltexts/@mib@"])
# mibdump.py  --mib-source ./mibs --mib-source http://mibs.snmplabs.com/asn1/@mib@ FORTINET-FORTIGATE-MIB
mibBuilder.loadModules('SNMPv2-MIB', 'SNMP-FRAMEWORK-MIB', 'SNMP-COMMUNITY-MIB', "IF-MIB",
                       "SNMPv2-CONF", "SNMPv2-SMI", "SNMPv2-TC", "CISCO-SMI", "CISCO-TC",
                       "IPV6-TC",
                       "HCNUM-TC",
                       "FORTINET-FORTIGATE-MIB",
                       "FORTINET-CORE-MIB")
snmpEngine = engine.SnmpEngine()
load_dotenv()
TrapAgentAddress = '0.0.0.0'  # Trap listerner address
Port = 162  # trap listerner port

print("Agent is listening SNMP Trap on " +
      TrapAgentAddress+" , Port : " + str(Port))
print('--------------------------------------------------------------------------')
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode((TrapAgentAddress, Port)),
)

config.addV1System(snmpEngine, 'cs1', 'cs1')

def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    execContext = snmpEngine.observer.getExecutionContext(
        'rfc3412.receiveMessage:request'
    )
    print(
        f"Received new Trap message from {execContext['transportAddress'][0]}")

    varBinds = [rfc1902.ObjectType(rfc1902.ObjectIdentity(x[0]), x[1]).resolveWithMib(mibViewController)
                for x in varBinds]
    snmp_trap = {
        "system_up_time_instance": "",
        "source_address": execContext['transportAddress'][0],
        "module_id": "",
        "message": ""
    }
    for i, (name, val) in enumerate(varBinds):
        oid = name.prettyPrint()
        value = val.prettyPrint()
        print(f"{oid} = {value}")
        if i == 0:
            snmp_trap["system_up_time_instance"] = value
        elif i == 1:
            snmp_trap["module_id"] = value
        else:
            if i != len(varBinds):
                if snmp_trap["message"]:
                    snmp_trap["message"] += "\n"
                snmp_trap["message"] += f"{oid} = {value}"
    client_ip = snmp_trap["source_address"]
    data = snmp_trap["message"]
    try:
        discordURL = os.environ["DISCORDURL"]
        webhook = DiscordWebhook(
            url=discordURL)
        embed = DiscordEmbed(
            title='SNMP Event', description=f'The following network device had a SNMP event', color='ab52fe')
        embed.set_author(name='NetBot',
                         icon_url='https://avatars0.githubusercontent.com/u/14542790')
        embed.set_footer(text='Network SNMP Event')
        embed.set_timestamp()
        embed.add_embed_field(
            name='Device', value=client_ip)
        embed.add_embed_field(
            name='Trap OID', value=snmp_trap["module_id"])
        embed.add_embed_field(
            name='Message', value=data, inline=False)
        webhook.add_embed(embed)
        webhook.execute()
        mongoURL = os.environ["MONGOURL"]
        snmp_trap["created_at"] = datetime.utcnow()
        client = MongoClient(f"mongodb://{mongoURL}/")
        logs = client["snmp"]
        source_address = snmp_trap["source_address"]
        log_table = logs[source_address]
        result = log_table.insert_one(snmp_trap)
        if result.acknowledged != True:
            print("Could not upload SNMP trap to DB")

        print(f"Posted to DB with id {result.inserted_id}\n{snmp_trap}")
    except Exception as e:
        print(e)

ntfrcv.NotificationReceiver(snmpEngine, cbFun)

snmpEngine.transportDispatcher.jobStarted(1)

try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise


import sys
import string
import random
from typing import TypedDict

class Individual(TypedDict):
    genome: str
    fitness: int

Population = list[Individual]

def initialize_individual(genome: str, fitness: int) -> Individual:
    """
    Purpose:        Create one individual
    Parameters:     genome as string, fitness as integer (higher better)
    User Input:     no
    Prints:         no
    Returns:        One Individual, as a dict[str, int]
    Modifies:       Nothing
    Calls:          Basic python only
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def initialize_pop(objective: str, pop_size: int) -> Population:
    """
    Purpose:        Create population to evolve
    Parameters:     Goal string, population size as int
    User Input:     no
    Prints:         no
    Returns:        a population, as a list of Individuals
    Modifies:       Nothing
    Calls:          random.choice, string.ascii_letters, initialize_individual
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def recombine_pair(parent1: Individual, parent2: Individual) -> Population:
    """
    Purpose:        Recombine two parents to produce two children
    Parameters:     Two parents as Individuals
    User Input:     no
    Prints:         no
    Returns:        One Individual, as a TypedDict[str, int]
    Modifies:       Nothing
    Calls:          Basic python, random.choice, initialize_individual
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def recombine_group(parents: Population, recombine_rate: float) -> Population:
    """
    Purpose:        Recombines a whole group, returns the new population
    Parameters:     genome as string, fitness as integer (higher better)
    User Input:     no
    Prints:         no
    Returns:        New population of children
    Modifies:       Nothing
    Calls:          Basic python, recombine pair
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def mutate_individual(parent: Individual, mutate_rate: float) -> Individual:
    """
    Purpose:        Mutate one individual
    Parameters:     One parents as Individual, mutation rate as float (0-1)
    User Input:     no
    Prints:         no
    Returns:        One Individual, as a TypedDict[str, int]
    Modifies:       Nothing
    Calls:          Basic python, initialize_individual
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def mutate_group(children: Population, mutate_rate: float) -> Population:
    """
    Purpose:        Mutates a whole Population, returns the mutated group
    Parameters:     Population, mutation rate as float (0-1)
    User Input:     no
    Prints:         no
    Returns:        One Individual, as a TypedDict[str, int]
    Modifies:       Nothing
    Calls:          Basic python, mutate_individual
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def evaluate_individual(objective: str, individual: Individual) -> None:
    """
    Purpose:        Computes and modifies the fitness for one individual
    Parameters:     Objective string, One Individual
    User Input:     no
    Prints:         no
    Returns:        None
    Modifies:       The individual (mutable object)
    Calls:          Basic python only
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def evaluate_group(objective: str, individuals: Population) -> None:
    """
    Purpose:        Computes and modifies the fitness for population
    Parameters:     Objective string, Population
    User Input:     no
    Prints:         no
    Returns:        None
    Modifies:       The Individuals, all mutable objects
    Calls:          Basic python, evaluate_individual
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def rank_group(individuals: Population) -> None:
    """
    Purpose:        Create one individual
    Parameters:     Population of Individuals
    User Input:     no
    Prints:         no
    Returns:        None
    Modifies:       The population's order (a mutable object)
    Calls:          Basic python only
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def parent_select(individuals: Population, number: int) -> Population:
    """
    Purpose:        Choose parents in direct probability to their fitness
    Parameters:     Population, the number of individuals to pick.
    User Input:     no
    Prints:         no
    Returns:        Sub-population
    Modifies:       Nothing
    Calls:          Basic python, random.choices (hint)
    Tests:          ./unit_tests/*
    Status:         Do this one!
    """
    print("Delete this and write your code")

def survivor_select(individuals: Population, pop_size: int) -> Population:
    """
    Purpose:        Picks who gets to live!
    Parameters:     Population, and population size to return.
    User Input:     no
    Prints:         no
    Returns:        Population, of pop_size
    Modifies:       Nothing
    Calls:          Basic python only
    Tests:          ./unit_tests/*
    Status:

    """
    print("Delete this and write your code")

def evolve(objective: str, pop_size: int) -> Population:
    """
    Purpose:        A whole EC run, main driver
    Parameters:     The evolved population of solutions
    User Input:     No
    Prints:         Updates every time fitness switches.
    Returns:        Population
    Modifies:       Various data structures
    Calls:          Basic python only, all your functions
    Tests:          ./stdio_tests/* and ./arg_tests/
    Status:         Giving you this one.
    """
    population = initialize_pop(objective=objective, pop_size=pop_size)
    evaluate_group(objective=objective, individuals=population)
    rank_group(individuals=population)
    best_fitness = population[0]["fitness"]
    perfect_fitness = len(objective)
    counter = 0
    while best_fitness < perfect_fitness:
        counter += 1
        parents = parent_select(individuals=population, number=80)
        children = recombine_group(parents=parents, recombine_rate=0.8)
        mutate_rate = (1 - (best_fitness / perfect_fitness)) / 5
        mutants = mutate_group(children=children, mutate_rate=mutate_rate)
        evaluate_group(objective=objective, individuals=mutants)
        everyone = population + mutants
        rank_group(individuals=everyone)
        population = survivor_select(individuals=everyone, pop_size=pop_size)
        if best_fitness != population[0]["fitness"]:
            best_fitness = population[0]["fitness"]
            print("Iteration number", counter, "with best individual", population[0])
    return population

if __name__ == "__main__":
    # Execute doctests to protect main:
    import doctest
    import json

    # This seeds, so can be commented for random runs
    doctest.testmod()
    if len(sys.argv) == 3:
        with open(file=sys.argv[1]) as finput:
            obj_name = finput.readlines()
            OBJECTIVE = obj_name[0].strip()
            POP_SIZE = int(obj_name[1])
        with open(file=sys.argv[2], mode="w") as foutput:
            population = evolve(OBJECTIVE, POP_SIZE)
            foutput.write(json.dumps(population) + "\n")
    else:
        OBJECTIVE = input("What string would you like to evolve?\n")
        POP_SIZE = int(input("How many individuals would you like to evolve?\n"))
        population = evolve(OBJECTIVE, POP_SIZE)


import h5py
import os
import argparse

import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.gridspec as gridspec
import matplotlib.cm as cm
from matplotlib import rcParams

import numpy as np
import scipy as sp
from scipy import optimize
from scipy.constants import pi, c, mu_0
import scipy.fftpack as fft

rcParams.update({'figure.autolayout': True})
rcParams.update({"font.size": 9})

# parser = argparse.ArgumentParser()
# parser.add_argument("location")
# args = parser.parse_args()

core_radius = 125e-6
T = 293.0

# sim_location = os.path.join("C:\\PhD\\Experiments\\HISOL\\SPM to soliton regime\\simulation", args.location)
# os.chdir(sim_location)

# try:
#     sim_location = os.path.join("C:\\Users\\Setaou\\Documents\\PhD\\Experiments\\HISOL\\SPM to soliton regime\\simulation\\spectrogram", args.location + "\\ionONshockON")
#     os.chdir(sim_location)
# except:
#     print("No such dir? Wrong dir?")

# try:
#     gas = args.location[:2]
#     pressure_mbar = int(((args.location).split(gas))[1].split("mbar")[0])
#     τfwhm_fs = int(((args.location).split("mbar"))[1].split("fs")[0])
# except:
#     print("File names are not in the appropriate notation, soz...")

def chrng(x):
    return (2*pi*c)/x

def ρ(p, T, p0=1.0, T0=273.0):
  ρ = (p/p0)*(T0/T)
  return ρ

def n_gas(λ, gas, p, T):
    if gas == "Ar":
        B = [20332.29e-8, 34458.31e-8]
        C = [206.12e-6, 8.066e-3]
    elif gas == "Kr":
        B = [26102.88e-8, 56946.82e-8]
        C = [2.01e-6, 10.043e-3]
    χ = B[0]*(λ*1e6)**2/((λ*1e6)**2 - C[0]) + B[1]*(λ*1e6)**2/((λ*1e6)**2 - C[1])
    ngas = sp.sqrt(1.0 + ρ(p, T)*χ)
    return ngas

def χ3(gas, p, T):
    if gas == "Ar":
        χ3 = 4.0*23.5*3.43e-28
    elif gas == "Kr":
        χ3 = 4.0*64.0*3.43e-28
    χ3gas = ρ(p, T)*χ3
    return χ3gas

def β_capillary(ω, gas, p, core_radius, T):
    β = ω/c*sp.sqrt(n_gas(chrng(ω), gas, p, T)**2 - ((2.4048**2)*(c**2)/((core_radius**2)*ω**2)))
    return β

def β2(ω, gas, p, core_radius, T):
    h = 1e13
    return (β_capillary(ω+h, gas, p, core_radius, T)- 2*β_capillary(ω, gas, p, core_radius, T) + β_capillary(ω-h, gas, p, core_radius, T))/(h**2)

def λzd(ω, β2, gas, p, core_radius, T):
    return chrng(sp.optimize.brentq(β2, chrng(200e-9), chrng(2000e-9), args=(gas, p, core_radius, T)))*1e9

def τ0(τfwhm):
    return τfwhm/(2*sp.log(1 + sp.sqrt(2)))

def L_dis(p, τfwhm, λpump, gas, core_radius, T):
    return τ0(τfwhm)**2/abs(β2(chrng(λpump), gas, p, core_radius, T))

def P_peak(E, τfwhm):
    return 0.88*E/τfwhm

def γ(gas, p, core_radius, λpump, T):
    return (3/4)*(chrng(λpump)*mu_0*χ3(gas, p, T))/(0.48*sp.pi*core_radius**2*n_gas(λpump, gas, p, T)**2)

def L_nl(gas, p, E, τfwhm, core_radius, λpump, T):
    return 1/(γ(gas, p, core_radius, λpump, T)*P_peak(E, τfwhm))

def Nsol(gas, p, E, τfwhm, λpump, core_radius, T):
    return sp.sqrt(L_dis(p, τfwhm, λpump, gas, core_radius, T)/L_nl(gas, p, E, τfwhm, core_radius, λpump, T))

def field_envelope(E):
    Eω = sp.fft(E)
    Eω[len(E)//2+1:] = 0.0
    Et = 2.0*sp.ifft(Eω)
    return Et

def browse_sims():
    files = os.listdir()
    sim_filenames = []
    energies = []
    for file in files:
        if file[:2] == gas:
            sim_filenames.append(file)
            energy = float((file.split("E="))[1].split("e-")[0])
            energy_mag = int((file.split("e-"))[1].split("_0000")[0])
            energies.append(energy*10**(-energy_mag))
    N = len(sim_filenames)
    return sim_filenames, N, energies

def load_sim(filename):
    sim = h5py.File(filename, "r")
    #z = np.array(sim["Z"])
    ω = np.array(sim["W"])
    ω_pivot = len(ω)
    t = np.array(sim["T"])
    E = np.array(sim["E"])
    steps = E.shape[0]
    modes = E.shape[1]
    Et = np.zeros((steps, modes, len(t)))
    for step in range(0, steps):
        for mode in range(0, modes):
            Et[step, mode, :] = abs(field_envelope(E[step][mode][:]))**2
    Eω = np.zeros((steps, modes, ω_pivot))
    for step in range(0, steps):
        for mode in range(0, modes):
            Eω[step, mode, :] = sp.real(abs(sp.fft(E[step][mode][:])[:ω_pivot])**2)
    energy_in = np.array(sim["input_energies"])[0]
    energy_out = np.array(sim["stats_energy_m"])[-1,0]
    energy = np.array([energy_in, energy_out])
    pressure = sim["gas_pressure"][()]
    τfwhm = np.array(sim["input_fwhms"])[0]
    sim.close()
    #return z, ω, t, Eω, Et, energy, pressure, τfwhm, modes
    return ω, t, Eω, Et, energy, pressure, τfwhm, modes

def load_sim_prop(filename):
    sim = h5py.File(filename, "r")
    z = np.array(sim["Z"])
    ω = np.array(sim["W"])
    ω_pivot = len(ω)
    t = np.array(sim["T"])
    E = np.array(sim["E"])
    steps = E.shape[0]
    modes = E.shape[1]
    Et = np.zeros((steps, modes, len(t)), dtype=np.cdouble)
    for step in range(0, steps):
        for mode in range(0, modes):
            Et[step, mode, :] = field_envelope(E[step][mode][:])
    Eω = np.zeros((steps, modes, ω_pivot), dtype=np.cdouble)
    for step in range(0, steps):
        for mode in range(0, modes):
            Eω[step, mode, :] = sp.fft(E[step][mode][:])[:ω_pivot]
    stats = {}
    pressure = sim["gas_pressure"][()]
    stats["pressure"] = pressure
    energy_out = np.array(sim["stats_energy"])[-1]
    stats["output energy"] = energy_out
    energy_in = np.array(sim["input_energies"])[0]
    stats["input energy"] = energy_in
    ne = np.array(sim["stats_peak_Ne"])
    stats["free electron density"] = ne
    z_solver = np.array(sim["stats_z"])
    stats["z"] = z_solver
    stats["neutral gas density"] = sim["gas_N0"][()]
    #stats["t_fwhm_compression"] = sim["stats_fwhm_t"][-1]
    sim.close()
    return z, ω, t, Eω, Et, modes, stats
    # return z, ω, t, Et, modes, stats

def plot_zprop_with_ion(t_compression_ratio=16, ω_compression_ratio=1):
    sim_filenames, N, energies = browse_sims()
    z, ω, t, Eω, Et, modes, stats = load_sim_prop(sim_filenames[0])
    time = t[::t_compression_ratio]
    afreq = ω[::ω_compression_ratio]
    Etime = np.zeros((len(z), modes, len(t)//t_compression_ratio))
    Espec = np.zeros((len(z), modes, len(t)//(2*ω_compression_ratio) + 1))
    Espec_full = np.zeros((len(t)//(2*ω_compression_ratio) + 1))
    wl = chrng(afreq)*1e9
    wl[0] = 6e5
    modes_to_plot = int(input("How many modes do you want to plot? "))
    Eplot = float(input("What input energy do you want to plot? "))
    idx_plot = np.abs(np.asarray(energies) - Eplot).argmin()
    _, _, _, Eω, Et, _, stats = load_sim_prop(sim_filenames[idx_plot])
    for mode in range(0, modes_to_plot):
        Etime[:, mode, :] = Et[:,mode,::t_compression_ratio]/Et[:,mode,::t_compression_ratio].max()
        Espec[:, mode, :] = Eω[:,mode,::ω_compression_ratio]/Eω[:,0,::ω_compression_ratio].max()
    #Espec_full = 10*np.log10(np.sum(Espec, axis=0))
    Espec = 10*np.log10(Espec)
    #Nsim = Nsol(gas, pressure, energies[:idx_lim,0], τfwhm, 800e-9, core_radius, T)
    fig = plt.figure(dpi=300)
    gs = gridspec.GridSpec(1, 3, width_ratios=[2, 2, 1], wspace=0.1, hspace=0.1, left=0.11, right=0.92, bottom=0.15, top=0.98)
    plt.rcParams.update({"font.size": 9})
    for mode in range(0, modes+1):
        if mode < modes:
            if mode == 0:
                ax = fig.add_subplot(gs[0])
                #plt.title("Propagation simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE1" + str(mode+1))
                #plt.pcolormesh(wl, Nsim, Espec[:idx_lim,mode,:], vmin=-30)
                ax.pcolormesh(afreq*1e-15, z, Espec[:, mode, :], vmin=-30, rasterized=True)
                ax.set_xlim(chrng(2500e-9)*1e-15, chrng(270e-9)*1e-15)
                ax.set_xlabel("Angular frequency [PHz]")
                #plt.ylabel("Soliton order")
                ax.set_ylabel("Propagation distance [m]")
                #plt.axvline(x=λzd(afreq, β2, gas, pressure, core_radius, T), color="white", linestyle="--")
            # else:
            #     plt.figure(dpi=250)
            #     plt.title("Energy scan spectrum simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE1" + str(mode+1))
            #     plt.pcolormesh(wl, energies[:,0]*1e6, Espec[:,mode,:], vmin=-30)
            #     plt.xlim(200, 1600)
            #     plt.xlabel("Wavelength [nm]")
            #     plt.ylabel("Soliton order")
        # else:
        #     plt.figure(dpi=250)
        #     plt.title("Energy scan spectrum simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, " + str(modes_to_plot) + " modes")
        #     plt.pcolormesh(wl, z, Espec_full, vmin=-30)
        #     plt.xlim(200, 1600)
        #     plt.xlabel("Wavelength [nm]")
            # plt.ylabel("Soliton order")
    bx = fig.add_subplot(gs[1])
    # plt.title("Energy scan time simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE11")
    bx.pcolormesh(time*1e15, z, Etime[:, 0, :], rasterized=True)
    bx.set_xlim(-15, 50)
    bx.set_yticks([], [])
    bx.set_xlabel("Time [fs]")
    cx = fig.add_subplot(gs[2])
    cx.plot(stats["free electron density"]/stats["neutral gas density"]*100, stats["z"], color=cm.viridis(0), linewidth=1.0)
    #plt.set_yticks([], [])
    cx.set_xlabel("Ionization fraction [%]")
    cx.set_yticks([], [])
    cx.set_ylim(0, 3)
    #cx.set_xlim(0, 4)
#     # plt.ylabel("Soliton order")
#     # x = np.linspace(0, energies[:idx_lim,0].max()*1e6, 200)
#     # straight_line_x = 0.64*x
#     # marker_colour = cm.viridis(0)
#     # plt.figure(dpi=250)
#     # plt.title("Energy scan energy simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE11")
#     # plt.plot(energies[:idx_lim,0]*1e6, energies[:idx_lim,1]*1e6, "o", markersize=3, color=marker_colour, label="Output energy")
#     # plt.plot(x, straight_line_x, "--k", linewidth=1.0, label="Linear output")
#     # plt.xlabel("Input energy [μJ]")
#     # plt.ylabel("Output energy [μJ]")
#     # plt.legend()

def plot_zprop(filename, t_compression_ratio=16, ω_compression_ratio=1):
    z, ω, t, Eω, Et, modes, stats = load_sim_prop(filename)
    fig = plt.figure(dpi=300)
    gs = gridspec.GridSpec(1, 2, width_ratios=[1, 1], wspace=0.1, hspace=0.1, left=0.11, right=0.92, bottom=0.15, top=0.98)
    plt.rcParams.update({"font.size": 9})
    ax = fig.add_subplot(gs[0])
    #plt.title("Propagation simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE1" + str(mode+1))
    #plt.pcolormesh(wl, Nsim, Espec[:idx_lim,mode,:], vmin=-30)
    ax.pcolormesh(ω[::ω_compression_ratio]*1e-15, z, 10*np.log10(abs(Eω[:, 0, ::ω_compression_ratio])**2/abs(Eω[:, 0, ::ω_compression_ratio].max())**2), vmin=-30, rasterized=True)
    ax.set_xlim(0.1, 6)
    ax.set_xlabel("Angular frequency (rad/fs)")
    #plt.ylabel("Soliton order")
    ax.set_ylabel("Propagation distance (m)")
    ax.axvline(x=chrng(λzd(ω, β2, "Ar", 467e-3, core_radius, T)*1e-9)*1e-15, color="white", linestyle="--")
    bx = fig.add_subplot(gs[1])
    # plt.title("Energy scan time simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE11")
    bx.pcolormesh(t[::t_compression_ratio]*1e15, z, abs(Et[:, 0, ::t_compression_ratio])**2, vmin=-30, rasterized=True)
    bx.set_xlim(-70, 150)
    bx.set_xticks([-50, 0, 50, 100, 150])
    bx.set_yticks([], [])
    bx.set_xlabel("Time (fs)")

def plot_escan(t_compression_ratio=16, ω_compression_ratio=1):
    sim_filenames, N, _ = browse_sims()
    ω, t, Eω, Et, energy, pressure, τfwhm, modes = load_sim(sim_filenames[0])
    time = t[::t_compression_ratio]
    afreq = ω[::ω_compression_ratio]
    Etime = np.zeros((N, modes, len(t)//t_compression_ratio))
    Espec = np.zeros((N, modes, len(t)//(2*ω_compression_ratio) + 1))
    Espec_full = np.zeros((N, len(t)//(2*ω_compression_ratio) + 1))
    energies = np.zeros((N, len(energy)))
    wl = chrng(afreq)*1e9
    wl[0] = 6e5
    modes_to_plot = int(input("How many modes do you want to plot? "))
    Elim = float(input("Up to what input energy do you want to plot? "))
    for idx, file in enumerate(sim_filenames):
        _, _, Eω, Et, energy, _, _, _ = load_sim(file)
        for mode in range(0, modes_to_plot):
            energies[idx] = energy
            Etime[idx,mode,:] = Et[-1,mode,::t_compression_ratio]/Et[-1,mode,::t_compression_ratio].max()
            Espec[idx,mode,:] = Eω[-1,mode,::ω_compression_ratio]/Eω[-1,0,::ω_compression_ratio].max()
        Espec_full[idx,:] = 10*np.log10(np.sum(Espec[idx], axis=0))
    Espec = 10*np.log10(Espec)
    idx_lim = np.abs(energies[:,0] - Elim).argmin()
    Nsim = Nsol(gas, pressure, energies[:idx_lim,0], τfwhm, 800e-9, core_radius, T)
    for mode in range(0, modes+1):
        if mode < modes:
            if mode == 0:
                plt.figure(dpi=200)
                plt.title("Energy scan spectrum simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE1" + str(mode+1))
                #plt.pcolormesh(wl, Nsim, Espec[:idx_lim,mode,:], vmin=-30)
                plt.pcolormesh(wl, energies[:idx_lim,0]*1e6, Espec[:idx_lim,mode,:], vmin=-30)
                plt.xlim(120, 1600)
                plt.xlabel("Wavelength [nm]")
                #plt.ylabel("Soliton order")
                plt.ylabel("Input energy [μJ]")
                plt.axvline(x=λzd(afreq, β2, gas, pressure, core_radius, T), color="white", linestyle="--")
            # else:
            #     plt.figure(dpi=250)
            #     plt.title("Energy scan spectrum simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE1" + str(mode+1))
            #     plt.pcolormesh(wl, energies[:,0]*1e6, Espec[:,mode,:], vmin=-30)
            #     plt.xlim(200, 1600)
            #     plt.xlabel("Wavelength [nm]")
            #     plt.ylabel("Soliton order")
        else:
            plt.figure(dpi=200)
            plt.title("Energy scan spectrum simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, " + str(modes_to_plot) + " modes")
            plt.pcolormesh(wl, energies[:idx_lim,0]*1e6, Espec_full[:idx_lim,:], vmin=-30)
            # plt.pcolormesh(wl, Nsim, Espec_full[:idx_lim,:], vmin=-30)
            plt.xlim(120, 1600)
            plt.xlabel("Wavelength [nm]")
            # plt.ylabel("Soliton order")
            plt.ylabel("Input energy [μJ]")
    plt.figure(dpi=200)
    plt.title("Energy scan time simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE11")
    # plt.pcolormesh(time*1e15, Nsim, Etime[:idx_lim,0,:])
    plt.pcolormesh(time*1e15, energies[:idx_lim,0]*1e6, Etime[:idx_lim,0,:])
    plt.xlim(-25, 50)
    plt.xlabel("Time [fs]")
    # plt.ylabel("Soliton order")
    plt.ylabel("Input energy [μJ]")
    x = np.linspace(0, energies[:idx_lim,0].max()*1e6, 200)
    straight_line_x = 0.64*x
    marker_colour = cm.viridis(0)
    plt.figure(dpi=200)
    plt.title("Energy scan energy simulation: " + str(τfwhm_fs) + " fs pulses, " + str(pressure_mbar) + " mbar Ar, mode HE11")
    plt.plot(energies[:idx_lim,0]*1e6, energies[:idx_lim,1]*1e6, "o", markersize=3, color=marker_colour, label="Output energy")
    plt.plot(x, straight_line_x, "--k", linewidth=1.0, label="Linear output")
    plt.xlabel("Input energy [μJ]")
    plt.ylabel("Output energy [μJ]")
    plt.legend()

def plot_1spec(filename):
    # sim_filenames, N, energies = browse_sims()
    # Eplot = float(input("What input energy do you want to plot? "))
    # idx_plot = np.abs(np.asarray(energies) - Eplot).argmin()
    # ω, t, Eω, Et, _, pressure, _, _ = load_sim(sim_filenames[idx_plot])
    ω, t, Eω, Et, _, pressure, _, _ = load_sim(filename)
    wl = chrng(ω)*1e9
    wl[0] = 6e5
    plt.figure(dpi=200)
    colour = cm.viridis(0)
    plt.plot(ω*1e-15, Eω[-1, 0, :]/Eω[-1, 0, :].max(0), color=colour, label=str(int(Eplot*1e6))+ " μJ")
    plt.axvline(x=chrng(λzd(ω, β2, gas, pressure, core_radius, T)*1e-9)*1e-15, color="black", linestyle="--")
    plt.xlim(0, 10)
    plt.xlabel("Angular frequency [PHz]")
    plt.ylim(0, 1.1)
    plt.ylabel("SPD [a.u.]")
    plt.legend()
    plt.figure(dpi=200)
    plt.plot(wl, (Eω[-1, 0, :]*ω**2/c)/(Eω[-1, 0, :]*ω**2/c).max(0), color=colour, label=str(int(Eplot*1e6))+ " μJ")
    plt.axvline(x=λzd(ω, β2, gas, pressure, core_radius, T), color="black", linestyle="--")
    plt.xlim(120, 2500)
    plt.xlabel("Wavelength [nm]")
    plt.ylim(0, 1.1)
    plt.ylabel("SPD [a.u.]")
    plt.legend()

def plot_filtered2(filename, λ0IR, ΔλIR, λ0VIS, ΔλVIS, supergauss_power, plot_filtered=True):
    def gauss_filter(x, x0, σ, supergauss_power):
        return sp.exp(-0.5*((x-x0)/σ)**supergauss_power)
    def chΔλ(Δλ, λ0):
        return (c/λ0**2)*Δλ
    z, ω, t, Eω, Et, _, _ = load_sim_prop(filename)
    # stats: "pressure", "output energy", "input energy", "free electron density", "z", "neutral gas density"
    # # wl = chrng(ω)*1e9
    # # wl[0] = 6e5
    Nt = len(t)
    T = Nt//2-1
    Nz = len(z)
    Nw = len(ω)
    gaussIR = np.zeros(Nt)
    gaussIR[T:] = gauss_filter(ω, chrng(λ0IR), chΔλ(ΔλIR, λ0IR), supergauss_power)
    gaussVIS = np.zeros(Nt)
    gaussVIS[T:] = gauss_filter(ω, chrng(λ0VIS), chΔλ(ΔλVIS, λ0VIS), supergauss_power)
    filteredEωIR = np.zeros((Nz, Nt), dtype=np.cdouble)
    filteredEωIRprop = np.zeros((Nz, Nw))
    filteredEtIR = np.zeros((Nz, Nt))
    ###### here this array if of real values only, because otherwise the printing complains; if I need phase I need to change it
    filteredEωVIS = np.zeros((Nz, Nt), dtype=np.cdouble)
    filteredEωVISprop = np.zeros((Nz, Nw))
    filteredEtVIS = np.zeros((Nz, Nt))
    for step in range(0, len(z)):
        filteredEωIR[step, T:] = gaussIR[T:]*Eω[step, 0, :]
        filteredEωIRprop[step, :] = abs(filteredEωIR[step, T:]/filteredEωIR[0, T:].max())**2
        filteredEtIR[step, :] = abs(2.0*sp.ifft(filteredEωIR[step, :]))**2
        filteredEωVIS[step, T:] = gaussVIS[T:]*Eω[step, 0, :]
        filteredEωVISprop[step, :] = abs(filteredEωVIS[step, T:]/filteredEωVIS[0, T:].max())**2
        filteredEtVIS[step, :] = abs(2.0*sp.ifft(filteredEωVIS[step, :]))**2
    colour = cm.viridis(0)
    colourIR = cm.viridis(100)
    colourVIS = cm.viridis(200)
    colourUV = cm.viridis(300)
    if plot_filtered == True:
        filteredEωIR = gaussIR[T:]*(Eω[-1, 0, :]/Eω[-1, 0, :].max())
        filteredEωVIS = gaussVIS[T:]*(Eω[-1, 0, :]/Eω[-1, 0, :].max())
        plt.figure(dpi=200)
        plt.plot(ω*1e-15, abs((Eω[-1, 0, :]))**2/(abs(Eω[-1, 0, :])**2).max(), color=colour, label="Full time")
        #plt.plot(ω*1e-15, abs(filteredEωIR)**2, color=colourIR, linestyle="--", label="Filtered IR")
        #plt.plot(ω*1e-15, abs(filteredEωVIS)**2, color=colourVIS, linestyle="--", label="Filtered VIS")
        plt.xlabel("Ang. frequency (rad/fs)")
        plt.ylabel("SPD (a.u.)")
        plt.axvline(x=2.35, color="black", linestyle="--")
        plt.xlim(0.1, 7)
    else:
        plt.figure(dpi=200)
        plt.plot(t*1e15, 1e-9*abs(Et[-1, 0, :])**2, color=colour, label="Full time")
        plt.plot(t*1e15, filteredEtIR[-1, :]*1e-9, color=colourIR, label="Filtered IR")
        plt.plot(t*1e15, filteredEtVIS[-1, :]*1e-9, color=colourVIS, label="Filtered VIS")
        plt.xlim(-30, 50)
        plt.xlabel("Time (fs)")
        plt.ylabel("Power (GW)")
        plt.legend()
    plt.show()

def plot_filtered(filename, λ0IR, ΔλIR, λ0VIS, ΔλVIS, λ0UV, ΔλUV, supergauss_power, plot_filtered=True):
    def gauss_filter(x, x0, σ, supergauss_power):
        return sp.exp(-0.5*((x-x0)/σ)**supergauss_power)
    def chΔλ(Δλ, λ0):
        return (c/λ0**2)*Δλ
    z, ω, t, Eω, Et, _, _ = load_sim_prop(filename)
    # stats: "pressure", "output energy", "input energy", "free electron density", "z", "neutral gas density"
    # # wl = chrng(ω)*1e9
    # # wl[0] = 6e5
    Nt = len(t)
    T = Nt//2-1
    Nz = len(z)
    Nw = len(ω)
    gaussIR = np.zeros(Nt)
    gaussIR[T:] = gauss_filter(ω, chrng(λ0IR), chΔλ(ΔλIR, λ0IR), supergauss_power)
    gaussVIS = np.zeros(Nt)
    gaussVIS[T:] = gauss_filter(ω, chrng(λ0VIS), chΔλ(ΔλVIS, λ0VIS), supergauss_power)
    gaussUV = np.zeros(Nt)
    gaussUV[T:] = gauss_filter(ω, chrng(λ0UV), chΔλ(ΔλUV, λ0UV), supergauss_power)
    filteredEωIR = np.zeros((Nz, Nt), dtype=np.cdouble)
    filteredEωIRprop = np.zeros((Nz, Nw))
    filteredEtIR = np.zeros((Nz, Nt))
    ###### here this array if of real values only, because otherwise the printing complains; if I need phase I need to change it
    filteredEωVIS = np.zeros((Nz, Nt), dtype=np.cdouble)
    filteredEωVISprop = np.zeros((Nz, Nw))
    filteredEtVIS = np.zeros((Nz, Nt))
    filteredEωUV = np.zeros((Nz, Nt), dtype=np.cdouble)
    filteredEωUVprop = np.zeros((Nz, Nw))
    filteredEtUV = np.zeros((Nz, Nt))
    maxIR = np.zeros((Nz))
    maxUV = np.zeros((Nz))
    energyIR = np.zeros((Nz))
    energyUV = np.zeros((Nz))
    for step in range(0, len(z)):
        filteredEωIR[step, T:] = gaussIR[T:]*Eω[step, 0, :]
        filteredEωIRprop[step, :] = abs(filteredEωIR[step, T:]/filteredEωIR[0, T:].max())**2
        maxIRidx = np.where(filteredEωIR[step, T:] == filteredEωIR[step, T:].max())
        maxIR[step] = ω[maxIRidx[0]]
        filteredEtIR[step, :] = abs(sp.ifft(filteredEωIR[step, :]))**2
        energyIR[step] = np.trapz(filteredEtIR[step, :], t)
        filteredEωVIS[step, T:] = gaussVIS[T:]*Eω[step, 0, :]
        filteredEωVISprop[step, :] = abs(filteredEωVIS[step, T:]/filteredEωVIS[0, T:].max())**2
        filteredEtVIS[step, :] = abs(2.0*sp.ifft(filteredEωVIS[step, :]))**2
        filteredEωUV[step, T:] = gaussUV[T:]*Eω[step, 0, :]
        filteredEωUVprop[step, :] = abs(filteredEωUV[step, T:]/filteredEωIR[0, T:].max())**2
        maxUVidx = np.where(filteredEωUV[step, T:] == filteredEωUV[step, T:].max())
        maxUV[step] = ω[maxUVidx[0]]
        filteredEtUV[step, :] = abs(2.0*sp.ifft(filteredEωUV[step, :]))**2
        energyUV[step] = np.trapz(filteredEtUV[step, :], t)
    colour = cm.viridis(0)
    colourIR = cm.viridis(100)
    colourVIS = cm.viridis(200)
    colourUV = cm.viridis(300)
    if plot_filtered == True:
        filteredEωIR = gaussIR[T:]*(Eω[-1, 0, :]/Eω[-1, 0, :].max())
        filteredEωVIS = gaussVIS[T:]*(Eω[-1, 0, :]/Eω[-1, 0, :].max())
        filteredEωUV = gaussUV[T:]*(Eω[-1, 0, :]/Eω[-1, 0, :].max())
        plt.figure(dpi=200)
        plt.plot(ω*1e-15, Eω[-1, 0, :]/Eω[-1, 0, :].max(), color=colour, label="Full time")
        plt.plot(ω*1e-15, filteredEωIR, color=colourIR, linestyle="--", label="Filtered IR")
        plt.plot(ω*1e-15, filteredEωVIS, color=colourVIS, linestyle="--", label="Filtered VIS")
        plt.plot(ω*1e-15, filteredEωUV, color=colourUV, linestyle="--", label="Filtered UV")
        plt.axvline(x=λzd(ω, β2, "Ar", 467e-3, core_radius, T), color="black", linestyle="--")
        plt.xlim(0.1, 8)
        plt.ylim(0, 1.1)
        plt.legend()
    else:
        plt.figure(dpi=200)
        plt.plot(chrng(ω)*1e9, Eω[-1, 0, :]/Eω[-1, 0, :].max(), color=colour)
        plt.axvline(x=λzd(ω, β2, "Ar", 467e-3, core_radius, T), color="black", linestyle="--")
        # plt.figure(dpi=200)
        # plt.subplot(1,2,1)
        # plt.plot(z, chrng(maxIR)*1e9, "ro", label="IR")
        # plt.plot(z, chrng(maxUV)*1e9, "ko", label="UV")
        # plt.legend()
        # plt.subplot(1,2,2)
        # plt.plot(z, energyIR*1e6, "ro", label="IR")
        # plt.plot(z, energyUV*1e6, "ko", label="UV")
        # plt.legend()
        # plt.figure(dpi=200)
        # plt.subplot(2,3,1)
        # plt.title("Filtered IR - spectrum")
        # filteredEωIRproplog = 10*np.log10(filteredEωIRprop)
        # filteredEωIRproplog[np.isinf(filteredEωIRproplog)] = -30
        # plt.pcolormesh(ω*1e-15, z, filteredEωIRproplog, vmin=-30, rasterized=True)
        # plt.xlim(0.1, 7)
        # plt.xlabel("Ang. frequency (rad/fs)")
        # plt.ylabel("Propagation distance (m)")
        # #plt.figure(dpi=200)
        # plt.subplot(2,3,4)
        # plt.title("Filtered IR - time")
        # plt.pcolormesh(t*1e15, z, filteredEtIR, rasterized=True)
        # plt.xlim(-25, 150)
        # plt.xlabel("Time (fs)")
        # plt.ylabel("Propagation distance (m)")
        # #plt.figure(dpi=200)
        # plt.subplot(2,3,2)
        # plt.title("Filtered VIS - spectrum")
        # filteredEωVISproplog = 10*np.log10(filteredEωVISprop)
        # filteredEωVISproplog[np.isinf(filteredEωVISproplog)] = -30
        # plt.pcolormesh(ω*1e-15, z, filteredEωVISproplog, vmin=-30, rasterized=True)
        # plt.xlim(0.1, 7)
        # plt.xlabel("Ang. frequency (rad/fs)")
        # plt.ylabel("Propagation distance (m)")
        # #plt.figure(dpi=200)
        # plt.subplot(2,3,5)
        # plt.title("Filtered VIS - time")
        # plt.pcolormesh(t*1e15, z, filteredEtVIS, rasterized=True)
        # plt.xlim(-25, 150)
        # plt.xlabel("Time [fs]")
        # plt.ylabel("Propagation distance (m)")
        # #plt.figure(dpi=200)
        # plt.subplot(2,3,3)
        # plt.title("Filtered UV - spectrum")
        # filteredEωUVproplog = 10*np.log10(filteredEωUVprop)
        # filteredEωUVproplog[np.isinf(filteredEωUVproplog)] = -30
        # plt.pcolormesh(ω*1e-15, z, filteredEωUVproplog, vmin=-30, vmax=0, rasterized=True)
        # plt.xlim(0.1, 7)
        # plt.xlabel("Ang. frequency (rad/fs)")
        # plt.ylabel("Propagation distance (m)")
        # plt.colorbar()
        # #plt.figure(dpi=200)
        # plt.subplot(2,3,6)
        # plt.title("Filtered UV - time")
        # plt.pcolormesh(t*1e15, z, filteredEtUV, rasterized=True)
        # plt.xlim(-25, 150)
        # plt.xlabel("Time (fs)")
        # plt.ylabel("Propagation distance (m)")
        # plt.colorbar()
    plt.show()

#plot_1spec()
#plot_filtered("Ar467mbar10fs170uJ.h5", 1250e-9, 5000e-9, 550e-9, 1100e-9, 200e-9, 650e-9, 10, False)
plot_filtered2("Ar467mbar_i0000000017_E=3.50854271e-05_0000.h5", 1320e-9, 5000e-9, 570e-9, 1100e-9, 30, True)
#plot_escan(8, 4)
#plot_zprop("collision.h5", 1, 4)

plt.show()


from logging import Logger
from pathlib import Path
from typing import Optional

import numpy as np
import torch
import torch.nn as nn
from torch.utils.tensorboard import SummaryWriter  # type: ignore

from .batch_generator import BatchGenerator
from .metrics import Metrics
from .misc import clean_print

AcceptedConfigTypes = int | float | str | bool | torch.Tensor

class TensorBoard:
    def __init__(self,
                 model: nn.Module,
                 tb_dir: Path,
                 train_dataloader: BatchGenerator,
                 val_dataloader: BatchGenerator,
                 logger: Logger,
                 metrics: Optional[Metrics] = None,
                 write_graph: bool = True):
        """Class with TensorBoard utility functions for classification-like tasks.

        Args:
            model (nn.Module): Pytorch model whose performance are to be recorded
            tb_dir (Path): Path to where the tensorboard files will be saved
            train_dataloader (BatchGenerator): DataLoader with a PyTorch DataLoader like interface, contains train data
            val_dataloader (BatchGenerator): DataLoader containing  validation data
            logger (Logger): Used to print things.
            metrics (Metrics, optional): Instance of the Metrics class, used to compute classification metrics
            write_graph (bool): If True, add the network graph to the TensorBoard
        """
        super().__init__()
        self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        self.tb_dir = tb_dir
        self.model = model
        self.train_dataloader = train_dataloader
        self.val_dataloader = val_dataloader
        self.logger = logger
        self.metrics = metrics

        self.weights_warning_printed: bool = False  # Prints a warning if the network cannot give its weights

        self.train_tb_writer = SummaryWriter(tb_dir / "Train")
        self.val_tb_writer = SummaryWriter(tb_dir / "Validation")
        if write_graph:
            self.logger.info("Adding network graph to TensorBoard")
            dummy_input = (torch.empty(2, *self.train_dataloader.data_shape, device=self.device), )
            self.train_tb_writer.add_graph(model, dummy_input)
            self.train_tb_writer.flush()

    def close_writers(self) -> None:
        self.train_tb_writer.close()
        self.val_tb_writer.close()

    def write_metrics(self, epoch: int, mode: str = "Train") -> None:
        """Writes metrics in TensorBoard.

        Args:
            epoch (int): Current epoch
            mode (str): Either "Train" or "Validation"

        Raises:
            TypeError: No Metrics instance
        """
        if not isinstance(self.metrics, Metrics):
            raise TypeError("Trying to write metrics, but did not get a Metrics instance during initialization")

        tb_writer = self.train_tb_writer if mode == "Train" else self.val_tb_writer
        metrics = self.metrics.get_metrics(mode)

        clean_print("Adding scalars to TensorBoard", end="\r")
        for scalar_metric_name, scalar_metric_value in metrics["scalars"].items():
            tb_writer.add_scalar(scalar_metric_name, scalar_metric_value, epoch)

        clean_print("Adding images to TensorBoard", end="\r")
        for img_metric_name, img_metric_value in metrics["imgs"].items():
            tb_writer.add_image(img_metric_name, img_metric_value, global_step=epoch, dataformats="HWC")

    def write_weights_grad(self, epoch: int):
        """Writes the model's weights and gradients to tensorboard, if the model can provide them.

        Args:
            epoch (int): Current epoch
        """
        try:
            for tag, (weight, grad) in self.model.get_weight_and_grads().items():  # type: ignore
                self.train_tb_writer.add_histogram(f"{tag}/weights", weight, epoch)
                self.train_tb_writer.add_histogram(f"{tag}/gradients", grad, epoch)
        except AttributeError:  # torch.nn.modules.module.ModuleAttributeError:
            if not self.weights_warning_printed:
                print(f"Warning: The model {self.model.__class__.__name__}"
                      f" does not support recording weights and gradients.")
                self.weights_warning_printed = True

    def write_loss(self, epoch: int, loss: float, mode: str = "Train"):
        """Writes loss metric in TensorBoard.

        Args:
            epoch (int): Current epoch
            loss (float): Epoch loss that will be added to the TensorBoard
            mode (str): Either "Train" or "Validation"
        """
        tb_writer = self.train_tb_writer if mode == "Train" else self.val_tb_writer
        tb_writer.add_scalar("Loss", loss, epoch)
        self.train_tb_writer.flush()

    def write_lr(self, epoch: int, lr: float):
        """Writes learning rate in the TensorBoard.

        Args:
            epoch (int): Current epoch
            lr (float): Learning rate for the given epoch
        """
        self.train_tb_writer.add_scalar("Learning Rate", lr, epoch)

    def write_config(self,
                     config: dict[str, AcceptedConfigTypes | list[float | int] | tuple[float | int, ...]],
                     metrics: Optional[dict[str, float]] = None):
        """Writes the config (and optionally metrics) to the TensorBoard.

        Args:
            config: The config to add to the TensorBoard.
                    The config's values should be "simple" values.
                    If a tuple/list is given, it will either be split up into several values or transformed into an str.
            metrics: The metrics for this run.
        """
        fixed_config: dict[str, AcceptedConfigTypes] = {}
        for key, value in config.items():
            if isinstance(value, (tuple, list, np.ndarray)):
                if len(value) < 3:
                    if isinstance(value, np.ndarray):
                        value = value.tolist()  # Convert from numpy types to python ones
                    for i in range(len(value)):
                        fixed_config[key + f"[{i}]"] = float(value[i])
                else:
                    fixed_config[key] = str(value)
            elif isinstance(value, int | float | str | bool | torch.Tensor):
                fixed_config[key] = value
            else:  # For things like None or other random types
                fixed_config[key] = str(value)

        config_tb_writer = SummaryWriter(self.tb_dir)
        config_tb_writer.add_hparams(fixed_config, metrics if metrics is not None else {"None": 0}, run_name="Config")
        config_tb_writer.close()

if __name__ == "__main__":
    def _main():
        import argparse

        from .logger import create_logger
        parser = argparse.ArgumentParser(description=("Script to test the Tensorboard template. "
                                                      "Run with 'python -m utils.tensorboard_template'."),
                                         formatter_class=argparse.ArgumentDefaultsHelpFormatter)
        parser.add_argument("--output_path", "-o", type=Path, default=Path("tb_temp"),
                            help="Path to where the TB file will be saved")
        parser.add_argument("--verbose_level", "-v", choices=["debug", "info", "error"], default="info", type=str,
                            help="Logger level.")
        args = parser.parse_args()

        output_path: Path = args.output_path

        logger = create_logger("Test TB", verbose_level=args.verbose_level)

        def _test_config():
            config = {"lr": 4e-3, "Batch Size": 32, "image_sizes": (224, 224)}
            metrics = {"Final/Accuracy": 0.99, "Final/counter": 3}
            tensorboard = TensorBoard(None, output_path, None, None, None, write_graph=False)  # type: ignore
            tensorboard.write_config(config, metrics)  # type: ignore
            logger.info(f"Tested the config writing part. TB can be found at {output_path}")
        _test_config()
    _main()


import cv2

# To capture video from webcam
# Might have to use 1 or 2 for parameter depending on if it's an integrated or usb webcam
cap = cv2.VideoCapture(0)

# Load Trained CascadeClassifier XML
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

while True:
    # Read the frame
    ret, img = cap.read()
    # Convert to grayscale
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Detect the faces
    faces = face_cascade.detectMultiScale(gray, 1.2, 5)
    # Draw the rectangle around each face
    for (x, y, w, h) in faces:
        cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
    # Display
    cv2.imshow('img', img)
    # Stop if escape key is pressed
    k = cv2.waitKey(1) & 0xff
    if k==27:
        break
# Release the VideoCapture object
cap.release()
#Delete any windows created
cv2.destroyallwindows()


"""
virtstrap.commands.install
--------------------------

The 'install' command
"""
import tempfile
import subprocess
from contextlib import contextmanager
from virtstrap import commands
from virtstrap import constants
from virtstrap.requirements import RequirementSet
from virtstrap.locker import RequirementsLocker

def process_requirements_config(raw_requirements):
    requirement_set = RequirementSet.from_config_data(raw_requirements)
    return requirement_set

class InstallationError(Exception):
    pass

class InstallCommand(commands.ProjectCommand):
    name = 'install'
    description = "Installs the project's python requirements"

    def run(self, project, options, **kwargs):
        requirement_set = self.get_requirement_set(project)
        if requirement_set:
            temp_reqs_path = self.write_temp_requirements_file(requirement_set)
            self.run_pip_install(project, temp_reqs_path)
            self.freeze_requirements(project, requirement_set)

    def get_requirement_set(self, project):
        requirement_set = project.process_config_section('requirements',
                                process_requirements_config)
        return requirement_set

    def write_temp_requirements_file(self, requirement_set):
        requirements_write = requirement_set.to_pip_str()
        os_handle, temp_reqs_path = tempfile.mkstemp()
        temp_reqs_file = open(temp_reqs_path, 'w')
        temp_reqs_file.write(requirements_write)
        temp_reqs_file.close()
        return temp_reqs_path

    def run_pip_install(self, project, requirements_path):
        logger = self.logger
        logger.info('Building requirements in "%s"' % project.config_file)
        pip_bin = project.bin_path('pip')
        pip_command = 'install'
        self.logger.debug('Running pip at %s' % pip_bin)
        return_code = subprocess.call([pip_bin, pip_command, '-r',
            requirements_path])
        if return_code != 0:
            raise InstallationError('An error occured during installation')

    def freeze_requirements(self, project, requirement_set):
        locker = RequirementsLocker()
        lock_str = locker.lock(requirement_set)
        requirements_lock = open(project.path(constants.VE_LOCK_FILENAME), 'w')
        requirements_lock.write(lock_str)
        requirements_lock.close()

    def freeze_requirements_old(self, project, requirement_set):
        pip_bin = project.bin_path('pip')
        process = subprocess.Popen([pip_bin, 'freeze'],
                stdout=subprocess.PIPE)
        requirements = process.stdout.read()
        requirements_lock = open(project.path(constants.VE_LOCK_FILENAME), 'w')
        requirements_lock.write(requirements)
        requirements_lock.close()


import os
import json
import time
from typing import Dict, List, Any

from jinja2 import Environment, PackageLoader

from pygwalker._constants import BYTE_LIMIT, ROOT_DIR
from pygwalker.utils.randoms import rand_str
from pygwalker.utils.display import display_html
from pygwalker import __version__, __hash__
from pygwalker_utils.config import get_config
from .global_var import GlobalVarManager

jinja_env = Environment(
    loader=PackageLoader("pygwalker"),
    autoescape=(()),  # select_autoescape()
)

class DataFrameEncoder(json.JSONEncoder):
    def default(self, o):
        try:
            return str(o)
        except TypeError:
            pass
        return json.JSONEncoder.default(self, o)

def gwalker_script() -> str:
    with open(os.path.join(ROOT_DIR, 'templates', 'dist', 'pygwalker-app.iife.js'), 'r', encoding='utf8') as f:
        gwalker_js = f.read()
    return gwalker_js

def _rand_slot_id():
    return __hash__ + '-' + rand_str(6)

def _send_js(js_code: str, slot_id: str):
    display_html(
        f"""""",
        GlobalVarManager.get_env(),
        slot_id=slot_id
    )

def _send_upload_data_msg(gid: int, msg: Dict[str, Any], slot_id: str):
    msg = json.dumps(msg, cls=DataFrameEncoder)
    js_code = (
        f"document.getElementById('gwalker-{gid}')?"
        ".contentWindow?"
        f".postMessage({msg}, '*');"
    )
    _send_js(js_code, slot_id)

def get_max_limited_datas(datas: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
    if len(datas) > 1024:
        smp0 = datas[::len(datas)//32]
        smp1 = datas[::len(datas)//37]
        avg_size = len(json.dumps(smp0, cls=DataFrameEncoder)) / len(smp0)
        avg_size = max(avg_size, len(json.dumps(smp1, cls=DataFrameEncoder)) / len(smp1))
        n = int(BYTE_LIMIT / avg_size)
        if len(datas) >= 2 * n:
            return datas[:n]
    return datas

class BatchUploadDatasTool:
    """Upload data in batches."""
    def __init__(self) -> None:
        self._caution_id = __hash__ + rand_str(6)
        self._progress_id = __hash__ + rand_str(6)

    def init(self) -> None:
        display_html("", GlobalVarManager.get_env(), slot_id=self._caution_id)
        display_html("", GlobalVarManager.get_env(), slot_id=self._progress_id)

    def run(
        self,
        *,
        data_source_id: str,
        gid: int,
        tunnel_id: str,
        records: List[Dict[str, Any]],
        sample_data_count: int,
        slot_count: int = 2
    ) -> None:
        progress_hint = "Dynamically loading into the frontend..."
        env = GlobalVarManager.get_env()
        chunk = 1 << 12
        cur_slot = 0
        display_slots = [_rand_slot_id() for _ in range(slot_count)]

        tips_title = (
            f'
Dataframe is too large for ipynb files. '
            f'Only {sample_data_count} sample items are printed to the file.
'
        )

        display_html(tips_title, env, slot_id=self._caution_id)
        display_html(f"{progress_hint} {sample_data_count}/{len(records)}", env, slot_id=self._progress_id)

        for i in range(sample_data_count, len(records), chunk):
            data = records[i: min(i+chunk, len(records))]
            msg = {
                'action': 'postData',
                'tunnelId': tunnel_id,
                'dataSourceId': data_source_id,
                'data': data,
            }
            _send_upload_data_msg(gid, msg, display_slots[cur_slot])
            cur_slot += 1
            cur_slot %= slot_count
            display_html(f"{progress_hint} {min(i+chunk, len(records))}/{len(records)}", env, slot_id=self._progress_id)

        finish_msg = {
            'action': 'finishData',
            'tunnelId': tunnel_id,
            'dataSourceId': data_source_id,
        }
        time.sleep(1)
        _send_upload_data_msg(gid, finish_msg, display_slots[cur_slot])
        display_html("", env, slot_id=self._progress_id)
        display_html("", env, slot_id=self._caution_id)

        for slot_id in display_slots:
            display_html("", env, slot_id=slot_id)

def render_gwalker_iframe(gid: int, srcdoc: str) -> str:
    return jinja_env.get_template("pygwalker_iframe.html").render(
        gid=gid,
        srcdoc=srcdoc,
    )

def render_gwalker_html(gid: int, props: Dict) -> str:
    ds = props.get('dataSource', [])

    props['len'] = len(ds)
    walker_template = jinja_env.get_template("walk.js")
    props['version'] = __version__
    props['hashcode'] = __hash__
    if 'spec' in props:
        props['visSpec'] = props.get('spec', None)
        del props['spec']
    props['userConfig'], _ = get_config()

    props['dataSourceProps'] = {
        'tunnelId': 'tunnel!',
        'dataSourceId': f'dataSource!{rand_str(4)}',
    }

    js = walker_template.render(gwalker={'id': gid, 'props': json.dumps(props, cls=DataFrameEncoder)})
    js = "var exports={}, module={};" + gwalker_script() + js

    template = jinja_env.get_template("index.html")
    html = f"{template.render(gwalker={'id': gid, 'script': js})}"
    return html


# encoding=utf-8
# -*- coding: utf-8 -*- vim:encoding=utf-8:
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
import warnings
warnings.simplefilter("ignore", DeprecationWarning)
import sys
import locale
import codecs
# Printing unicode: WAS: https://wiki.python.org/moin/PrintFails
# But now rather follow:
#    https://docs.djangoproject.com/en/1.9/howto/custom-management-commands/#management-commands-and-locales
# I.e., set leave_locale_alone and let the system locale handle things.
# Django 1.8 now uses unicode by default - so all works well without modifying the codecs.

from optparse import make_option
from django.core.management.base import BaseCommand
from accounts.models import User
from django.utils.translation import get_language, to_locale, ugettext as _
from utils.locale import setlocale, compat_strxfrm

class Command(BaseCommand):

    help = 'Prints institution contacts in CSV format'

    leave_locale_alone = True

    def add_arguments(self, parser):
        parser.add_argument(
            '--mail-list',
            action='store_true',
            dest='maillist',
            default=False,
            help='Return only emails (output suitable for a mailing list)'
        )

    def handle(self, *args, **options):
        users = User.objects.all()
        if not options['maillist']:
            self.stdout.write(
                u'{},{},{}'.format(
                    _('Institution'),
                    _('Administrator'),
                    "email"
                )
                + "\n"
            )
        data = [
            (
                u.userprofile.institution.get_name(get_language()),
                u.first_name + " " + u.last_name,
                m
            ) for u in users if (
                u.registrationprofile
                and u.registrationprofile.activation_key == "ALREADY_ACTIVATED"
            ) for m in u.email.split(';')
        ]
        with setlocale((to_locale(get_language()), 'UTF-8'),
                       locale.LC_COLLATE):
            data.sort(key=lambda d: compat_strxfrm(d[0]))
        for (foreas, onoma, email) in data:
            if options['maillist']:
                self.stdout.write(
                    u'{email}\t{onoma}'.format(
                        email=email,
                        onoma=onoma
                    )
                    + "\n")
            else:
                self.stdout.write(
                    u'"{foreas}","{onoma}","{email}"'.format(
                        onoma=onoma,
                        email=email,
                        foreas=foreas
                    )
                    + "\n"
                )


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

import os
import re
import tempfile
from time import time
from .basecache import BaseCache
from .posixemulation import rename, _items
try:
    import cPickle as pickle
except ImportError:
    import pickle
try:
    from hashlib import md5
except ImportError:
    from md5 import new as md5

_test_memcached_key = re.compile(r'[^\x00-\x21\xff]{1,250}$').match

class MemcachedCache(BaseCache):
    """A cache that uses memcached as backend.

    The first argument can either be an object that resembles the API of a
    :class:`memcache.Client` or a tuple/list of server addresses. In the
    event that a tuple/list is passed, Werkzeug tries to import the best
    available memcache library.

    Implementation notes:  This cache backend works around some limitations in
    memcached to simplify the interface.  For example unicode keys are encoded
    to utf-8 on the fly.  Methods such as :meth:`~BaseCache.get_dict` return
    the keys in the same format as passed.  Furthermore all get methods
    silently ignore key errors to not cause problems when untrusted user data
    is passed to the get methods which is often the case in web applications.

    :param servers: a list or tuple of server addresses or alternatively
                    a :class:`memcache.Client` or a compatible client.
    :param default_timeout: the default timeout that is used if no timeout is
                            specified on :meth:`~BaseCache.set`.
    :param key_prefix: a prefix that is added before all keys.  This makes it
                       possible to use the same memcached server for different
                       applications.  Keep in mind that
                       :meth:`~BaseCache.clear` will also clear keys with a
                       different prefix.
    """

    def __init__(self, servers=None, default_timeout=300, key_prefix=None):
        BaseCache.__init__(self, default_timeout)
        if servers is None or isinstance(servers, (list, tuple)):
            if servers is None:
                servers = ['127.0.0.1:11211']
            self._client = self.import_preferred_memcache_lib(servers)
            if self._client is None:
                raise RuntimeError('no memcache module found')
        else:
            # NOTE: servers is actually an already initialized memcache
            # client.
            self._client = servers

        self.key_prefix = key_prefix

    def get(self, key):
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if self.key_prefix:
            key = self.key_prefix + key
        # memcached doesn't support keys longer than that.  Because often
        # checks for so long keys can occour because it's tested from user
        # submitted data etc we fail silently for getting.
        if _test_memcached_key(key):
            return self._client.get(key)

    def get_dict(self, *keys):
        key_mapping = {}
        have_encoded_keys = False
        for key in keys:
            if isinstance(key, unicode):
                encoded_key = key.encode('utf-8')
                have_encoded_keys = True
            else:
                encoded_key = key
            if self.key_prefix:
                encoded_key = self.key_prefix + encoded_key
            if _test_memcached_key(key):
                key_mapping[encoded_key] = key
        d = rv = self._client.get_multi(key_mapping.keys())
        if have_encoded_keys or self.key_prefix:
            rv = {}
            for key, value in d.iteritems():
                rv[key_mapping[key]] = value
        if len(rv) < len(keys):
            for key in keys:
                if key not in rv:
                    rv[key] = None
        return rv

    def add(self, key, value, timeout=None):
        if timeout is None:
            timeout = self.default_timeout
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if self.key_prefix:
            key = self.key_prefix + key
        self._client.add(key, value, timeout)

    def set(self, key, value, timeout=None):
        if timeout is None:
            timeout = self.default_timeout
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if self.key_prefix:
            key = self.key_prefix + key
        self._client.set(key, value, timeout)

    def get_many(self, *keys):
        d = self.get_dict(*keys)
        return [d[key] for key in keys]

    def set_many(self, mapping, timeout=None):
        if timeout is None:
            timeout = self.default_timeout
        new_mapping = {}
        for key, value in _items(mapping):
            if isinstance(key, unicode):
                key = key.encode('utf-8')
            if self.key_prefix:
                key = self.key_prefix + key
            new_mapping[key] = value
        self._client.set_multi(new_mapping, timeout)

    def delete(self, key):
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if self.key_prefix:
            key = self.key_prefix + key
        if _test_memcached_key(key):
            self._client.delete(key)

    def delete_many(self, *keys):
        new_keys = []
        for key in keys:
            if isinstance(key, unicode):
                key = key.encode('utf-8')
            if self.key_prefix:
                key = self.key_prefix + key
            if _test_memcached_key(key):
                new_keys.append(key)
        self._client.delete_multi(new_keys)

    def clear(self):
        self._client.flush_all()

    def inc(self, key, delta=1):
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if self.key_prefix:
            key = self.key_prefix + key
        self._client.incr(key, delta)

    def dec(self, key, delta=1):
        if isinstance(key, unicode):
            key = key.encode('utf-8')
        if self.key_prefix:
            key = self.key_prefix + key
        self._client.decr(key, delta)

    def import_preferred_memcache_lib(self, servers):
        """Returns an initialized memcache client.  Used by the constructor."""
        try:
            import pylibmc
        except ImportError:
            pass
        else:
            return pylibmc.Client(servers)

        try:
            from google.appengine.api import memcache
        except ImportError:
            pass
        else:
            return memcache.Client()

        try:
            import memcache
        except ImportError:
            pass
        else:
            return memcache.Client(servers)

# backwards compatibility
GAEMemcachedCache = MemcachedCache


import click
import boto3

from decouple import config
from prettytable import PrettyTable

@click.command()
@click.argument('resource')
@click.option('--profile', default=config('DEFAULT_AWS_PROFILE', None),
              help='AWS CLI Profile to use.')
def main(resource, profile):
    if profile:
        boto3.setup_default_session(profile_name=profile)

    if resource == 'ec2':
        list_aws_instances()
    elif resource == 'rds':
        list_aws_rds()
    else:
        with click.get_current_context() as ctx:
            click.echo(ctx.get_help())
            ctx.exit()

def list_aws_instances():
    ec2 = boto3.resource('ec2')
    table = PrettyTable(align='l')
    table.field_names = ['State', 'Name', 'ID', 'Key Pair', 'Public IP',
                         'Public Address']

    for instance in ec2.instances.all():
        try:
            instance_name = [x for x in instance.tags if x['Key'] == 'Name']
            instance_name = instance_name[0]['Value']
        except TypeError:
            instance_name = '-'

        table.add_row([
            instance.state['Name'].title(),
            instance_name,
            instance.id,
            instance.key_name,
            instance.public_ip_address,
            instance.public_dns_name
        ])

    print(table)

def list_aws_rds():
    resource = boto3.client('rds')
    instances = resource.describe_db_instances()

    table = PrettyTable(align='l')
    table.field_names = ['State', 'Engine', 'Name', 'Type', 'Address',
                         'MasterUser']

    for instance in instances['DBInstances']:
        table.add_row([
            instance['DBInstanceStatus'].title(),
            instance['Engine'],
            instance['DBInstanceIdentifier'],
            instance['DBInstanceClass'],
            instance['Endpoint']['Address'],
            instance['MasterUsername']
        ])

    print(table)

if __name__ == '__main__':
    main()