from dotenv import load_dotenv

load_dotenv()

from datetime import datetime
import json
import os.path
from pydub import AudioSegment
from pydub.playback import play
import alsaaudio
import lights
import music

weekday_to_string = ["MON", "YUE", "WED", "THU", "FRI", "SAT", "SUN"]

def time_fmt(time: [int, int]) -> float:
    return time[0] + time[1] / 60

def check_time():
    settings = json.load(open("data/settings.json"))
    bedtime = time_fmt(settings["bedtime"])
    wakeup = time_fmt(settings["wakeup"])
    now = time_fmt([datetime.now().hour, datetime.now().minute])
    wakeup_day = weekday_to_string[datetime.now().weekday()]

    if bedtime > wakeup:
        wakeup += 24
        wkday = datetime.now().weekday()
        wakeup_day = weekday_to_string[wkday + 1 if wkday < 6 else 0]
        if now >= 0:
            now += 24

    if wakeup_day not in settings["days"]:
        return

    if bedtime == now:
        lights.bedtime()
    elif bedtime < now < wakeup:
        lights.meantime(now, bedtime, wakeup)
    elif now == wakeup:
        its_wakeup(settings)

def its_wakeup(settings: map):
    song = music.get_song(settings["musicURI"], settings["spotify"])
    song_audio = music.download_song(song["download"], settings["playTime"])

    alsaaudio.Mixer("PCM").setvolume(settings["volume"])

    lights.fade_in(0.5, song["color"])
    play(song_audio)
    lights.fade_out(0.5)

if __name__ == "__main__":
    check_time()


import csv
import os
from datetime import datetime
import pickle
from constructor import Routine

def activity_matching(str, time: datetime) -> str:
    if str == "Morning_Meds" or str == "Eve_Meds":
        return "taking_medication"
    elif str == "Kitchen_Activity" or str == "Dining_Rm_Activity":
        if time.hour < 10:
            return "breakfast"
        elif time.hour > 4:
            return "lunch"
        else:
            return "dinner"
    elif str == "Bed_to_Toilet" or str == "Guest_Bathroom" or str == "Master_Bathroom":
        return "going_to_the_bathroom"
    elif str == "Sleep":
        return "sleeping"
    elif str == "Watch_TV":
        return "watching_tv"
    elif str == "Chores":
        return "cleaning"
    elif str == "Leave_Home":
        return "leave_home"
    elif str == "Come_Home":
        return "come_home"
    elif str == "Read":
        return "reading"
    elif str == "Desk_Activity":
        return "computer_work"
    elif str == "Meditate":
        return "meditating"
    elif str == "Master_Bedroom_Activity":
        return "master_bedroom"
    else:
        raise ValueError(f"Unknown activity: {str}")

def main():
    # change to current file path
    os.chdir(os.path.dirname(os.path.realpath(__file__)))

    routines = []

    with open("casas-raw/milan/data", 'r') as f:
        data = csv.reader(f, delimiter="\t")
        current_routine = Routine(None, "-1")
        in_progress = False
        start_time = None
        curr_day = None
        curr_activity = None
        prev_time = datetime(1970, 1, 1)

        act_list = {}
        nested_start_time = None
        nested_activity = None

        for i, row in enumerate(data):

            try:
                time = datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S.%f")
            except ValueError:
                time = datetime.strptime(row[0], "%Y-%m-%d %H:%M:%S")

            if time < prev_time:
                continue
                raise ValueError(f"Time is not in order starting at {i}")
            prev_time = time

            if time.day != curr_day:
                print(time.day)
                routines.append(current_routine)
                curr_day = time.day
                current_routine = Routine(None, str(time.day))

            if len(row) > 3 and row[-1] != "":

                state = row[-1].split(" ")[1]
                activity_name = row[3].split(" ")[0]

                if state == "begin":

                    if curr_activity is not None:
                        if nested_activity is None:
                            nested_activity = activity_name
                            nested_start_time = start_time
                        else:
                            print(f"Activity {curr_activity} is not finished at {i}")
                    else:
                        start_time = time
                        curr_activity = activity_name
                else:
                    if curr_activity != activity_name:
                        if activity_name == nested_activity:
                            current_routine.add_activity(activity_matching(activity_name, start_time), start_time, nested_start_time, remove_overlap=True)
                            current_routine.add_activity(activity_matching(nested_activity, nested_start_time), nested_start_time, time, remove_overlap=True)
                            start_time = time
                            nested_activity = None
                    else:
                        current_routine.add_activity(activity_matching(activity_name, start_time), start_time, time, remove_overlap=True)
                        curr_activity = None
                        nested_activity = None

    if len(routines) > 0:
        print("days: ", len(routines))
        os.makedirs("datasets/casas", exist_ok=True)
        with open("datasets/casas/routines.pkl", 'wb') as f:
            pickle.dump(routines, f)

if __name__ == "__main__":
    main()


import os
import time
import logging
import argparse
import numpy as np
from scipy import stats
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
import torchvision
import torchvision.transforms as transforms
from torchsummary import summary
from sklearn.metrics import classification_report, confusion_matrix
from model import CNN, CNN2
os.chdir(os.getcwd())
logging.basicConfig(filename='record.log', level=logging.INFO)
logging.getLogger().setLevel(logging.INFO)

class cifar10(Dataset):
    def __init__(self, hdf5_path, train=True, transform=None):
        super(cifar10, self).__init__()
        data = h5py.File(hdf5_path, "r")
        if train:
            self.X = data.get("X_train").value
            self.y = data.get("Y_train").value
        else:
            self.X = data.get("X_test").value
            self.y = data.get("Y_test").value
        # need to change data type to unit8 otherwise transform won't work
        if str(self.X.dtype)[:3] == "int":
            self.X = self.X.astype(np.uint8)
            self.y = self.y.astype(np.uint8)
        self.transform = transform

    def __getitem__(self, index):
        image = self.X[index]
        label = self.y[index]
        if self.transform is not None:
            image = self.transform(image)
        return image, label

    def __len__(self):
        return self.X.shape[0]

def parse_args():
    parser = argparse.ArgumentParser(description="hyper-parameters for CNN training on CIFAR10")
    parser.add_argument("--n_epochs", type=int, default=100,
                        help="total number of epoches, only integer are allowed")
    parser.add_argument("--batch_size", type=int, default=128,
                        help="batch size for each iteration, only integer are allowed")
    parser.add_argument("--record_step", type=int, default=100,
                        help="how many of iterations take a snapshot of metrics")
    parser.add_argument("--lr", type=float, default=0.01,
                        help="learning rate for training")
    parser.add_argument("--optimizer", type=str, default="Adam", choices=["Adam", "RMSprop"],
                        help="choice of optimizer, only Adam and RMSprop are allowed so far")
    parser.add_argument("--need_train", type=bool, default=True,
                        help="do you already have a pre-trained model? use model_name to determine which model to load")
    parser.add_argument("--model_name", type=str, default="",
                        help="model will be saved at ./result directory")

    args = parser.parse_args()
    return args

def load_data(transform, batch_size):
    transform_train = transform["train_transform"]
    transform_test = transform["test_transform"]

    # train_data = cifar10(data_path, train=True, transform=transform_train)
    # test_data = cifar10(data_path, train=False, transform=transform_test)

    trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                            download=True, transform=transform_train)
    testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                           download=True, transform=transform_test)

    train_loader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)
    test_loader = DataLoader(dataset=test_data, batch_size=batch_size, shuffle=True)

    return train_loader, test_loader

def train(model, train_loader, test_loader, n_epochs, optimizer, criterion, scheduler=None, record_step=100):
    total_step = len(train_loader)
    start_time = time.perf_counter()
    train_loss, test_loss = [], []
    train_acc, test_acc = [], []
    for n in range(n_epochs):
        print("==============Epoch:{}/{}==============".format(n+1, n_epochs))
        logging.info("==============Epoch:{}/{}==============".format(n+1, n_epochs))
        correct = 0
        num_train = 0

        for i, (images, labels) in enumerate(train_loader):
            images = images.to(device)
            labels = labels.type(torch.long).to(device)

            optimizer.zero_grad()
            # forward
            predicts = model(images)
            loss = criterion(predicts, labels)

            # calculate accuracy
            num_train += labels.size(0)
            _, predict_class = torch.max(predicts.data, 1)
            correct += (predict_class == labels).sum().item()

            # backward and optimize

            loss.backward()
            optimizer.step()

            curr_time = time.perf_counter()
            if (i+1) % record_step == 0:
                print("Epoch: {}/{}, step:{}/{}, Loss:{:.4f}, Cumulative accuracy:{:.4f}, time used:{:.4f}".
                    format(n+1, n_epochs, i+1, total_step, loss.item(), correct/num_train, curr_time - start_time))
                logging.info("Epoch: {}/{}, step:{}/{}, Loss:{:.4f}, Cumulative accuracy:{:.4f}, time used:{:.4f}".
                    format(n+1, n_epochs, i+1, total_step, loss.item(), correct/num_train, curr_time - start_time))

        train_loss.append(loss.item())
        train_acc.append(correct/num_train)

        train_accuray = correct/num_train
        scheduler.step()
        # evaluate performance on test data each epoch
        with torch.no_grad():
            correct = 0
            num_test = 0
            for images, labels in test_loader:
                images = images.to(device)
                labels = labels.type(torch.long).to(device)

                predicts = model(images)
                loss = criterion(predicts, labels)

                _, predict_class = torch.max(predicts.data, 1)
                num_test += labels.size(0)
                correct += (predict_class == labels).sum().item()

        test_accuracy = correct/num_test
        test_loss.append(loss.item())
        test_acc.append(test_accuracy)

        curr_time = time.perf_counter()
        logging.info("********************************")
        logging.info("Epoch:{}/{}, time used:{:.4f}, train accuracy:{:.4f}, test accuracy:{:.4f}".
            format(n+1, n_epochs, curr_time - start_time, train_accuray, test_accuracy))
        logging.info("********************************")
        print("********************************")
        print("Epoch:{}/{}, time used:{:.4f}, train accuracy:{:.4f}, test accuracy:{:.4f}".
            format(n+1, n_epochs, curr_time - start_time, train_accuray, test_accuracy))
        print("********************************")
    metrics = {
        "train_loss": train_loss,
        "test_loss": test_loss,
        "train_acc": train_acc,
        "test_acc": test_acc
    }
    return model, metrics

def prediction(model, test_loader, device, result_path, is_MC=False, step=100):
    if is_MC:
        model.train()
    else:
        model.eval()
    all_predict_class = []
    all_true_class = []
    with torch.no_grad():
        correct = 0
        num_test = 0
        for images, labels in test_loader:
            images = images.to(device)
            labels = labels.type(torch.long).to(device)

            if is_MC:
                each_batch_pred = []
                for n in range(step):
                    predict = model(images)
                    _, predict_class = torch.max(predict.data, 1)
                    each_batch_pred.append(predict_class.tolist())
                predict_class, _ = stats.mode(each_batch_pred)
                predict_class = predict_class[0]
            else:
                predict = model(images)
                _, predict_class = torch.max(predict.data, 1)

            all_true_class.extend(labels.tolist())
            all_predict_class.extend(predict_class.tolist())

            num_test += labels.size(0)
            correct += (labels == predict_class).sum().item()

        test_accuracy = correct/num_test
        print("overall test_accuracy is:{:.4f}".format(test_accuracy))
        logging.info("overall test_accuracy is:{:.4f}".format(test_accuracy))
    return all_true_class, all_predict_class

def loss_acc_curve(train_loss, train_acc, test_loss, test_acc, plot_path):
    # summarize history for accuracy
    plt.figure(figsize=(20,10))
    plt.plot(train_acc)
    plt.plot(test_acc)
    plt.title('model accuracy')
    plt.ylabel('accuracy')
    plt.xlabel('epoch')
    plt.legend(['train', 'test'], loc='upper left')
    plt.savefig(os.path.join(plot_path, "accuracy curve.png"))
    plt.close()
    # summarize history for loss
    plt.figure(figsize=(20,10))
    plt.plot(train_loss)
    plt.plot(test_loss)
    plt.title('model loss')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train', 'test'], loc='upper left')
    plt.savefig(os.path.join(plot_path, "loss curve.png"))
    plt.close()

def main():
    # set hyper-parameters
    args = parse_args()
    n_epochs = args.n_epochs
    batch_size = args.batch_size
    record_step = args.record_step
    learning_rate = args.lr
    optimizer_name = args.optimizer
    need_train = args.need_train
    model_name = args.model_name

    assert optimizer_name in ["Adam", "RMSprop"], "optimizer should choose from ['Adam', 'RMSprop']"
    assert len(model_name) > 0, "you should give the model a name to save/load"

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

    if os.path.splitext(model_name)[1] != ".ckpt":
        model_name = model_name + ".ckpt"

    root_path = os.getcwd()
    res_path = os.path.join(root_path, "result")
    if not os.path.exists(res_path):
        raise PermissionError("didn't make directory /result!!")

    # load data
    data_transform = {
        "train_transform": transforms.Compose([
            transforms.RandomCrop(32, padding=4),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
        ]),
        "test_transform": transforms.Compose([
            transforms.ToTensor(),
            transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
        ])
    }

    train_loader, test_loader = load_data(data_transform, batch_size)

    # define the model and optimizer
    model = CNN2(num_class=10).to(device)
    criterion = nn.CrossEntropyLoss()

    if optimizer_name == "Adam":
        optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
    elif optimizer_name == "RMSprop":
        optimizer = torch.optim.RMSprop(model.parameters(), lr=learning_rate)

    scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[50, 100, 150, 200], gamma=0.1)

    # start training...
    if need_train:
        model, metrics = train(model, train_loader, test_loader, n_epochs,
                      optimizer, criterion, scheduler=scheduler, record_step=record_step)

        train_loss, test_loss = metrics["train_loss"], metrics["test_loss"]
        train_acc, test_acc = metrics["train_acc"], metrics["test_acc"]
        loss_acc_curve(train_loss, train_acc, test_loss, test_acc, res_path)
        torch.save(model.state_dict(), os.path.join(res_path, model_name))
    else:
        model = CNN2(num_class=10)
        model_path = os.path.join(res_path, model_name)
        model.load_state_dict(torch.load(model_path))

    # start testing
    heuristic_true_class, heuristic_predict_class = prediction(model, test_loader, device, res_path)

    report = classification_report(heuristic_true_class, heuristic_predict_class)
    confusion_mat = confusion_matrix(heuristic_true_class, heuristic_predict_class)
    logging.info("==========heuristic test set performance===========")
    logging.info("{}".format(report))
    logging.info("{}".format(confusion_mat))

    print("==========heuristic test set performance===========")
    print(report)
    print(confusion_mat)

    MC_true_class, MC_predict_class = prediction(mode, test_loader, device, res_path, is_MC=True, step=100)
    report = classification_report(MC_true_class, MC_predict_class)
    confusion_mat = confusion_matrix(MC_true_class, MC_predict_class)
    logging.info("==========Monte Carlo test set performance===========")
    logging.info("{}".format(report))
    logging.info("{}".format(confusion_mat))

    print("==========Monte Carlo test set performance===========")
    print(report)
    print(confusion_mat)

if __name__ == '__main__':
    main()


import socket
import threading
import time
import datetime
from ..sdn_message_reader import SDNMessageReader
from ryu.ofproto.ofproto_v1_0 import OFPT_BARRIER_REQUEST, OFPT_FLOW_MOD

class TimedReverseFlowmodsMessageManager(object):
    '''
    When receive flow mod sdn messages on incoming queue, delay until:

      1) Receive a barrier or
      2) A user-specified period of time has expired since received
         last flow mod.

    If either of these happen, send out buffered flow mods in reverse
    order of when they were accepted.
    '''

    def __init__(self,timeout_seconds,receiver_socket,sender_socket):
        '''
        @param {float} timeout_seconds --- If we have not received a
        flow mod in this many seconds, then send all buffered flow
        mods in reverse order.

        @param {SDNSocket} receiver_socket --- Reads messages on this
        socket and instantly forwards them on sender_socket.

        @param {SDNSocket} sender_socket --- Forwards openflow
        messages out of this socket.
        '''
        self.timeout_seconds = timeout_seconds

        self.sdn_message_reader = SDNMessageReader(receiver_socket)
        self.sender_socket = sender_socket
        # Elements are byte arrays/strings for openflow messages since
        # last barrier message.
        self.received_flowmods_list = []
        self.last_received_time = None
        self._received_flow_mods_lock = threading.RLock()

    def check_time_expired_thread(self):
        '''
        Runs, periodically checking whether it's been
        self.timeout_seconds since the last flow mod has been
        received, and send buffered flow mods if it has been.
        '''
        sleep_time = .5
        while True:
            time.sleep(sleep_time)
            with self._received_flow_mods_lock:
                # empty flowmods list.
                if self.last_received_time is None:
                    sleep_time = .5
                    continue

                # check when the last sent flowmod was received.
                current_time = datetime.datetime.now()
                diff = current_time - self.last_received_time
                seconds_since_last_flowmod = diff.total_seconds()
                if seconds_since_last_flowmod >= self.timeout_seconds:
                    self._send_outstanding()
                    sleep_time = .5
                else:
                    sleep_time =  (
                        self.timeout_seconds - seconds_since_last_flowmod + .01)

    def start_service(self):
        '''
        Actually start listening for openflow messages and forwarding
        them.
        '''
        t = threading.Thread(target=self._start_forwarding)
        t.setDaemon(True)
        t.start()
        t_watchdog = threading.Thread(target=self.check_time_expired_thread)
        t_watchdog.setDaemon(True)
        t_watchdog.start()

    def _send_outstanding(self,barrier_message_buf=None):
        '''
        @param {bytearray} barrier_message_buf --- None if we aren't
        supposed to send a barrier message.
        '''
        with self._received_flow_mods_lock:
            # by default, we forward flowmods in reverse order.
            for msg_buf_to_write in reversed(self.received_flowmods_list):
                self.sender_socket.write(msg_buf_to_write)

            self.received_flowmods_list = []

            if barrier_message_buf is not None:
                self.sender_socket.write(barrier_message_buf)

            # reset receipt timer because sent out last outstanding
            # message.
            self.last_received_time = None

    def _start_forwarding(self):
        while True:
            # msg will either be a ryu message or an unparsed message.
            # in either case, should support serialize + buf calls.
            msg = self.sdn_message_reader.blocking_read_sdn_message()
            msg.serialize()

            with self._received_flow_mods_lock:
                try:
                    if msg.msg_type == OFPT_BARRIER_REQUEST:
                        self._send_outstanding(msg.original_buffer)
                    elif msg.msg_type == OFPT_FLOW_MOD:
                        self.last_received_time = datetime.datetime.now()
                        self.received_flowmods_list.append(msg.original_buffer)
                    else:
                        # just forward the message along: it's non-barrier
                        # and non-flow mod
                        self.sender_socket.write(msg.original_buffer)
                except socket.error:
                    break


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

from __future__ import absolute_import
import six

from . import mimeparse
from .errors import Error
from .protocol import WHITESPACE, DIGITS, ORD_0, to_hstring

def decode_text(text, encoding, backup_encoding='latin1'):
    if encoding is None:
        encoding = backup_encoding

    try:
        decoded_text = text.decode(encoding)
    except LookupError:
        # Failed to find the given encoding (such as "x-unkown", for
        # example).
        decoded_text = text.decode(backup_encoding)
    except UnicodeDecodeError:
        # (from iKOM) Fscking clients that can't detect coding...
        decoded_text = text.decode(backup_encoding)

    return decoded_text

def parse_content_type(contenttype):
    try:
        mime_type = mimeparse.parse_mime_type(contenttype)
    except Exception:
        raise Error("Failed to parse mime type '%s'" % (contenttype,))

    if mime_type[0] == 'x-kom' and mime_type[1] == 'text':
        mime_type = ('text', 'x-kom-basic', mime_type[2])

    if "charset" in mime_type[2]:
        # Remove charset from mime_type, if we have it
        encoding = mime_type[2].pop("charset")
    else:
        encoding = None

    if encoding == 'x-ctext':
        encoding = 'latin1'

    return mime_type, encoding

def mime_type_tuple_to_str(mime_type):
    params = [ "%s=%s" % (k, v) for k, v in mime_type[2].items() ]
    t = "%s/%s" % (mime_type[0], mime_type[1])
    l = [t]
    l.extend(params)
    return ";".join(l)

def parse_hollerith_string(hstring):
    if not hstring:
        return None, hstring
    assert isinstance(hstring, six.binary_type)

    i = 0

    # Skip all leading whitespaces
    c = hstring[i:i+1]
    while c in WHITESPACE:
        i += 1
        c = hstring[i:i+1]

    # Parse length
    length = 0
    while c in DIGITS:
        length = length * 10 + (ord(c) - ORD_0)
        i += 1
        c = hstring[i:i+1]

    if c != b"H":
        raise Exception("Not a valid hollerith string")

    i += 1 # skip "H"
    hend = i + length
    return hstring[i:hend], hstring[hend:]

def to_hollerith_string(s):
    return to_hstring(s)

def decode_user_area(user_area):
    """Decodes the user area to a dictionary where the keys are the
    block names and the values are the hollerith string encoded block
    content.
    """
    h_block_names, h_blocks = parse_hollerith_string(user_area)

    def parse_block_names(hstring):
        # Parse block names
        block_names = []
        h_block_name, hstring = parse_hollerith_string(hstring)
        while h_block_name:
            block_names.append(h_block_name)
            h_block_name, hstring = parse_hollerith_string(hstring)
        return block_names

    def parse_blocks(hstring, block_names):
        blocks = {}
        for block_name in block_names:
            h_block, hstring = parse_hollerith_string(hstring)
            blocks[block_name] = h_block
        return blocks

    block_names = parse_block_names(h_block_names)
    blocks = parse_blocks(h_blocks, block_names)
    return blocks

def encode_user_area(blocks):
    """Encodes a dictionary to a user area. The keys should be the
    block names and the values are the hollerith string encoded
    blocks. The block names will be sorted alphabetically.
    """
    h_block_names = b""
    h_blocks = b""
    for block_name in sorted(blocks.keys()):
        h_block_names += b" " + to_hollerith_string(block_name)
        h_blocks += b" " + to_hollerith_string(blocks[block_name])

    return to_hollerith_string(h_block_names) + h_blocks

def case_insensitive_regexp(regexp, collate_table):
    """Make regular expression case insensitive"""
    result = ""
    inside_brackets = 0
    for c in regexp:
        if c == "[":
            inside_brackets = 1

        if inside_brackets:
            eqv_chars = c
        else:
            eqv_chars = _equivalent_chars(c, collate_table)

        if len(eqv_chars) > 1:
            result += "[%s]" % eqv_chars
        else:
            result += eqv_chars

        if c == "]":
            inside_brackets = 0

    return result

def _equivalent_chars(c, collate_table):
    """Find all chars equivalent to c in collate table"""
    c_ord = ord(c)
    if c_ord >= len(collate_table):
        return c

    result = ""
    norm_char = collate_table[c_ord]
    next_index = 0
    while 1:
        next_index = collate_table.find(norm_char, next_index)
        if next_index == -1:
            break
        result += chr(next_index)
        next_index += 1

    return result

def read_ranges_to_gaps_and_last(read_ranges):
    """Return all texts excluded from read_ranges.

    @return: Returns a 2-tuple of a list and the first possibly
    unread text number after the last read range. The text number
    could be larger than the highest local number, if we have read
    everything in the conference. The list contains a 2-tuples for
    each gap in the read ranges, where each tuple is the first
    unread text in the gap and the length of the gap.
    """
    gaps = []
    last = 1
    for read_range in read_ranges:
        gap = read_range.first_read - last
        if gap > 0:
            gaps.append((last, gap))
        last = read_range.last_read + 1
    return gaps, last


from cntk import ops, cpu, parameter, NDArrayView, input
import numpy as np
import cntk as C
import os
import sys
import pytest

abs_path = os.path.dirname(os.path.abspath(__file__))
custom_convolution_ops_dir = os.path.join(abs_path, "..", "..", "..", "..", "Examples", "Extensibility", "BinaryConvolution")
sys.path.append(custom_convolution_ops_dir)

from custom_convolution_ops import *
import cntk.contrib.netopt as nopt

# checks the functionality of the binary convolution custom function
def test_native_binary_function():
    # user functions need to be registered before being callable by python
    if not nopt.native_convolve_function_registered:
      pytest.skip("Could not find {0} library. "
        "Please check if HALIDE_PATH is configured properly "
        "and try building {1} again"
        .format('Cntk.BinaryConvolution-' + C.__version__.rstrip('+'),
        'Extnsibiliy\\BinaryConvolution'))

    # be sure to only run on CPU, binary convolution does not have GPU support for now
    dev = C.cpu()
    # create an arbitrary input mimicking a realistic cifar input
    x = input((64, 28, 28))
    # random filter weights for testing
    w = parameter((64, 64, 3, 3), init=np.reshape(2*(np.random.rand(64*64*3*3)-.5), (64, 64, 3, 3)), dtype=np.float32, device=dev)

    # set the convolution parameters by passing in an attribute dictionary
    #attributes = {'stride' : 1, 'padding' : False, 'size' : 3}

    attributes = {'stride' : 1,
                  'padding' : False,
                  'size' : 3,
                  'h' : 28,
                  'w' : 28,
                  'channels' : 64,
                  'filters' : 64 }

    # define the binary convolution op
    op = ops.native_user_function('NativeBinaryConvolveFunction', [w, x], attributes, 'native_binary_convolve')

    # also define an op using python custom functions that should have the same output
    op2 = C.convolution(CustomMultibitKernel(w, 1), CustomSign(x), auto_padding = [False])
    # create random input data
    x_data = NDArrayView.from_dense(np.asarray(np.reshape(2*(np.random.rand(64*28*28)-.5), (64, 28, 28)),dtype=np.float32), device=dev)
    # evaluate the CPP binary convolve
    result = op.eval({x : x_data}, device=dev)

    # evaluate the python emulator
    result2 = op2.eval({x : x_data}, device=dev)
    native_times_primitive = op.find_by_name('native_binary_convolve')
    # assert that both have the same result
    '''
    Disable this tempororily. Needs to investigate and fix the halide
    code to match the previous test behavior.
    '''
    #assert np.allclose(result, result2, atol=0.001)


"""
 Analyzer
 API for analyzing user activity in relation with hobbies
 API will receive signals from home such as create , view, love, share, comment or follow
 API can manage hobby_map and primary hobby of user for personalized experience
"""
import math
from insight.models import *
from insight.workers.interface import AnalyzerInterface
from celery import shared_task
from insight.utils import next_sunday
from insight.competitions.main import RankReportManager
from rest_framework.authtoken.models import Token
from django.db.models import Q, QuerySet, ExpressionWrapper, F, DecimalField
from math import log

class Analyzer(AnalyzerInterface):
    counts = None

    @staticmethod
    def calculate_freshness_score(post: Post) -> float:
        diff = get_ist() - post.created_at
        return (2 * math.exp(-diff.days / 4)) + 0.8

    @staticmethod
    def audit_post_counts(post: Post, after=None) -> dict:
        audits = {'love': post.loves.count(), 'view': post.views.count(), 'comment': post.comments.count(),
                  'share': post.shares.count(), 'up_vote': post.up_votes.count(), 'down_vote': post.down_votes.count()}
        return audits

    def manage_hobby_report(self, hobby: str, **reports) -> HobbyReport:
        hobbies: QuerySet = Hobby.objects.filter(code_name=hobby)
        if not hobbies.exists():
            return None
        hobby: Hobby = hobbies.first()
        hobby_reports: QuerySet = HobbyReport.objects.filter(Q(account=self.user) & Q(hobby=hobby))
        hobby_report: HobbyReport = None
        if hobby_reports.exists():
            hobby_report: HobbyReport = hobby_reports.first()
        else:
            hobby_report: HobbyReport = HobbyReport.objects.create(
                account=self.user, hobby=hobby
            )
        for key, value in reports.items():
            # only view,love, share supported
            hobby_report.__dict__[f'{key}s'] += value
        hobby_report.save()
        return hobby_report

    def score_post(self, counts: dict, for_comp=False) -> float:
        score: float = (self.WEIGHT_LOVE * counts['love']) + (self.WEIGHT_VIEW * counts['view']) + \
                       (self.WEIGHT_SHARE * counts['share'])
        score += (self.WEIGHT_UP_VOTE * counts['up_vote']) - (self.WEIGHT_DOWN_VOTE * counts['down_vote'])

        return score * 0.01

    def manage_score_post(self, post: Post, is_new: bool = False, after: datetime = None, view_action: bool = False):
        post.freshness_score = self.calculate_freshness_score(post)
        post.score = self.score_post(self.audit_post_counts(post))
        if view_action:
            post.score -= self.WEIGHT_LOVE/2
        post.net_score = post.freshness_score + post.score
        post.last_modified = get_ist()
        post.save()
        return post

    def manage_scoreboard(self, post: Post, **actions):
        scoreboards: QuerySet = Scoreboard.objects.filter(Q(account=post.account) & Q(expires_on__gte=get_ist()) &
                                                          Q(created_at__lte=get_ist()))
        if not scoreboards.exists():
            scoreboard: Scoreboard = Scoreboard.objects.create(account=post.account, original_creation=get_ist(),
                                                               created_at=get_ist(),
                                                               expires_on=next_sunday(get_ist()))
        else:
            scoreboard: Scoreboard = scoreboards.first()
        if not scoreboard.posts.filter(post_id=post.post_id).exists():
            scoreboard.posts.add(post)
        for key, value in actions.items():
            scoreboard.__dict__[f'{key}s'] += value
        scoreboard.save()
        return scoreboard

    def user_activity(self, scoreboard: Scoreboard):
        posts: QuerySet = scoreboard.posts.all()
        if posts.exists():
            number: int = posts.count()
            avg_number_in_week: float = number / 4  # minimum required posts are 4 in a week
            if avg_number_in_week > 1:
                scoreboard.retention = log(avg_number_in_week)
                scoreboard.save()
        reports: QuerySet = HobbyReport.objects.select_related('hobby').filter(account=self.user) \
            .annotate(focus=ExpressionWrapper(
            F('views') + F('shares') + F('loves') + F('comments'), output_field=DecimalField()
        )).order_by('-focus')
        if reports.exists():
            report: HobbyReport = reports.first()
            self.user.primary_hobby = report.hobby.code_name
            self.user.save()

    @staticmethod
    @shared_task
    def log_data(path: str, header: dict, body):
        if 'HTTP_AUTHORIZATION' not in header:
            return None
        tokens: QuerySet = Token.objects.filter(key=header['HTTP_AUTHORIZATION'].replace("Token ", ""))
        if not tokens.exists():
            return None
        user: Account = tokens.first().user
        data_logs: QuerySet = DataLog.objects.filter(user_id=user.account_id)
        data_log: DataLog = None
        if data_logs.exists():
            data_log = data_logs.first()
        else:
            data_log = DataLog(user_id=user.account_id, created_at=get_ist(),)
        data_log.logs.append({"header": {}, "body": body, "process": path})
        data_log.save()
        return None

    @staticmethod
    @shared_task
    def background_task(user_id: str, post_id: str, created: bool = False, *count, **kwargs) -> None:
        users: QuerySet = Account.objects.filter(account_id=user_id)
        posts: QuerySet = Post.objects.filter(post_id=post_id)
        if users.count() == 0 or posts.count() == 0:
            return None
        user = users.first()
        post = posts.first()
        analyzer = Analyzer(user)
        if 'hobby' in kwargs and 'report' in kwargs:
            analyzer.manage_hobby_report(kwargs['hobby'], **kwargs['report'])
        if 'post' in kwargs:
            del kwargs['post']
        analyzer.user_activity(analyzer.manage_scoreboard(post, **kwargs))
        return None

    def analyzer_create_post(self, post: Post):
        self.manage_hobby_report(post.hobby.code_name, post=1)
        self.manage_score_post(post, is_new=True)
        self.background_task.delay(self.user.account_id, post.post_id, post=1),

    def analyze_post_action(self, post: Post, for_test: bool = False, **actions):
        self.manage_score_post(post, view_action='view' in actions)
        RankReportManager.update_score(post)
        self.background_task.delay(self.user.account_id, post.post_id, **actions)


import cv2

image = cv2.imread("Tank64229left.png")
#image = cv2.imread("Test1.png")

# grayscale the image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#cv2.imwrite("gray_tank64229.png",gray)
# apply a Gaussian blur
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# threshold the image
(t, binary) = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY)
# find contours
(_, contours, _) = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
image_cropped_contoured_drawn = cv2.drawContours(image.copy(), contours, -1, (0, 255, 0), 3)
cv2.imwrite("exploration.jpg", image_cropped_contoured_drawn)

f = open('co_ordinates.txt', 'w+')
for c in range(len(contours)):
    n_contour = contours[c]
    for d in range(len(n_contour)):
        XY_Coordinates = n_contour[d]
        print(XY_Coordinates)
        f.write("%s\n" % (XY_Coordinates))


from ...other import basicComponent
from .bscChromo import basicChromo
from statistics import mean, stdev
import unittest

class basicRepresentation(basicComponent):

    last_ID = 0

    __slots__ = ('chromo', 'mapped', 'ID', 'attrs', 'hsh', 'val_min', 'val_max')

    def __init__(self, *args, **kargs):

        super().__init__(*args, **kargs)

        self.chromo = kargs.get('chromo', None)
        self.mapped = kargs.get('mapped', None)

        self.ID = kargs.get('ID', self.__get_next_ID())

        self.attrs = kargs.get('attrs', {})

        self.hsh = kargs.get('hsh', None)

        self.val_min = kargs.get('val_min', None)
        self.val_max = kargs.get('val_max', None)

    def __del__(self):
        del self.chromo
        del self.mapped
        del self.attrs
        del self.val_min
        del self.val_max
        self.ID, self.hsh = None, None
        super().__del__()

    ''' chromosome '''
    # Returns chromosome
    def get_chromo(self, return_copy=True):
        if return_copy:
            return self.chromo.copy()
        return self.chromo

    def __len__(self):
        return self.get_chromo(return_copy=False).__len__()
    # Applies maps
    def _map(self, chromo):
        self.log.exception('_map not implemented', err=NotImplementedError)

    # Returns chromosome mapped
    def get_mapped(self, return_copy=True):
        if self.mapped is None or self.get_chromo(return_copy=False).get_fit() is None:
            self.mapped = self._map(self.get_chromo(return_copy=False))
        if return_copy:
            self.mapped.copy()
        return self.mapped

    # Returns min/max/range of values encodable post mapping
    def get_valmin(self):
        return self.val_min
    def get_valmax(self):
        return self.val_max
    def get_valrange(self):
        return self.val_max - self.val_min

    ''' Inheritance '''
    # Sets chromosome
    def set_chromo(self, new_chromo, set_copy=False, clear_attrs=True):
        if isinstance(new_chromo, list):
            self.get_chromo(return_copy=False).set_chromo(new_chromo,\
                                                        clear_fit=clear_attrs)
        elif isinstance(new_chromo, basicChromo):
            if not set_copy:
                self.chromo = new_chromo
            else:
                self.chromo = new_chromo.copy()
        else:
            self.log.exception('Attempted to set a chromo without a proper '+\
                               'chromosome', err=TypeError)

        if clear_attrs:
            self.clear_attrs()
            self.set_fit(None)

    # Fancy inheriting, accepts new chromo and then compiles information from
    #   parents
    def inherit(self, new_chromo, *parents):
        self.set_chromo(new_chromo)
        # Adds informatoin about the parents
        if len(parents) == 1:
            # Set new chromo
            pfit = parents[0].get_fit()
            p_ID = parents[0].get_ID()
            plen = len(parents[0])
            self.update_attrs({'pfits':[pfit],\
                               'avg_pfit':pfit,\
                               'stdev_pfit':0,\
                               'range_pfit':0,\
                               'max_pfit':pfit,\
                               'min_pfit':pfit,\
                               'pIDs':[p_ID],\
                               'plens':[plen],\
                               'avg_plen':plen,\
                               'stdev_plen':0,\
                               'range_plen':0,\
                               'min_plen':plen,\
                               'max_plen':plen})
        elif len(parents) > 1:
            # Get some values from parents
            pfits = [parent.get_fit() for parent in parents]
            plens = [len(parent.get_chromo(return_copy=False)) for parent in parents]
            min_pfit, max_pfit = min(pfits), max(pfits)
            min_plen, max_plen = min(plens), max(plens)
            self.update_attrs({'pfits':pfits,\
                               'avg_pfit':mean(pfits),\
                               'stdev_pfit':stdev(pfits),\
                               'range_pfit':max_pfit-min_pfit,\
                               'max_pfit':max_pfit,\
                               'min_pfit':min_pfit,\
                               'pIDs':[parent.get_ID() for parent in parents],\
                               'plens':plens,\
                               'avg_plen':mean(plens),\
                               'stdev_plen':stdev(plens),\
                               'range_plen':max_plen-min_plen,\
                               'min_plen':min_plen,\
                               'max_plen':max_plen})

    ''' Fitness '''
    # Returns fitness
    def get_fit(self):
        return self.get_chromo(return_copy=False).get_fit()
    # Sets the fitness, also places it in attrs for easy info compilation
    def set_fit(self, new_fit):
        self.set_attr('fit', new_fit)
        self.get_chromo(return_copy=False).set_fit(new_fit)
    # Clears fitness
    def clear_fit(self):
        self.get_chromo(return_copy=False).set_fit(None)
        try:
            self.del_attr('fit')
        except:
            pass
        return
    # If comparing against another individual, will check if mapped chromosome
    #    is the same.  if given a chromosome, will check if chromsome is the same
    #   if given an integer or a float, will check if int/float is the same
    def __eq__(self, other):
        if isinstance(other, basicRepresentation):
            return (self.get_mapped().__eq__(other.get_mapped()))
        if isinstance(other, basicChromo):
            return (self.get_chromo(return_copy=False).__eq__(other))
        elif isinstance(other, (int, float)):
            return self.get_fit().__eq__(other)
        else:
            try:
                return self.get_fit().__eq__(float(other))
            except:
                pass
        self.log.exception('Must compare another individual or an int/float',\
                           err=TypeError)
    # Sees if fitnesses are equal to, can take int, float, or individual
    def eq_fit(self, other):
        if isinstance(other, (basicRepresentation, basicChromo)):
            return (self.get_fit().__eq__(other.get_fit()))
        elif isinstance(other, (int, float)):
            return self.get_fit().__eq__(other)
        else:
            try:
                return self.get_fit().__eq__(float(other))
            except:
                pass
        self.log.exception('Must compare another individual or an int/float',\
                           err=TypeError)
    # Sees if two chromosomes are equal
    def eq_chromo(self, other):
        if isinstance(other, (basicRepresentation, basicChromo)):
            return (self.get_chromo(return_copy=False).\
                        __eq__(other.get_chromo(return_copy=False)))
        self.log.exception('Must compare another indv to another indv or chromo')
    # Sees if two mapped solutions are equal
    def eq_mapped(self, other):
        if isinstance(other, basicRepresentation):
            return (self.get_mapped().__eq__(other.get_mapped()))
        self.log.exception('Must compare another indv to another indv')

    # Sees if fitnesses are greater than, can take int, float, or individual
    def __gt__(self, other):
        if isinstance(other, basicRepresentation):
            return (self.get_fit().__gt__(other.get_fit()))
        elif isinstance(other, (int, float)):
            return (self.get_fit().__gt__(other))
        else:
            try:
                return self.get_fit().__gt__(float(other))
            except:
                pass
        self.log.exception('Must compare another individual or an int/float',\
                           err=TypeError)
    # Sees if fitnesses are greater or equal to, can take int, float, or individual
    def __ge__(self, other):
        if isinstance(other, basicRepresentation):
            return (self.get_fit().__ge__(other.get_fit()))
        elif isinstance(other, (int, float)):
            return (self.get_fit().__ge__(other))
        else:
            try:
                return self.get_fit().__ge__(float(other))
            except:
                pass
        self.log.exception('Must compare another individual or an int/float',\
                           err=TypeError)
    # Sees if fitnesses are less than, can take int, float, or individual
    def __lt__(self, other):
        if self.get_fit() is None:
            self.log.exception('Cannot compare indv with no fit', err=ValueError)
        if other is None:
            self.log.exception('Cannot compare indv with None', err=TypeError)
        elif isinstance(other, basicRepresentation):
            if other.get_fit() is None:
                self.log.exception('Cannot compare indv with no fit', err=ValueError)
            return (self.get_fit().__lt__(other.get_fit()))
        elif isinstance(other, (int, float)):
            return (self.get_fit().__lt__(other))
        else:
            try:
                return self.get_fit().__lt__(float(other))
            except:
                pass

        self.log.exception('Must compare another individual or an int/float',\
                           err=TypeError)
    # Sees if fitnesses are less or equal to, can take int, float, or individual
    def __le__(self, other):
        if isinstance(other, basicRepresentation):
            return (self.get_fit().__le__(other.get_fit()))
        elif isinstance(other, (int, float)):
            return (self.get_fit().__le__(other))
        else:
            try:
                return self.get_fit().__le__(float(other))
            except:
                pass
        self.log.exception('Must compare another individual or an int/float',\
                           err=TypeError)

    ''' Utility '''
    # Returns hash of contained mapped chromosome
    def __hash__(self):
        if self.mapped is None or self.get_chromo(return_copy=False).get_fit() is None:
            self.hsh = tuple(self.get_mapped()).__hash__()
        return self.hsh
    def set_hash(self, new_hash):
        self.hsh = new_hash
    def get_hash(self):
        return self.__hash__()

    # Returns copy of the individual
    def copy(self, copy_ID=False):
        if copy_ID:
            return basicRepresentation(log_name=self.log.getLogKey(),\
                                      chromo=self.get_chromo(return_copy=True),\
                                      fit=self.get_fit(),\
                                      attrs=self.get_attrs(return_copy=True),\
                                      ID=self.get_ID(),\
                                      val_min=self.get_valmin(),\
                                      val_max=self.get_valmax())
        return basicRepresentation(log_name=self.log.getLogKey(),\
                                   chromo=self.get_chromo(return_copy=True),\
                                   fit=self.get_fit(),\
                                   attrs=self.get_attrs(return_copy=True),\
                                   val_min=self.get_valmin(),\
                                   val_max=self.get_valmax())
    def __copy__(self):
        return self.copy()

    ''' attrs '''

    def clear_attrs(self):
        self.attrs = {}

    def set_attr(self, attr_name, value):
        self.attrs.__setitem__(attr_name, value)

    def get_attr(self, attr_name):
        return self.attrs.__getitem__(attr_name)

    def get_attrs(self, return_copy=True, add_len=True):
        if add_len:
            self.add_len_to_attrs()
        if return_copy:
            return self.attrs.copy()
        return self.attrs

    def update_attrs(self, dct):
        if not isinstance(dct, dict):
            self.log.exception('update_attrs takes a dictionary', err=TypeError)
        self.attrs.update(dct)

    def incr_attr(self, *args):
        if len(args) == 2:
            self.attrs.__setitem__(args[0], self.attrs.get(args[0], 0) + \
                                    args[1])
        elif len(args) == 1:
            self.attrs.__setitem__(args[0], self.attrs.get(args[0], 0) + 1)
        else:
            self.log.exception('Should be 1-2 arguments for incr_attr',\
                               err=ValueError)

    def add_len_to_attrs(self):
        self.set_attr('chromo_len', self.get_chromo(return_copy=False).__len__())

    def del_attr(self, attr_name):
        del self.attrs[attr_name]

    ''' ID '''

    def get_ID(self):
        return self.ID

    @classmethod
    def __get_next_ID(self):
        ''' Returns next ID '''
        basicRepresentation.last_ID += 1
        return basicRepresentation.last_ID-1

    def to_dict(self, return_copy=True, extract_attrs=False):
        if extract_attrs:
            dct = {'ID':self.get_ID(),\
                    'chromo':self.get_chromo(return_copy=False)\
                                        .to_list(return_copy=return_copy),\
                    'mapped':self.get_mapped(return_copy=return_copy),\
                    'fit':self.get_fit()}
            dct.update(self.get_attrs())
            return dct
        return {'ID':self.get_ID(),\
                'chromo':self.get_chromo(return_copy=False)\
                                    .to_list(return_copy=return_copy),\
                'mapped':self.get_mapped(return_copy=return_copy),\
                'fit':self.get_fit(),\
                'attrs':self.get_attrs(return_copy=return_copy)}

class basicRepresentation_unittest(unittest.TestCase):

    def test_AAA_init(self):
        brep = basicRepresentation()

    def test_AAB_setchromo(self):
        logging.disable()
        brep = basicRepresentation()
        chromo = basicChromo(len=10, min=0, max=1, dtype=int)
        self.assertEqual(brep.get_chromo(return_copy=False), None, \
                         msg='failed to get chromo')
        brep.set_chromo(chromo)
        self.assertEqual(brep.get_chromo(return_copy=False), chromo, \
                         msg='failed to get chromo')
        logging.disable(logging.NOTSET)

    def test_AAC_map(self):
        logging.disable()
        brep = basicRepresentation()
        chromo = basicChromo(len=10, min=0, max=1, dtype=int)
        brep.set_chromo(chromo)
        self.assertEqual(brep.get_chromo(), chromo, msg='failed to get_chromo')
        self.assertRaises(NotImplementedError, brep.get_mapped)
        logging.disable(logging.NOTSET)

    def test_AAD_fit(self):
        logging.disable()
        brep = basicRepresentation()
        self.assertEqual(brep.get_fit(), None, msg='failed to get_fit')
        brep.set_fit(4)
        self.assertEqual(brep.get_fit(), 4, msg='failed to get_fit')
        logging.disable(logging.NOTSET)

    def test_AAE_copy(self):
        logging.disable()
        brep = basicRepresentation()
        brep.set_chromo(basicChromo(len=10, min=0, max=100, dtype=int))
        brep2 = brep.copy()
        logging.disable(logging.NOTSET)

    def test_AAF_comparison(self):
        logging.disable()
        brep = basicRepresentation()
        brep2 = basicRepresentation()
        brep.set_fit(10)
        brep2.set_fit(20)

        self.assertFalse(brep>brep2, msg='> failed')
        self.assertFalse(brep>=brep2, msg='>= failed')
        self.assertFalse(brep2<brep, msg='> failed')
        self.assertFalse(brep2<=brep, msg='>= failed')

        self.assertTrue(brep<brep2, msg='< failed')
        self.assertTrue(brep<=brep2, msg='<= failed')
        self.assertTrue(brep2>brep, msg='< failed')
        self.assertTrue(brep2>=brep, msg='<= failed')

        self.assertFalse(brep.eq_fit(brep2), msg='eq_fit failed')
        brep2.set_fit(10)
        self.assertTrue(brep.eq_fit(brep2), msg='eq_fit failed')

    def test_AAG_attrs(self):
        brep = basicRepresentation()
        self.assertTrue(brep.get_attrs(add_len=False) == {}, \
                        msg='Either get_attrs failed or started with attrs')
        brep.set_attr('x', 10)
        self.assertEqual(brep.get_attr('x'),10, msg='Failed to get_attr')
        brep.clear_attrs()
        self.assertEqual(brep.get_attrs(add_len=False),{}, msg='Failed to clear')
        brep.set_attr('x', 100)
        brep.del_attr('x')
        self.assertEqual(brep.get_attrs(add_len=False),{}, msg='Failed to del')
        brep.set_fit(10)
        self.assertEqual(brep.get_attrs(add_len=False),{'fit':10}, \
                         msg='Failed to add fit to attrs')
        brep.incr_attr('fit', 10)
        self.assertEqual(brep.get_attr('fit'),20, \
                         msg='incr_attr or get_attrs failed')
        brep.clear_attrs()
        brep.update_attrs({'test':4})
        self.assertEqual(brep.get_attrs(add_len=False), {'test':4}, msg='Failed to update')

if __name__ == '__main__':
    unittest.main()


import tensorflow as tf

import os, argparse

dir = os.path.dirname(os.path.realpath(__file__))

def freeze_graph(model_dir, output_node_names):
  """Extract the sub graph defined by the output nodes and convert
    all its variables into constant
    Args:
        model_dir: the root folder containing the checkpoint state file
        output_node_names: a string, containing all the output node's names,
                            comma separated
  """

  if not tf.gfile.Exists(model_dir):
    raise AssertionError(
      "Export directory doesn't exists. Please specify an export "
      "directory: %s" % model_dir)

  if not output_node_names:
    print("You need to supply the name of a node to --output_node_names.")
    return -1

  checkpoint = tf.train.get_checkpoint_state(model_dir)
  input_checkpoint = checkpoint.model_checkpoint_path
  print(input_checkpoint)

  absolute_model_dir = '/'.join(input_checkpoint.split('/')[:-1])
  output_graph = os.path.join(absolute_model_dir,'freeze_model.pb')

  clear_devices =True

  with tf.Session(graph=tf.Graph()) as sess:
    saver = tf.train.import_meta_graph(input_checkpoint + '.meta', clear_devices=clear_devices)

    saver.restore(sess, input_checkpoint)

    print([n.name for n in tf.get_default_graph().as_graph_def().node])

    output_graph_def = tf.graph_util.convert_variables_to_constants(
      sess,
      tf.get_default_graph().as_graph_def(),
      output_node_names.split(',')

      )

    with tf.gfile.GFile(output_graph, 'wb') as f:
      f.write(output_graph_def.SerializeToString())
    print("%d ops in the final graph." % len(output_graph_def.node))

  return output_graph_def

if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument("--model_dir", type=str, default="", help="Model folder to export")
  parser.add_argument("--output_node_names", type=str, default="", help="The name of the output nodes, comma separated.")
  args = parser.parse_args()
  freeze_graph(args.model_dir, args.output_node_names)


"""
Created on Wed Dec 14 22:11:37 2022

@author: alexandre
"""

import epics
import matplotlib.pyplot as plt
from time import sleep

"""
           PV Information

Object: Text Monitor
Thu Dec 15 06:49:54 CST 2022

alexandre:PM1:X:positionM
======================================
DESC: PM1:X
RTYP: calc
TYPE: DBF_DOUBLE
COUNT: 1
ACCESS: RW
IOC: localhost:5064
VALUE: -0.06
STAMP: Thu Dec 15, 2022 06:49:54.777
ALARM: NO

PRECISION: 2
HOPR: 5  LOPR: -5
"""

"""
           PV Information

Object: Slider
Thu Dec 15 11:37:31 CST 2022

alexandre:H1:setCurrentC
======================================
DESC: H1 current setpoint
RTYP: ao
TYPE: DBF_DOUBLE
COUNT: 1
ACCESS: RW
IOC: localhost:5064
VALUE: -1.28
STAMP: Thu Dec 15, 2022 08:34:18.962
ALARM: NO

PRECISION: 2
HOPR: 5  LOPR: -5
"""

#%% channels
PM1_X = epics.PV("alexandre:PM1:X:positionM")
PM1_X_current = epics.PV("alexandre:H1:setCurrentC")

#%% control parameters
PM1_X_set_point = 0.0
PM1_X_error     = 0.001

PM1_X_current_step = .1

#%% control loop
PM1_Xvalues =[]
PM1_X_currentvalues =[]

PM1_X_current.put(6)
sleep(.2)

for n in range(100):
    PM1_Xvalues.append(PM1_X.get())
    PM1_X_currentvalues.append(PM1_X_current.get())
    if   abs(PM1_Xvalues[-1] - PM1_X_set_point) <= PM1_X_error: break
    if   PM1_Xvalues[-1] > PM1_X_set_point                    : PM1_X_current.put(PM1_X_currentvalues[-1] - PM1_X_current_step)
    elif PM1_Xvalues[-1] < PM1_X_set_point                    : PM1_X_current.put(PM1_X_currentvalues[-1] + PM1_X_current_step)

    sleep(.2)

#%%
plt.close("all")
fig , (ax1, ax2) = plt.subplots(2,1)
ax1.plot(PM1_Xvalues)
ax1.set_ylabel("X tranverse position, $X_1$ [$\mu m$]")
ax1.grid(True)

ax2.plot(PM1_X_currentvalues)
ax2.set_ylabel("Horizontal Magnet Current, $I_1$ [A]")
ax2.set_xlabel("time, t [s]")
ax2.grid(True)


import pygame
from ray import Ray
from wall import Wall
import numpy as np

def dist(x1, y1, x2, y2):
	return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)

class Point:
	def __init__(self, pos, n):
		self.x, self.y = pos
		self.rays = []

		for i in range(n):
			self.rays.append(Ray(self.x, self.y, i * 2 * np.pi / n))

	def update(self, pos):
		self.x, self.y = pos
		for ray in self.rays:
			ray.x = self.x
			ray.y = self.y

	def look(self, walls, screen):
		for i in range(len(self.rays)):
			ray = self.rays[i]
			clostest = None
			record = np.Inf

			for wall in walls:
				x1, y1 = ray.cast(wall)
				if x1 == -100 and y1 == -100:
					continue
				d = dist(x1, y1, self.x, self.y)

				if d < record:
					clostest = (x1, y1)
					record = d
			if clostest != None:
				pygame.draw.line(screen, pygame.Color(255, 255, 255), (ray.x, ray.y), clostest)

	def draw(self, screen):
		pygame.draw.circle(screen, pygame.Color(255, 255, 255), (self.x, self.y), 3)


# For correcting the gait
import h5py
import keras
import numpy as np
from keras.models import load_model

def Recog_Action(data, modelname):
    #  data: a dictionary made by RecogData.Recog_data()
    model = load_model(modelname)
    x_action = data['x_action']
    y_action = model.predict(x_action)
    y_res = []

    #  For the 25 in the beginning
    for i in range(25):
        y_res.append(0)

    for i in range(y_action.shape[0]):
        index = 0
        for j in range(4):
            if y_action[i, j] == max(y_action[i]) and y_action[i, j] >= 0.7:
                y_res.append(j+1)
                index = 1
                break
        if index == 0:
            y_res.append(0)

    return y_res


"""Pullers Collection"""
# -*- coding: utf-8 -*-
from __future__ import annotations
import os
import asyncio
from asyncio import Future
from typing import Iterable, Mapping, Any, Callable, Awaitable, Sequence, TypeVar, TYPE_CHECKING
from abc import abstractmethod
from httpx import Request, Response, Timeout
from utils import DummyAioFileStream as Dummyf, EventHint as Hint, LoggerLike, to_ordinal
from utils import ActionChain, AsyncActionChain, EventHook, StrChain
from utils import ensure_async
from httpx._types import HeaderTypes, ProxiesTypes, CookieTypes, QueryParamTypes
import aiofiles as aiof
import httpx

if TYPE_CHECKING:
    import rich.progress

__all__ = [
    "Request",
    "Response",
    "HeaderTypes",
    "ProxiesTypes",
    "CookieTypes",
    "QueryParamTypes",
    "HttpActionType",
    "AsyncPuller",
    "AsyncPullerEventHook",
    "AsyncActionChain",
    "AsyncHttpActionChain",
    "MaxRetryReached",
    "Modifier",
]

class MaxRetryReached(Exception):
    """Raised when the maximum number of retries is reached."""

class BasePuller:
    """Base class for pullers."""
    @property
    def event_hooks(self) -> EventHook:
        """Event hook for this puller."""
        raise NotImplementedError()

    @abstractmethod
    def pull(self, url: str, path: str) -> Any:
        """Pull the file from the url to the path."""

class BaseWorker:
    """Base class for workers."""

class BaseMaster:
    """Base class for masters."""

HttpActionType = Callable[[Request | Response], Awaitable]
ActionVar = TypeVar('ActionVar', bound=Callable)

class AsyncHttpActionChain(ActionChain[HttpActionType]):
    """Async Http Action Chain"""

    def __init__(self, actions: Iterable[HttpActionType] = None) -> None:
        super().__init__(actions=actions)

class AsyncPullerEventHook(EventHook):
    """Async Puller Event Hook"""

    def __init__(self, chain: Mapping[str, Iterable[Callable[..., Any]]] | None = None):
        self.hook("request", AsyncHttpActionChain())
        self.hook("response", AsyncHttpActionChain())
        self.hook("worker.spawn", AsyncActionChain())
        self.hook("worker.destroy", AsyncActionChain())
        self.hook("worker.start", AsyncActionChain())
        self.hook("worker.response_get", AsyncActionChain())
        self.hook("worker.bytes_get", AsyncActionChain())
        self.hook("worker.retry", AsyncActionChain())
        self.hook("worker.success", AsyncActionChain())
        self.hook("worker.fail", AsyncActionChain())
        self.hook("puller.spawn", AsyncActionChain())
        self.hook("puller.destroy", AsyncActionChain())
        self.hook("puller.join", AsyncActionChain())
        EventHook.__init__(self, chain=chain)

class AsyncWorker(BaseWorker):
    def __init__(
        self,
        puller: AsyncPuller,
        url: str,
        path: str | None,
        retry: int,
        overwrite: bool,
        *,
        future: Future | None = None,
        timeout: Timeout | float | None,
        extra_headers: HeaderTypes | None,
        extra_params: QueryParamTypes | None,
        extra_cookies: CookieTypes | None,
        **kw
    ):
        self.puller = puller
        self.url = url
        self.path = path
        self.future = future
        self.timeout = timeout
        self.max_retry = retry
        self.overwrite = overwrite
        self.extra_headers = extra_headers
        self.extra_params = extra_params
        self.extra_cookies = extra_cookies
        self.kw = kw
        self.event_hooks = AsyncPullerEventHook(
            self.puller.event_hooks)  # snapshot

    async def run(self):
        """Run the worker."""
        event_hooks = self.event_hooks
        try:
            await event_hooks.aemit("worker.start", self)
            if self.path and os.path.exists(self.path) and not self.overwrite:
                raise FileExistsError(f"{self.path} already exists")
            client = self.puller.client
            retry = 0
            while True:
                try:
                    # Open file in async mode, if path is None, write to void
                    async with aiof.open(self.path, "wb") if self.path else Dummyf() as f:
                        # Establish connection
                        async with client.stream(
                            method=self.kw.pop("method", "GET"),
                            url=self.url,
                            params=self.extra_params,
                            headers=self.extra_headers,
                            cookies=self.extra_cookies,
                            timeout=self.timeout or self.puller.client.timeout,
                            **self.kw
                        ) as r:
                            await event_hooks.aemit("worker.response_get", self, r)
                            # Iterate over response chunks
                            # 256KB
                            async for chunk in r.aiter_bytes(chunk_size=2**18):
                                await event_hooks.aemit("worker.bytes_get", self, r, chunk)
                                await f.write(chunk)
                            await f.flush()
                            await event_hooks.aemit("worker.success", self, r)
                    # set result for placeholder
                    self.future.set_result(self.path)
                    break  # Quit successfully
                # Retry on network IO error
                except (httpx.HTTPError, httpx.StreamError) as e:
                    retry += 1
                    if retry > self.max_retry:
                        raise MaxRetryReached(
                            f"Max retry reached: {retry} times") from e
                    await event_hooks.aemit("worker.retry", self, e, retry)
                    await asyncio.sleep(min(30, 1.7 ** retry))
        # Fatal Errors
        except (Exception, asyncio.CancelledError) as e:
            handled = await event_hooks.aemit("worker.fail", self, e)
            if True not in handled:
                self.future.cancel()
                raise e
        finally:
            await event_hooks.aemit("worker.destroy", self)
            self.puller._workers.get_nowait()
            self.puller._workers.task_done()  # workers count - 1

    def __repr__(self) -> str:
        return f"AsynWorker({self.url}, {self.path})"

class AsyncMaster(BaseMaster):
    def __init__(self, puller: AsyncPuller):
        self.puller = puller

    async def run(self):
        """Run the master."""
        try:
            buffer = self.puller._buffer
            workers = self.puller._workers
            while True:
                worker = await buffer.get()
                if worker is None:  # Kill signal
                    buffer.task_done()
                    break
                await workers.put(worker)  # blocks when max_workers reached
                self.puller._loop.create_task(worker.run())
                buffer.task_done()
                await asyncio.sleep(self.puller.interval)  # sleep a while
        finally:
            self.puller._master = None  # Reset master

class AsyncPuller(BasePuller):
    """### Async Puller"""

    def __init__(
        self,
        *,
        headers: HeaderTypes | None = None,
        params: QueryParamTypes | None = None,
        proxies: ProxiesTypes | None = None,
        cookies: CookieTypes | None = None,
        event_hooks: Mapping[str, Sequence[Callable]] = None,
        interval: float = 0.0,
        max_workers: int = 8,
        timeout: Timeout | float | None = 10,
        retry: int = 3,
        overwrite: bool = False,
        loop: asyncio.AbstractEventLoop | None = None,
        **kw
    ):
        """### Init Puller
        * `headers`: Default headers for all requests
        * `params`: Default query params for all requests
        * `proxies`: Default proxies for all requests
        * `cookies`: Default cookies for all requests
        * `event_hooks`: dict[str, Iterable[Callable]]
        * `interval`: Interval between each request
        * `max_workers`: Max downloading threads count
        * `timeout`: Timeout for each request
        * `retry`: Max retry times for each request
        * `overwrite`: Overwrite existing files
        * `loop`: Event loop
        * `**kw`: Other keyword arguments for httpx.Client
        """
        self._loop = loop
        self.interval = interval
        self.max_retry = max(retry, 0)
        self.overwrite = overwrite

        self._proxies = proxies
        self._master: AsyncMaster | None = None
        self._buffer: asyncio.Queue = asyncio.Queue()  # Pending workers
        self._workers: asyncio.Queue = asyncio.Queue(
            maxsize=max_workers)  # Running workers

        limits = httpx.Limits(
            max_connections=None,
            max_keepalive_connections=None,
            keepalive_expiry=10)

        self._client = httpx.AsyncClient(
            headers=headers,
            params=params,
            proxies=proxies,
            cookies=cookies,
            timeout=timeout,
            limits=limits,
            follow_redirects=True,
            http2=True,
            **kw
        )
        self.event_hooks = event_hooks or AsyncPullerEventHook()

        # Set up event hook
        def subscribe(event: StrChain, action: ActionVar) -> ActionVar:
            ev = str(event)
            self._event_hooks[ev].append(action)
            if ev in ["request", "response"]:
                self._client.event_hooks = self._event_hooks  # type: ignore[assignment]
            return action

        self._on = PullerEventHint(
            strchain=StrChain(joint='.', callback=subscribe))

    @property
    def client(self):
        return self._client

    @property
    def loop(self):
        return self._loop

    @property
    def headers(self):
        """Get headers."""
        return self._client.headers

    @headers.setter
    def headers(self, value: HeaderTypes):
        """Set headers for all requests."""
        self._client.headers = value  # type: ignore[assignment]

    @property
    def cookies(self):
        return self._client.cookies

    @cookies.setter
    def cookies(self, value: CookieTypes):
        """Set cookies for all requests."""
        self._client.cookies = value  # type: ignore[assignment]

    @property
    def params(self):
        return self._client.params

    @params.setter
    def params(self, value: QueryParamTypes):
        """Set params for all requests."""
        self._client.params = value  # type: ignore[assignment]

    @property
    def event_hooks(self):
        return self._event_hooks

    @event_hooks.setter
    def event_hooks(self, hooks: Mapping[str, Sequence[Callable]]):
        """Set event hooks for all requests."""
        self._event_hooks = AsyncPullerEventHook(hooks)  # deepcopy
        self._client.event_hooks = self._event_hooks  # type: ignore[assignment]

    @property
    def on(self):
        """Shortcut for `Puller.event_hooks.on`"""
        return self._on

    @property
    def proxies(self):
        return self._proxies

    async def pull(
        self,
        url: str,
        path: str | None,
        *,
        extra_headers: HeaderTypes | None = None,
        extra_params: QueryParamTypes | None = None,
        extra_cookies: CookieTypes | None = None,
        timeout: Timeout | float | None = 0,
        retry: int | None = None,
        overwrite: bool | None = None,
        **kw
    ) -> Future:
        """
        ### Asyncronously pull a file from a url.
        File will be decompressed and saved in binary mode.

        Returns a `Future` instance refer to the path of the downloaded file.
        * `url`: url to pull from
        * `path`: path to save to, set to `None` or `""` will not save file

        Optional Parameters:
        * `extra_headers`: extra headers to add to request
        * `extra_params`: extra params to add to request
        * `extra_cookies`: extra cookies to add to request
        * `timeout`: timeout for this request, set to None will be no limit, 0 for default
        * `retry`: retry times for this request, set to None will use default
        * `overwrite`: overwrite file if exists, set to None will use default
        * `**kw`: extra keyword arguments for httpx.stream
        """
        timeout = self.client.timeout if timeout == 0 else timeout
        self._loop = self.loop or asyncio.get_running_loop()
        if self._master is None:
            self._master = AsyncMaster(self)
            self._loop.create_task(self._master.run())
        future: Future = Future()  # A placeholder for the worker.run() task
        worker = AsyncWorker(
            self,
            url=url,
            path=path,
            extra_headers=extra_headers,
            extra_params=extra_params,
            extra_cookies=extra_cookies,
            future=future,
            timeout=timeout,
            retry=retry or self.max_retry,
            overwrite=overwrite or self.overwrite,
            **kw
        )
        await self._event_hooks.aemit("worker.spawn", worker)
        await self._buffer.put(worker)
        return future

    async def join(self) -> None:
        """### Wait for all workers to finish."""
        await self._event_hooks.aemit("puller.join", self)
        await self._buffer.join()  # Make sure no pending tasks
        await self._workers.join()  # Make sure all tasks are done

    async def aclose(self):
        await self.join()  # Make sure all tasks are done
        await self._buffer.put(None)  # Kill master
        await self._event_hooks.aemit("puller.destroy", self)
        await self._client.aclose()

    async def __aenter__(self) -> AsyncPuller:
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb) -> bool:
        await self.aclose()
        return False

    def __repr__(self):
        return f"{self.__class__.__name__}:\n" +\
            f"  max_retry: {self.max_retry}\n" +\
            f"  overwrite: {self.overwrite}\n" +\
            f"  headers: {self.headers}\n" +\
            f"  params: {self.params}\n" +\
            f"  cookies: {self.cookies}\n" +\
            f"  proxies: {self.proxies}\n" +\
            f"  event_hooks: {self.event_hooks}\n"

################### Useful Modifiers #####################

class Modifier:
    progress = None

    @classmethod
    def show_progress(
        cls,
        puller: BasePuller,
        desc_len: int = os.get_terminal_size().columns//3,
        delay_on_finish: float | None = 0.5,
        progress: rich.progress.Progress | None = None,
    ) -> None:
        """
        ### Show progress of all tasks.
        * `puller`: Puller instance
        * `desc_len`: max length of url description
        * `delay_on_finish`: time to remove finished progress bar, set to `None` to keep it
        * `progress`: rich.progress.Progress instance, set to `None` to use default one
        """
        from rich.progress import Progress
        if cls.progress is None:
            cls.progress = Progress(refresh_per_second=30)
        _progress = progress or cls.progress

        if isinstance(puller, AsyncPuller):
            @puller.on.worker.start
            async def create_task(event: str, worker: AsyncWorker):
                nonlocal puller
                progress = _progress  # In case outer progress is changed
                desc = worker.url.split("://")[-1]
                if len(desc) > desc_len:
                    desc = desc[:(desc_len-3)//2] + "..." + \
                        desc[-(desc_len-3)//2:]
                progress.start()  # start progress bar
                task = progress.add_task(desc)
                total = 0
                failed = False

                @worker.event_hooks.on.worker.response_get
                async def get_total(event: str, worker: AsyncWorker, r: Response):
                    nonlocal task
                    nonlocal total
                    total = int(r.headers.get("Content-Length", 0))
                    # type: ignore[arg-type]
                    progress.update(task, total=total or 100)

                @worker.event_hooks.on.worker.bytes_get
                async def update_task(ev: str, w: AsyncWorker, r: Response, b: bytes):
                    nonlocal task
                    nonlocal total
                    if total:
                        # type: ignore[arg-type]
                        progress.update(task, completed=r.num_bytes_downloaded)

                @worker.event_hooks.on.worker.fail
                async def fail_task(ev: str, w: AsyncWorker, e: Exception):
                    nonlocal failed
                    nonlocal task
                    failed = True
                    if isinstance(e, FileExistsError):
                        progress.remove_task(task)

                @worker.event_hooks.on.worker.destroy
                async def remove_task(event: str, worker: AsyncWorker):
                    nonlocal task
                    nonlocal total
                    if not failed:
                        if not total:  # Show cpmpleted for unknown size
                            progress.update(task, total=1, completed=1)
                        progress.refresh()
                        if delay_on_finish is not None:
                            await asyncio.sleep(delay_on_finish)
                            progress.update(task, visible=False, refresh=True)
                            progress.remove_task(task)
                    if progress.finished:
                        progress.stop()

    @staticmethod
    def ignore_failure(puller: BasePuller) -> None:
        """
        ### Ignore all exceptions.
        * `puller`: Puller instance
        """
        if isinstance(puller, AsyncPuller):
            @puller.on.worker.fail
            async def ignore_failure(event: str, worker: AsyncWorker, e: Exception):
                return True

    @staticmethod
    def ignore_file_exists(puller: BasePuller) -> None:
        """
        ### Ignore `FileExistsError`.
        * `puller`: Puller instance
        """
        if isinstance(puller, AsyncPuller):
            @puller.on.worker.fail
            async def ignore_file_exists(event: str, worker: AsyncWorker, e: Exception):
                if isinstance(e, FileExistsError):
                    return True

    @staticmethod
    def raise_for_status(puller: BasePuller) -> None:
        """
        ### Raise exception on non-200 status.
        * `puller`: Puller instance
        """
        if isinstance(puller, AsyncPuller):
            @puller.on.worker.response_get
            async def raise_for_status(event: str, worker: AsyncWorker, r: Response):
                r.raise_for_status()

    @staticmethod
    def add_logging(puller: BasePuller, logger: LoggerLike) -> None:
        """
        ### Add logging for events.
        * `puller`: Puller instance
        * `logger`: logger-like instance
        """
        if isinstance(puller, AsyncPuller):
            @puller.on.puller.spawn
            async def p_log_spawn(event: str, puller: AsyncPuller):
                logger.debug(f"Puller spawned. {puller}")

            @puller.on.puller.join
            async def p_log_join(event: str, puller: AsyncPuller):
                logger.debug(f"Puller joined. {puller}")

            @puller.on.puller.destroy
            async def p_log_destroy(event: str, puller: AsyncPuller):
                logger.debug(f"Puller destroyed. {puller}")

            @puller.on.worker.spawn
            async def log_spawn(event: str, worker: AsyncWorker):
                logger.debug(f"Spawned worker {worker}")

            @puller.on.worker.start
            async def log_start(event: str, worker: AsyncWorker):
                logger.debug(f"Start: {worker}")

            @puller.on.worker.response_get
            async def log_response(event: str, worker: AsyncWorker, r: Response):
                logger.debug(f"Response: {r.status_code}  {r.url}")

            @puller.on.worker.bytes_get
            async def log_bytes(event: str, worker: AsyncWorker, r: Response, b: bytes):
                logger.debug(f"Bytes size: {len(b)} from {r.url}")

            @puller.on.worker.retry
            async def log_retry(event: str, worker: AsyncWorker, e: Exception, retry: int):
                logger.debug(f"Retry: {worker} at {to_ordinal(retry)} time")

            @puller.on.worker.destroy
            async def log_destroy(event: str, worker: AsyncWorker):
                logger.debug(f"Destroy: {worker}")

            @puller.on.worker.fail
            async def log_fail(event: str, worker: AsyncWorker, e: Exception):
                logger.error(f"Fail: {worker} due to {e}")
                logger.exception(e)

    @staticmethod
    def on_every_event(puller: BasePuller, func: Callable) -> None:
        """
        ### Call function on every event.
        * `puller`: Puller instance
        * `func`: function to call
        """
        if isinstance(puller, AsyncPuller):
            @puller.on.puller.spawn
            @puller.on.puller.join
            @puller.on.puller.destroy
            @puller.on.worker.spawn
            @puller.on.worker.start
            @puller.on.worker.response_get
            @puller.on.worker.bytes_get
            @puller.on.worker.retry
            @puller.on.worker.destroy
            @puller.on.worker.fail
            async def on_every_event(*args, **kw):
                await ensure_async(func)(*args, **kw)

    @staticmethod
    def clear_hooks(puller: BasePuller) -> None:
        """
        ### Clear all hooks.
        * `puller`: Puller instance
        """
        puller.event_hooks.clear()

#### Start Hinting: Add hints for event names ####

class on_puller_hint(Hint):
    @property
    def spawn(self):
        """Callback Type: (event_name: str, AsyncPuller) -> None"""
        return self._chain.spawn

    @property
    def destroy(self):
        """Callback Type: (event_name: str, AsyncPuller) -> None"""
        return self._chain.destroy

    @property
    def join(self):
        """Callback Type: (event_name: str, AsyncPuller) -> None"""
        return self._chain.join

class on_worker_hint(Hint):
    @property
    def spawn(self):
        """Callback Type: (event_name: str, AsyncWorker) -> None"""
    @property
    def destroy(self):
        """Callback Type: (event_name: str, AsyncWorker) -> None"""
    @property
    def start(self):
        """Callback Type: (event_name: str, AsyncWorker) -> None"""
    @property
    def response_get(self):
        """Callback Type: (event_name: str, AsyncWorker, Response) -> None"""
    @property
    def bytes_get(self):
        """Callback Type: (event_name: str, AsyncWorker, Response, bytes) -> None"""
    @property
    def retry(self):
        """Callback Type: (event_name: str, AsyncWorker, Exception, retry: int) -> None"""
    @property
    def success(self):
        """Callback Type: (event_name: str, AsyncWorker) -> None"""
    @property
    def fail(self):
        """Callback Type: (event_name: str, AsyncWorker, Exception) -> bool:
        whether to ignore the exception"""

class PullerEventHint(Hint):
    """Puller Event Hint."""
    @property
    def puller(self) -> on_puller_hint:
        return self._chain.puller  # type: ignore

    @property
    def worker(self) -> on_worker_hint:
        return self._chain.worker  # type: ignore

    @property
    def request(self):
        """Callback Type: (Request) -> None"""
        return self._chain.request

    @property
    def response(self):
        """Callback Type: (Response) -> None"""
        return self._chain.response

### End Hinting ###


import cv2 # pip install opencv-python-headless or conda install opencv
from matplotlib import pyplot as plt

# Due to jupyters architecture we can not use cv2.imshow() so we create a helper method to utilize pyplot.
def show_image(image, title):
    plt.imshow(image)
    plt.title(title)
    plt.show()

# Create the haar cascade
cascPath = "/kaggle/input/bildebehandling/haarcascade_frontalface_default.xml" # Just an xml file that contains data about a face.
faceCascade = cv2.CascadeClassifier(cascPath) # Load the cascade into memory.

face_image_kode24 = cv2.imread("/kaggle/input/fjas-og-tull/kode24_meta_og_google.jpg") # cv2 read in BGR format.
gray_face_image_kode24 = cv2.cvtColor(face_image_kode24, cv2.COLOR_BGR2GRAY)

kode24_faces = faceCascade.detectMultiScale(
    gray_face_image_kode24,
    scaleFactor=1.1,
    minNeighbors=1,  # Endra den her, då endra det seg kor mange fjes den fant
    minSize=(30, 30),
    flags = cv2.CASCADE_SCALE_IMAGE
)

# Draw a rectangle around the faces
print(kode24_faces)
for (x, y, w, h) in kode24_faces:
    cv2.rectangle(face_image_kode24, (x, y), (x+w, y+h), (0, 255, 0), 1)

show_image(cv2.cvtColor(face_image_kode24, cv2.COLOR_BGR2RGB), "Number of faces found = %s" % len(kode24_faces))
show_image(cv2.cvtColor(gray_face_image_kode24, cv2.COLOR_BGR2RGB), "Greyscale...")