# builtins
import datetime
import os
import tkinter as tk
from tkinter import ttk

# this is neccessary even though in the code I use tk.filedialog, if not imported specifically error is thrown
from tkinter import filedialog
from tkinter import messagebox
import threading
import time
import numpy
import controller
from os import startfile
from functools import partial

# non builtins
try:
    from PIL import Image, ImageTk
except ImportError:
    tk.Tk().withdraw()
    messagebox.showerror(
        "error",
        "Can't import PIL module. Please install PIL package. Program will exit",
    )

try:
    import cv2
except ImportError:
    tk.Tk().withdraw()
    messagebox.showerror(
        "error",
        "Can't import cv2 module. Please install cv2 package. Program will exit",
    )

from ttkthemes import ThemedTk

import ttk_styles
import colours

class GUI:
    def __init__(self) -> None:
        self.controller = controller.Controller()

        self.stop_thread = False
        self.pause = False

        # init window
        self.root = ThemedTk(theme="equilux")
        self.root.title("Motion Detector")
        self.root.minsize(width=650, height=400)
        self.root.geometry("800x500")
        self.root.state("zoomed")
        # self.root.attributes("-fullscreen", True)
        self.root.grid_columnconfigure(0, weight=1)
        self.root.grid_columnconfigure(1, weight=1)
        self.root.grid_columnconfigure(2, weight=1)
        self.root.grid_rowconfigure(0, weight=1)
        self.root.grid_rowconfigure(1, weight=1)
        self.height, self.width = 0, 0

        # style
        self.style = ttk_styles.Styles(self.root)
        self.style.use_dark_theme()

        # create widgets
        self.create_menubar()
        self.create_image_label()

        self.create_modifiers_frame()
        self.create_motion_frame()
        self.create_camera_modifiers_frame()

        self.update_frame_label_frame_thread_controller()
        self.first_frame_loaded = False
        self.root.bind("<Configure>", self.resize)

        self.root.mainloop()

    def resize(self, event):
        # return
        if event.widget == self.image_label and (
            event.height != self.height or event.width != self.width
        ):
            self.height, self.width = event.height, event.width

    def quit(self):
        # stops threads otherwise they throw errors trying to execute
        # processes when TCL objects are destroyed
        self.controller.stop_thread = True
        self.stop_thread = True

        # if quit first, then root freezes for a couple of seconds before
        # closing
        self.root.destroy()
        quit()

    def create_menubar(self):
        self.menubar = tk.Menu(self.root, background="blue", fg="white")
        self.populate_menubar()
        self.root.config(menu=self.menubar)

    def populate_menubar(self):
        ###########
        # File menu
        ###########
        file_menu = tk.Menu(self.menubar, tearoff=0, background="#434B4C", fg="white")

        file_menu.add_command(
            label="Exit", command=lambda: self.quit(), accelerator="Alt+F4"
        )

        #################
        # Source Settings
        #################
        self.source_menu = tk.Menu(
            self.menubar, tearoff=0, background="#434B4C", fg="white"
        )

        self.source_menu.add_command(
            label="Open Video File",
            command=lambda: self.change_frame_source(change_to="video"),
            accelerator="Ctrl+O",
        )
        self.source_menu.add_command(
            label="Switch to Camera Mode",
            command=lambda: self.change_frame_source(change_to="camera"),
            accelerator="Ctrl+I",
            state="disabled",  # program will start in camera mode
        )

        ###############
        # Frame settings
        ###############
        # Rect settings
        frame_settings_menu = tk.Menu(
            self.menubar, tearoff=0, background="#434B4C", fg="white"
        )
        rect_size = tk.Menu(
            frame_settings_menu, tearoff=0, background="#434B4C", fg="white"
        )
        for x in [100, 500, 1000, 2500, 5000, "custom"]:
            rect_size.add_command(
                label=x,
                command=partial(self.rect_area_changed, x),
            )
        # menu is created before settings, so self variable has to be created
        # here
        self.rect_draw_checkbox = tk.BooleanVar(value=True)
        frame_settings_menu.add_checkbutton(
            label="Enable Rect Drawing",
            command=lambda: self.rect_draw_changed(),
            variable=self.rect_draw_checkbox,
            onvalue=True,
            offvalue=False,
            accelerator="Ctrl+Shift+D",
        )
        frame_settings_menu.add_cascade(label="Minimum Rect Area", menu=rect_size)

        frame_settings_menu.add_separator()

        # Frame Settings
        self.flip = tk.BooleanVar(value=False)
        frame_settings_menu.add_checkbutton(
            label="Flip Frame",
            command=lambda: self.flip.set(not self.flip.get()),
            variable=self.flip,
            onvalue=True,
            offvalue=False,
            accelerator="Ctrl+Shift+F",
        )

        ##############
        # Add Cascades
        ##############
        self.menubar.add_cascade(label="File", menu=file_menu)
        self.menubar.add_cascade(label="Source Settings", menu=self.source_menu)
        self.menubar.add_cascade(label="Frame Settings", menu=frame_settings_menu)

        ###########
        # Add Binds
        ###########
        # File Menu

        # Source Menu
        self.root.bind(
            "<Control-o>", lambda _: self.change_frame_source(change_to="video")
        )
        self.root.bind(
            "<Control-i>", lambda _: self.change_frame_source(change_to="camera")
        )

        # Frame Settings Menu
        self.root.bind(
            "<Control-Shift-D>",
            lambda _: self.rect_draw_changed(not self.rect_draw_checkbox.get()),
        )
        self.root.bind(
            "<Control-Shift-F>", lambda _: self.flip.set(not self.flip.get())
        )
        # self.root.bind("<KeyPress>", lambda e: print(e))

    def create_image_label(self):
        self.image_label = ttk.Label(self.root, image=None)
        self.image_label.grid(row=0, column=1, sticky="NSEW")

    def create_modifiers_frame(self):
        self.modifiers_frame = ttk.Labelframe(self.root, text="Frame Settings")
        self.modifiers_frame.grid(row=0, column=0, sticky="NSEW", rowspan=2)

        self.create_modifiers()

    def create_motion_frame(self):
        self.motion_frame = ttk.Labelframe(self.root, text="Recent Motions")
        self.motion_frame.grid(row=0, column=2, sticky="NSEW", rowspan=2)

        self.recent_motions = []
        self.motions_labels = []

        self.open_output_file_button()

        self.update_motions_display_thread_controller()

    def create_camera_modifiers_frame(self):
        self.camera_modifiers_frame = ttk.Labelframe(self.root, text="Source Settings")
        self.camera_modifiers_frame.grid(row=1, column=1, sticky="NSEW", ipady=10)

        self.create_camera_modifiers()

    def create_camera_modifiers(self):
        self.previous_camera_button = ttk.Button(
            self.camera_modifiers_frame,
            text="<-- Previous Camera",
            command=partial(self.changed_camera, "prev"),
        )
        self.previous_camera_button.grid(row=0, column=0, padx=(10, 0))

        self.switch_frame_source_button = ttk.Button(
            self.camera_modifiers_frame,
            text="Pick File",
            command=lambda: self.change_frame_source(
                change_to="video"
                if self.switch_frame_source_button["text"] == "Pick File"
                else "camera"
            ),
        )
        self.switch_frame_source_button.grid(row=0, column=1)

        self.disable_source_button = ttk.Button(
            self.camera_modifiers_frame,
            text="Disable Source",
            command=lambda: self.change_disabled_source(
                "disabled"
                if self.disable_source_button["text"] == "Disable Source"
                else "enabled"
            ),
        )
        self.disable_source_button.grid(row=0, column=2)

        self.pause_button = ttk.Button(
            self.camera_modifiers_frame,
            text="Pause",
            command=lambda: self.paused_changed(),
        )
        self.pause_button.grid(row=0, column=3)

        self.next_camera_button = ttk.Button(
            self.camera_modifiers_frame,
            text="Next Camera -->",
            command=partial(self.changed_camera, "next"),
        )
        self.next_camera_button.grid(row=0, column=4, padx=(0, 10))

        for i in range(0, 4):
            self.camera_modifiers_frame.grid_columnconfigure(i, weight=1)

    def paused_changed(self):
        self.pause_button.config(state="disabled")
        if self.pause_button["text"] == "Pause":
            self.pause_button.config(text="Play")
            self.controller.pause = True
            self.pause = True

        else:
            self.pause_button.config(text="Pause")
            self.controller.pause = False
            self.pause = False
        self.pause_button.config(state="normal")

    def change_disabled_source(self, change_to):
        if change_to == "disabled":
            self.change_frame_source(
                change_to="blank",
            )
            self.disable_source_button.config(text="Enable Source")
        else:
            self.disable_source_button.config(text="Disable Source")
            self.change_frame_source(change_to="camera")

    def load_video_file(self):
        filetypes = (
            ("video files", "*.mp4"),
            ("movie files", "*.mov"),
        )
        return tk.filedialog.askopenfilename(
            initialdir=os.path.join(os.path.join(os.environ["USERPROFILE"]), "Desktop"),
            title="Select a File",
            filetypes=(filetypes),
        )

    def change_frame_source(self, change_to):
        self.switch_frame_source_button.config(state="disabled")

        if change_to == "blank":
            self.controller.source = "blank"
            self.controller.changed_source("blank")
            self.source_menu.entryconfig("Switch to Camera Mode", state="normal")
            self.switch_frame_source_button.config(state="normal")

        if change_to == "video":
            source = self.load_video_file()
            if source != "":
                self.controller.vid_source = source
                self.controller.source = "video"
                self.controller.changed_source("video")
                self.switch_frame_source_button.config(text="Switch to Camera Mode")
                self.source_menu.entryconfig("Switch to Camera Mode", state="normal")

        elif change_to == "camera":
            # This if statement is to handle when user uses keyboard shortcut
            # to call "Switch to Camera Mode" when already in camera mode
            if (
                self.source_menu.entrycget(
                    self.source_menu.index("Switch to Camera Mode"), "state"
                )
                == "disabled"
            ):
                self.switch_frame_source_button.config(state="normal")
                return

            self.controller.source = "camera"
            self.controller.changed_source("camera")
            self.disable_source_button.config(state="normal")
            self.switch_frame_source_button.config(text="Pick File")
            self.source_menu.entryconfig("Switch to Camera Mode", state="disabled")

        self.switch_frame_source_button.config(state="normal")

    def changed_camera(self, change):
        if change == "prev":
            btn = self.previous_camera_button
            btn.config(state="disabled")
            change = 1
        elif change == "next":
            btn = self.next_camera_button
            btn.config(state="disabled")
            change = -1

        self.controller.change_camera(change)
        btn.after(100, lambda: btn.config(state="normal"))

    def rect_draw_changed(self, val=None):
        if val is not None:
            self.rect_draw_checkbox.set(val)
            self.rect_draw_changed()
        if self.rect_draw_checkbox.get() == True:
            self.controller.show_rect = True
        else:
            self.controller.show_rect = False

    def custom_val_rect_area_changed(self, val):
        if val == "custom":
            ttk_styles.CustomInputWindow(
                command=self.custom_val_rect_area_changed,
                title="Custom Val",
                input_message="Please enter a value for rect area (10-5000 inclusive)",
                type="integer",
                number_low_boundary=10,
                number_high_boundary=5000,
            )
        else:
            self.rect_area_scale.set(val)

    def rect_area_changed(self, val=None):
        if val is None:
            rect_area = self.rect_area.get()
        elif val == "custom":
            ttk_styles.CustomInputWindow(
                command=self.rect_area_changed,
                title="Custom Val",
                input_message="Please enter a value for rect area (10-5000 inclusive)",
                type="integer",
                number_low_boundary=10,
                number_high_boundary=5000,
            )
            return
        else:
            rect_area = val
            self.rect_area_scale.set(val)
        self.controller.rect_area = rect_area

    def sensitivity_changed(self):
        threshold = 100 - self.motion_sensitivity.get()
        self.controller.threshold = threshold

    def frame_type_changed(self):
        self.controller.frame_type = self.frame_type.get()

    def create_modifiers(self):
        # Whether or not to draw rects around moving objects
        rect_draw_checkbox = ttk.Checkbutton(
            self.modifiers_frame,
            text="Enable Rect Drawing",
            command=lambda: self.rect_draw_changed(),
            variable=self.rect_draw_checkbox,
            onvalue=True,
            offvalue=False,
        )
        rect_draw_checkbox.state(["selected"])
        rect_draw_checkbox.grid(row=2, column=0)

        # How big the minimum area of a moving object rect has to be to be drawn
        ttk.Separator(self.modifiers_frame, orient="horizontal").grid(
            row=3, column=0, sticky="EW", pady=3
        )
        self.rect_area = tk.IntVar()
        ttk.Label(self.modifiers_frame, text="Minimum Rect Area").grid(row=4, column=0)

        self.rect_area_scale = ttk_styles.CustomHorizontalScale(
            root=self.modifiers_frame,
            variable=self.rect_area,
            from_=10,
            to=5000,
            command=lambda _: self.rect_area_changed(),
        )

        self.rect_area_scale.set(500)
        self.rect_area_scale.grid(row=5, column=0)

        # Threshold for defining image as "different" - higher threshold is
        # lower motion sensitivity
        ttk.Separator(self.modifiers_frame, orient="horizontal").grid(
            row=6, column=0, sticky="EW", pady=3
        )
        self.motion_sensitivity = tk.IntVar()
        ttk.Label(self.modifiers_frame, text="Motion Sensitivity").grid(row=7, column=0)
        sensitivity_scale = ttk_styles.CustomHorizontalScale(
            self.modifiers_frame,
            from_=1,
            to=99,
            variable=self.motion_sensitivity,
            command=lambda _: self.sensitivity_changed(),
        )
        sensitivity_scale.set(80)
        sensitivity_scale.grid(row=8, column=0)

        # Flip image
        ttk.Separator(self.modifiers_frame, orient="horizontal").grid(
            row=9, column=0, sticky="EW", pady=3
        )
        self.flip_image = ttk.Checkbutton(
            self.modifiers_frame,
            text="Flip Frame",
            variable=self.flip,
            onvalue=True,
            offvalue=False,
        )
        self.flip_image.grid(row=10, column=0)

        # Frame types
        ttk.Separator(self.modifiers_frame, orient="horizontal").grid(
            row=11, column=0, sticky="EW", pady=3
        )
        self.frame_type = tk.StringVar(value="normal")
        frame_type_options = ["normal", "greyscale"]

        self.frame_type_combobox = ttk.Combobox(
            self.modifiers_frame,
            textvariable=self.frame_type,
            values=frame_type_options,
        )
        self.frame_type_combobox["state"] = "readonly"
        self.frame_type_combobox.bind(
            "<<ComboboxSelected>>", lambda *_: self.frame_type_changed()
        )
        self.frame_type_combobox.grid(row=12, column=0)

    def destroy_motion_labels(self):
        for i, label in enumerate(self.motions_labels):
            label.destroy()
            self.motions_labels.pop(i)

    def create_motion_labels(self):
        # split into 2 for loops so that labels are put on screen at closer
        # intervals for a smoother experience
        for timestamp in self.recent_motions:
            label = ttk.Label(self.motion_frame, text=timestamp)
            self.motions_labels.append(label)
        for label in self.motions_labels:
            label.pack()

    def update_motions_display(self):
        while True:
            if self.stop_thread:
                break
            if self.pause:
                # if not here in every thread, causes app to grind to halt
                # during pause
                time.sleep(0.25)
                continue

            self.set_recent_motions()
            self.destroy_motion_labels()
            self.create_motion_labels()

            # if grabbing motion from controller class is done every 0.25s,
            # then 12 will be grabbed in 3s so none are missed, but update speed
            # is less than having to do a widget update every 0.25s which was
            # glitchy
            time.sleep(3)

    def set_recent_motions(self):
        self.recent_motions = self.controller.get_motion()

    def open_output_file(self):
        startfile(self.controller.output_file_name)

    def open_output_file_button(self):
        open_output_file_button = ttk.Button(
            self.motion_frame,
            text="Open Output File",
            command=lambda: self.open_output_file(),
        )
        open_output_file_button.pack(side="bottom", pady=(0, 10))

    def update_motions_display_thread_controller(self):
        self.update_motions_display_thread = threading.Thread(
            target=self.update_motions_display
        )
        self.update_motions_display_thread.daemon = True
        self.update_motions_display_thread.start()

    def update_frame_label_frame(self):
        while True:
            start_process_time = time.time()
            if self.stop_thread:
                break
            if self.pause:
                time.sleep(
                    0.25
                )  # if not here in every thread, causes app to grind to halt during pause
                continue
            frame = self.controller.get_frame()
            if frame is None:
                continue
            else:
                height, width, *_ = frame.shape
                if not self.first_frame_loaded:
                    self.height = height
                    self.width = width
                    self.first_frame_loaded = True

                if height != self.height or width != self.width:
                    # take off 3 from self.height,width so that frame borders
                    # are shown clearly with no overlap
                    frame = cv2.resize(frame, (self.width - 3, self.height - 3))
                if self.flip.get():
                    # flips frame vertically
                    frame = numpy.fliplr(frame)
                frame = Image.fromarray(frame)
                frame = ImageTk.PhotoImage(image=frame)

                # Put it in the display window
                self.image_label.configure(image=frame)
                # https://web.archive.org/web/20201111190625id_/http://effbot.org/pyfaq/why-do-my-tkinter-images-not-appear.htm
                # So if there isn't a reference to the .image, then
                # tkinter will discard it and a white image will be shown instead
                self.image_label.image = frame

                end_process_time = time.time()
                difference = end_process_time - start_process_time
                # fps = 60
                if difference < 1 / 60:
                    # If the time spent processing the frame (difference) is less than the time that each frame should
                    # be displayed for (to fit with fps) then add a wait until the frame duration is finished
                    time.sleep(((1 / 60) - difference))

    def update_frame_label_frame_thread_controller(self):
        self.update_frame_label_frame_thread = threading.Thread(
            target=self.update_frame_label_frame
        )
        self.update_frame_label_frame_thread.daemon = True
        self.update_frame_label_frame_thread.start()

if __name__ == "__main__":
    GUI()


from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms

from torch.utils.data.sampler import Sampler, SubsetRandomSampler
import numpy as np
from sklearn.model_selection import StratifiedKFold

from DataSampler import index_gen
from utils.loss import UncertaintyLoss

class Net(nn.Module):
    def __init__(self, args):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 20, 5, 1)
        self.conv2 = nn.Conv2d(20, 50, 5, 1)
        self.fc1 = nn.Linear(4 * 4 * 50, 500)
        self.fc2 = nn.Linear(500, 10)
        self.args = args
        if args.uncertainty:
            self.fc2_var = nn.Linear(500,10)
            self.softp = nn.Softplus()
    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.max_pool2d(x, 2, 2)
        x = F.relu(self.conv2(x))
        x = F.max_pool2d(x, 2, 2)
        x = x.view(-1, 4 * 4 * 50)
        x = F.relu(self.fc1(x))
        if not self.args.uncertainty:
            x = self.fc2(x)
            return F.log_softmax(x, dim=1)
        else:
            x_mu = self.fc2(x)
            x_logvar = self.fc2_var(x)
            x_var = torch.exp(x_logvar)
            return x_mu, x_var

def test(args, model, device, test_loader, data_idx, epoch):
    model.eval()
    test_loss = 0
    correct = 0

    print('------start final prediction and save the result-----')
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)

            if args.uncertainty:
                output, output_var = model(data)
                test_loss += UncertaintyLoss(output, output_var, target, reduction='sum')
            else:
                output = model(data)
                test_loss += F.nll_loss(output, target, reduction='sum')
            # output = model(data)
            # test_loss += F.nll_loss(output, target, reduction='sum').item()  # sum up batch loss
            pred = output.argmax(dim=1, keepdim=True)  # get the index of the max log-probability
            correct += pred.eq(target.view_as(pred)).sum().item()
            if args.uncertainty:
                try:
                    predictions_mu = np.concatenate((predictions_mu,output.data.cpu().numpy()), 0)
                    predictions_var = np.concatenate((predictions_var,output_var.data.cpu().numpy()), 0)
                    labels = np.concatenate((labels,target.data.cpu().numpy()), 0)
                except:
                    predictions_mu = output.data.cpu().numpy()
                    predictions_var = output_var.data.cpu().numpy()
                    labels = target.data.cpu().numpy()

            else:
                try:
                    predictions = np.concatenate((predictions,output.data.cpu().numpy()), 0)
                    labels = np.concatenate((labels,target.data.cpu().numpy()), 0)
                except:
                    predictions = output.data.cpu().numpy()
                    labels = target.data.cpu().numpy()

    test_loss /= len(test_loader.dataset)

    print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(data_idx),
        100. * correct / len(data_idx)))
    if args.uncertainty:
        return predictions_mu, predictions_var, labels
    else:
        return predictions, labels

def main():
    # Training settings
    parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
    parser.add_argument('--batch-size', type=int, default=64, metavar='N',
                        help='input batch size for training (default: 64)')
    parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
                        help='input batch size for testing (default: 1000)')
    parser.add_argument('--epochs', type=int, default=100, metavar='N',
                        help='number of epochs to train (default: 10)')
    parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
                        help='learning rate (default: 0.01)')
    parser.add_argument('--momentum', type=float, default=0.5, metavar='M',
                        help='SGD momentum (default: 0.5)')
    parser.add_argument('--no-cuda', action='store_true', default=False,
                        help='disables CUDA training')
    parser.add_argument('--seed', type=int, default=1, metavar='S',
                        help='random seed (default: 1)')
    parser.add_argument('--log-interval', type=int, default=10, metavar='N',
                        help='how many batches to wait before logging training status')
    parser.add_argument('--k-fold', type=int, default=6,
                        help='How many folds')
    parser.add_argument('--base-prediction', type=bool, default=True,
                        help='save base learner prediction ')
    parser.add_argument('--uncertainty', type=bool, default=True,
                        help='predict mu and var ? ')

    parser.add_argument('--save-model', action='store_true', default=True,
                        help='For Saving the current Model')
    args = parser.parse_args()
    use_cuda = not args.no_cuda and torch.cuda.is_available()

    torch.manual_seed(args.seed)

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

    kwargs = {'num_workers': 2, 'pin_memory': True} if use_cuda else {}

    train_transform = transforms.Compose([
                           transforms.ToTensor(),
                           transforms.Normalize((0.1307,), (0.3081,))
                            ])

    trainset = datasets.MNIST('../data', train=True, download=True, transform=train_transform)

    train_idxs, val_idxs = index_gen(trainset, seed = 100, k=args.k_fold)

    ## restart in kfold - 3~4
    for fold in range(args.k_fold):
        print('Start {}-th fold Inference'.format(fold))

        val_loader = torch.utils.data.DataLoader(trainset,
            batch_size=args.test_batch_size, sampler=SubsetRandomSampler(val_idxs[fold]) , shuffle=False, **kwargs)

        model = Net(args).to(device)
        model.load_state_dict(torch.load('./run/uncertainty_2/[{}-th fold]mnist_cnn.pt'.format(fold)))

        if args.uncertainty:
            predictions_mu, predictions_var, labels = test(args, model, device, val_loader, val_idxs[fold], 1)
            try:
                predictions_mu_folds = np.concatenate((predictions_mu_folds, predictions_mu), 0)
                predictions_var_folds = np.concatenate((predictions_var_folds, predictions_var), 0)
                labels_folds = np.concatenate((labels_folds, labels), 0)
            except:
                predictions_mu_folds = predictions_mu
                predictions_var_folds = predictions_var
                labels_folds = labels
        else:
            predictions, labels = test(args, model, device, val_loader, val_idxs[fold], 1)
            try:
                predictions_folds = np.concatenate((predictions_folds, predictions), 0)
                labels_folds = np.concatenate((labels_folds, labels), 0)
            except:
                predictions_folds = predictions
                labels_folds = labels

        if args.base_prediction and fold == args.k_fold-1:
            if args.uncertainty:
                np.save('./run/uncertainty_2/x_mu.npy',predictions_mu_folds)
                np.save('./run/uncertainty_2/x_var.npy',predictions_var_folds)
                np.save('./run/uncertainty_2/y.npy',labels_folds)

            else:
                np.save('./run/baseline/x.npy',predictions_folds)
                np.save('./run/baseline/y.npy',labels_folds)

if __name__ == '__main__':
    main()


# https://www.acmicpc.net/problem/1926
import sys
from collections import deque
input = sys.stdin.readline
d = [(-1, 0), (1, 0), (0, -1), (0, 1)]

n, m = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(n)]
visited = [[0] * m for _ in range(n)]
cnt = 0
area = 0
for i in range(n):
    for j in range(m):
        if board[i][j] and not visited[i][j]:
            cnt += 1
            q = deque()
            q.append((i, j))
            visited[i][j] = 1
            tmp = 0
            while q:
                x, y = q.popleft()
                tmp += 1
                for dx, dy in d:
                    nx = dx + x
                    ny = dy + y
                    if -1 < nx < n and -1 < ny < m:
                        if board[nx][ny] and not visited[nx][ny]:
                            q.append((nx, ny))
                            visited[nx][ny] = 1
            area = max(tmp, area)
print(cnt)
print(area)


from django.http import HttpResponse
from django.shortcuts import render , redirect , HttpResponseRedirect
from django.views.generic import View
from django.core.paginator import Paginator
from daawat.util.generate_pdf import render_to_pdf
from django.template.loader import get_template
from daawat.service.astra_service import *
from .menu import hotel_exists

class GeneratePDF(View):
    def post(self, request, *args, **kwargs):
        ordersList = []
        foodIds = []
        data = {}
        invoiceList = []

        hotel_id = request.session.get('hotelid')
        grand_total = request.POST.get('grand_total')
        invoice_id = request.POST.get('invoice_id')
        time_of_invoice = request.POST.get('time_of_invoice')
        hotel_exists(data,hotel_id)

        invoices = astra_service.get_invoice_by_invoice_id(invoice_id)
        for i in invoices:
            invoiceList.append(i)
            foodIds.append(i["product"])

        foodIds = set(foodIds)
        products = astra_service.get_food_by_food_ids(foodIds)

        template = get_template('invoice_admin.html')
        context = {
            "invoices": invoiceList,
            "invoice_id":invoice_id,
            "invoice_date":time_of_invoice,
            "data": data,
            "foods":products,
            "grand_total":grand_total,
        }

        html = template.render(context)
        pdf = render_to_pdf('invoice.html', context)
        if pdf:
            response = HttpResponse(pdf, content_type='application/pdf')
            filename = "Invoice_%s" %(invoice_id)
            content = "inline; filename='%s'" %(filename)
            download = request.GET.get("download")
            if download:
                content = "attachment; filename='%s'" %(filename)
            response['Content-Disposition'] = content
            return response
        return HttpResponse("Not found")

    def get(self, request, *args, **kwargs):

        data = {}
        invoiceList = []
        invoice_numbers = []
        customer_info_list = []
        if astra_service.check_connection() == False:
            astra_service.connect()

        page = request.GET.get("page")
        hotel_id = request.session.get('hotelid')
        invoices = astra_service.get_invoice_by_hotel_id(hotel_id)

        for i in invoices:
            invoice_numbers.append(i["invoice_id"])

        invoice_numbers = set(invoice_numbers)

        for invoice_id in invoice_numbers:
            result = astra_service.get_invoice_by_invoice_id_with_limit_1(invoice_id)
            for out in result:
                invoiceList.append(out)
        invocieList = invoiceList.sort(key=lambda x:x["time_of_invoice"],reverse=True)

        p = Paginator(invoiceList,10)
        page_obj = p.get_page(page)

        data["invoices"] = page_obj
        data["invoice_numbers"] = invoice_numbers
        return render(request , 'invoice_details.html'  , data)


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

# AutoLoss copy from my HPA paper
# FocalBCELoss & FocalMSELoss copy from LTG

import torch
import torch.nn as nn
import torch.nn.functional as F

epsilon = 1e-8

class RectifyLoss(nn.Module):
    '''RectifyLoss'''

    def __init__(self, gamma=1, thr=0.5):
        super(RectifyLoss, self).__init__()
        self.gamma = gamma
        self.thr = thr

    def agree_mask(self, p, y):
        '''return 0-1 agree mask, p > 0.5 for y = 1, p < 0.5 for y = 0'''
        sign = (p - self.thr) * (y - self.thr)
        return torch.sigmoid(1e8 * sign)

    def mask_tp(self, p, y):
        '''tp is y == 1 and p agree with y'''
        return y * self.agree

    def mask_fp(self, p, y):
        '''fp is y == 0 and p not agree with y'''
        return (1 - y) * (1 - self.agree)

    def mask_tn(self, p, y):
        '''tn is y == 0 and p agree with y'''
        return (1 - y) * self.agree

    def mask_fn(self, p, y):
        '''fn is y == 1 and p not agree with y'''
        return y * (1 - self.agree)

    def forward(self, p, y):
        pass

class SFocalLoss(nn.Module):
    '''Simple Focal Loss without alpha t'''
    def __init__(self, gamma=2, reduction="elementwise_mean"):
        super(SFocalLoss, self).__init__()
        self.gamma = gamma
        self.reduction = reduction

    def forward(self, p, y):
        pt = y * p + (1 - y) * (1 - p)
        pt = pt.clamp(epsilon, 1.0 - epsilon)

        w = (1 - pt).pow(self.gamma)
        w = w.clamp(epsilon, 10000.0)
        loss = -w * pt.log() / 2
        if self.reduction == 'none':
            return loss
        else:
            return torch.mean(loss)

class FECLoss(nn.Module):
    '''auto weighted loss, focus on error correction'''
    def __init__(self, alpha=100, gamma=1,
                 reduction='mean', thr=0.5):
        '''
        alpha controls weights between bce & penalty.
        gamma controls penalty level.
        p > thr as positive, otherwise negative.
        '''
        super(FECLoss, self).__init__()
        self.alpha = alpha
        self.gamma = gamma
        self.reduction = reduction
        self.thr = thr

    def agree_mask(self, p, y):
        '''return 0-1 agree mask, p > 0.5 for y = 1, p < 0.5 for y = 0'''
        sign = (p - self.thr) * (y - self.thr)
        return torch.sigmoid(1e8 * sign)

    def mask_tp(self, p, y):
        '''tp is y == 1 and p agree with y'''
        return y * self.agree

    def mask_fp(self, p, y):
        '''fp is y == 0 and p not agree with y'''
        return (1 - y) * (1 - self.agree)

    def mask_tn(self, p, y):
        '''tn is y == 0 and p agree with y'''
        return (1 - y) * self.agree

    def mask_fn(self, p, y):
        '''fn is y == 1 and p not agree with y'''
        return y * (1 - self.agree)

    def forward(self, p, y):
        oloss = F.binary_cross_entropy(p, y, reduction='none')

        self.agree = self.agree_mask(p, y)
        nsample, nlabel = y.shape

        tp_ind = self.mask_tp(p, y)
        fp_ind = self.mask_fp(p, y)
        tn_ind = self.mask_tn(p, y)
        fn_ind = self.mask_fn(p, y)

        tp = torch.sum(tp_ind, dim=0)
        fp = torch.sum(fp_ind, dim=0)
        tn = torch.sum(tn_ind, dim=0)
        fn = torch.sum(fn_ind, dim=0)

        fp_coef = fp / (tn + 1)
        fn_coef = fn / (tp + 1)
        fp_w = fp_coef * fp_ind
        fn_w = fn_coef * fn_ind

        penalty = fp_w ** self.gamma + fn_w ** self.gamma
        weights = 1 + self.alpha * penalty / nsample

        w_loss = weights * oloss

        if self.reduction == 'none':
            return w_loss
        else:
            return torch.mean(w_loss)

class F1Loss(nn.Module):
    '''add f1 term to loss'''

    def __init__(self, bce=False, factor=1.0, thr=0.5):
        super(F1Loss, self).__init__()
        self.bce = bce
        self.factor = factor
        self.thr = thr

    def forward(self, p, y):
        floss = self.compute_floss(p, y).float()
        if self.bce:
            oloss = F.binary_cross_entropy(p, y, reduction='elementwise_mean')
            floss = oloss + floss * self.factor
        return floss

    def agree_mask(self, p, y):
        '''return 0-1 agree mask, p > 0.5 for y = 1, p < 0.5 for y = 0'''
        sign = (p - self.thr) * (y - self.thr)
        return torch.sigmoid(1e8 * sign)

    def mask_tp(self, p, y):
        '''tp is y == 1 and p agree with y'''
        return y * self.agree

    def mask_fp(self, p, y):
        '''fp is y == 0 and p not agree with y'''
        return (1 - y) * (1 - self.agree)

    def mask_tn(self, p, y):
        '''tn is y == 0 and p agree with y'''
        return (1 - y) * self.agree

    def mask_fn(self, p, y):
        '''fn is y == 1 and p not agree with y'''
        return y * (1 - self.agree)

    def compute_floss(self, p, y):
        self.agree = self.agree_mask(p, y)

        tp_ind = self.mask_tp(p, y)
        fp_ind = self.mask_fp(p, y)
        fn_ind = self.mask_fn(p, y)

        tp = torch.sum(tp_ind, dim=0)
        fp = torch.sum(fp_ind, dim=0)
        fn = torch.sum(fn_ind, dim=0)

        f1 = tp / (tp + 0.5 * fp + 0.5 * fn + epsilon)
        ma_f1 = torch.mean(f1)

        return 1 - ma_f1

class FocalMSELoss(nn.Module):
    """
    this criterion is a implementation of Focal Loss,
    Loss(y,p)=alpha*(1-p)^gamma*(y-p)^2 when y=1
             =(1-alpha)*p^gamma*(y-p)^2
    Args:
        alpha(float,double): a scalar factor for this criterion
        gamma(float,double): reduces the relative loss for well-classified
            examples, and putting more focus on hard, misclassified examples
        size_average(bool): By default, the losses are averaged over each
            minibatch. However, if the field size_average is set to False,
            then the losses are instead summed for each minibatch
    """

    def __init__(self, alpha, gamma, size_average):
        super(FocalMSELoss, self).__init__()
        self.alpha = alpha
        self.gamma = gamma
        self.size_average = size_average

    def forward(self, inputs, targets):
        N = inputs.size(0)
        C = inputs.size(1)
        MseLoss = torch.pow(targets - inputs, 2)
        chaZhi = torch.abs(targets - inputs)
        MseLoss = torch.mul(MseLoss, torch.pow(chaZhi, self.gamma))

        for i in range(N):
            for j in range(C):
                if targets[i][j] == 1:
                    MseLoss[i][j] = MseLoss[i][j]*self.alpha[j]
        if self.size_average:
            loss = MseLoss.mean()
        else:
            loss = MseLoss.sum()
        return loss

class FocalBCELoss(nn.Module):

    def __init__(self, alpha, gamma, size_average):
        super(FocalBCELoss, self).__init__()
        self.alpha = alpha
        self.gamma = gamma
        self.size_average = size_average

    def forward(self, inputs, targets):
        N = inputs.size(0)
        C = inputs.size(1)

        PosLoss = torch.mul(torch.log(inputs), torch.pow(1-inputs, self.gamma))
        LeftLoss = torch.mul(targets, PosLoss)
        for i in range(N):
            for j in range(C):
                LeftLoss[i][j] = LeftLoss[i][j] * self.alpha[j]

        NegLoss = torch.mul(torch.log(1-inputs), torch.pow(inputs, self.gamma))
        RightLoss = torch.mul(1-targets, NegLoss)

        Loss = -1*(RightLoss + LeftLoss)

        if self.size_average:
            loss = Loss.mean()
        else:
            loss = Loss.sum()
        return loss


# TODO: get info on available graphs
import os
import random
import zipfile

import igraph as ig
import numpy as np
import pandas as pd

PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
DATA_PATH = os.path.join(PATH, 'processed_graphs')
MSIGDB_PATH = os.path.join(PATH, 'raw_graphs/msigdb_v2023.1.Hs_json_files_to_download_locally.zip')

def get_available_graphs():
    files = os.listdir(DATA_PATH)
    return files

def get_available_msigdb():
    f = zipfile.ZipFile(MSIGDB_PATH)
    files = f.namelist()
    f.close()
    return files

def load_graph(filename):
    files = os.listdir(DATA_PATH)
    if filename not in files:
        # if filename not in files, try opening it as a path
        if not os.path.exists(filename):
            raise FileNotFoundError()
    else:
        filename = os.path.join(DATA_PATH, filename)
    if 'tsv' in filename:
        df = pd.read_csv(filename, sep='\t')
    else:
        df = pd.read_csv(filename)
    return df

def _load_msigdb(data, object_category='BiologicalProcess', object_id_prefix='MSigDB',
        predicate='participates_in'):
    """
    Returns a pandas edge table and a graph
    """
    import gene_names
    new_entries = []
    for k, v in data.items():
        gene_set_name = k
        source = v['exactSource']
        ref = v['pmid']
        genes = v['geneSymbols']
        try:
            gene_ids = gene_names.get_ids(genes)
        except:
            gene_ids = []
            for g in genes:
                try:
                    gene_id = gene_names.get_ids([g])
                    gene_ids.append(gene_id)
                except:
                    continue
        for gene_id, symbol in zip(gene_ids, genes):
            entry = {}
            entry['subject_category'] = 'Gene'
            entry['subject_id_prefix'] = 'NCBIGene'
            entry['subject_id'] = gene_id
            entry['subject_name'] = symbol
            entry['predicate'] = predicate
            entry['object_category'] = object_category
            entry['object_id_prefix'] = object_id_prefix
            entry['object_id'] = gene_set_name
            entry['object_name'] = gene_set_name
            entry['Primary_Knowledge_Source'] = source
            entry['Knowledge_Source'] = v['collection']
            entry['publications'] = ref
            new_entries.append(entry)
    return pd.DataFrame(new_entries)

def load_msigdb(name):
    """Load an msigdb graph as a dataframe. Input name is one of the filenames returned by get_available_msigdb."""
    import json
    f = zipfile.ZipFile(MSIGDB_PATH)
    m = f.getinfo(name)
    json_file = f.open(m)
    data = json.load(json_file)
    df = _load_msigdb(data)
    f.close()
    return df

def df_to_networkx(df, directed=False):
    """
    Converts a panda dataframe to a networkx Graph (or DiGraph), with node and edge attributes.
    """
    import networkx as nx
    create_using = nx.Graph
    if directed:
        create_using = nx.DiGraph
    df['subject_id_full'] = df['subject_id_prefix'] + '::' + df['subject_id'].astype(str)
    df['object_id_full'] = df['object_id_prefix'] + '::' + df['object_id'].astype(str)
    graph = nx.from_pandas_edgelist(df, source='subject_id_full', target='object_id_full',
            edge_attr=['predicate',
                       'Primary_Knowledge_Source',
                       'Knowledge_Source',
                       'publications'],
            create_using=create_using)
    node_attributes = {}
    for _, row in df.iterrows():
        if row['subject_id'] not in node_attributes:
            node_attributes[row['subject_id_full']] = {
                    'id': row['subject_id'],
                    'id_prefix': row['subject_id_prefix'],
                    'name': row['subject_name'],
                    'category': row['subject_category']}
        if row['object_id'] not in node_attributes:
            node_attributes[row['object_id_full']] = {
                    'id': row['object_id'],
                    'id_prefix': row['object_id_prefix'],
                    'name': row['object_name'],
                    'category': row['object_category']}
    nx.set_node_attributes(graph, node_attributes)
    return graph

def df_to_graph(df, directed=False):
    """
    Converts a panda dataframe to an igraph.Graph (or DiGraph), with node and edge attributes.
    """
    df['subject_id_full'] = df['subject_id_prefix'] + '::' + df['subject_id'].astype(str)
    df['object_id_full'] = df['object_id_prefix'] + '::' + df['object_id'].astype(str)
    # reorder?
    edges_df = df[['subject_id_full', 'object_id_full', 'predicate', 'Primary_Knowledge_Source', 'Knowledge_Source', 'publications']]
    graph = ig.Graph.DataFrame(edges_df, directed=directed, use_vids=False)
    node_attributes = {}
    for i, row in df.iterrows():
        if row['subject_id'] not in node_attributes:
            node_attributes[row['subject_id_full']] = {
                    'id': row['subject_id'],
                    'id_prefix': row['subject_id_prefix'],
                    'feature_name': row['subject_name'],
                    'category': row['subject_category']}
        if row['object_id'] not in node_attributes:
            node_attributes[row['object_id_full']] = {
                    'id': row['object_id'],
                    'id_prefix': row['object_id_prefix'],
                    'feature_name': row['object_name'],
                    'category': row['object_category']}
    for v in graph.vs:
        attributes = node_attributes[v['name']]
        v.update_attributes(attributes)
    return graph

def get_nodes_table(graph):
    """
    Returns a Pandas DataFrame of the nodes.
    """
    rows = []
    for v in graph.vs:
        row = {'id': v['name']}
        row.update(v.attributes())
        rows.append(row)
    return pd.DataFrame(rows)

def get_names_to_ids(graph, category=None):
    """Returns a dict mapping node names to IDs (ignoring prefixes and categories so on)"""
    names_to_ids = {}
    for v in graph.vs:
        if category is not None and v['category'] != category:
            continue
        names_to_ids[v['feature_name']] = v['name']
    return names_to_ids

def get_names_to_ids_networkx(graph, category=None):
    """Returns a dict mapping node names to IDs (ignoring prefixes and categories so on)"""
    names_to_ids = {}
    for n, attrs in graph.nodes.items():
        if category is not None and attrs['category'] != category:
            continue
        names_to_ids[attrs['name']] = n
    return names_to_ids

def get_spoke_categories(graph):
    return set(attrs['category'] for attrs in graph.vs)

def get_spoke_sources(graph):
    return set(attrs['source'] for attrs in graph.vs)

def spoke_identifiers_to_ids(graph, category, source=None):
    """
    Returns a mapping from SPOKE identifiers to IDs.

    category: 'Protein', 'Gene', 'Compound', 'Disease', etc
    source: 'KEGG', ...
    """
    identifiers_to_ids = {}
    for v in graph.vs:
        attrs = v.attributes()
        if 'category' in attrs and attrs['category'] == category:
            if source is None or ('source' in attrs and attrs['source'] == source):
                identifiers_to_ids[attrs['identifier']] = v['name']
    return identifiers_to_ids

def spoke_identifiers_to_ids_networkx(graph, category, source=None):
    """
    Returns a mapping from SPOKE identifiers to IDs.

    category: 'Protein', 'Gene', 'Compound', 'Disease', etc
    source: 'KEGG', ...
    """
    identifiers_to_ids = {}
    for n, attrs in graph.nodes.items():
        if 'category' in attrs and attrs['category'] == category:
            if source is None or ('source' in attrs and attrs['source'] == source):
                identifiers_to_ids[attrs['identifier']] = n
    return identifiers_to_ids

def get_category_ids_to_nodes(graph, category):
    """
    Returns a dict that maps from identifiers in the specified category to graph node indices, for graphs that are not SPOKE.
    """
    identifiers_to_ids = {}
    for v in graph.vs:
        if v['category'] == category:
            identifiers_to_ids[v['id']] = v.index
    return identifiers_to_ids

def largest_component(graph):
    "Returns a subgraph containing the largest connected component of the given graph."
    components = graph.connected_components()
    sizes = components.sizes()
    largest_component = 0
    largest_component_size = 0
    for i, c in enumerate(sizes):
        if c > largest_component_size:
            largest_component_size = c
            largest_component = i
    subgraph = components.subgraph(largest_component)
    return subgraph

def nodes_in_category(graph, category, attr_name='category'):
    "Returns all nodes that are within a given category, as a list of igraph.Vertex objects."
    nodes_in_category = []
    for v in graph.vs:
        attrs = v.attributes()
        if attr_name in attrs and attrs[attr_name] == category:
            nodes_in_category.append(v)
    return nodes_in_category

def nodes_in_category_networkx(graph, category, attr_name='category'):
    nodes_in_category = []
    for n, attrs in graph.nodes.items():
        if attr_name in attrs and attrs[attr_name] == category:
            nodes_in_category.append(n)
    return nodes_in_category

def random_nodes_in_category(graph, category, n_nodes):
    """
    Returns a list of random node ids in the given category.
    """
    nodes_in_category = []
    for v in graph.vs:
        attrs = v.attributes()
        if 'category' in attrs and attrs['category'] == category:
            nodes_in_category.append(attrs['name'])
    return random.sample(nodes_in_category, n_nodes)

def random_nodes(graph, n_nodes):
    """
    Returns a list of random node ids.
    """
    return random.sample([v['name'] for v in graph.vs], n_nodes)

def random_nodes_in_category_networkx(graph, category, n_nodes):
    """
    Returns a list of random spoke ids in the given category.
    """
    nodes_in_category = []
    for n, attrs in graph.nodes.items():
        if 'category' in attrs and attrs['category'] == category:
            nodes_in_category.append((n, attrs['identifier']))
    return random.sample(nodes_in_category, n_nodes)

# random nodes with similar degree distributions? investigative bias - constrain null model to be similar to the problem. We could select random nodes among the nodes that are in the general set...
def degree_sample(graph, node_list, n_samples, dist_or_probs):
    """
    Degree-based node sampling, to sample nodes such that they approximately match the given degree distribution.

    Args:
        graph - an igraph.Graph
        node_list - a list of vertices to be sampled from
        n_samples - the number of points to sample
        dist_or_probs - Either a scipy.stats distribution that has a pdf function (could probably use a kernel density estimation), or a list/array of probabilities over node_list
    """
    prob_vals = []
    # lol
    if hasattr(dist_or_probs, '__getitem__'):
        prob_vals = dist_or_probs
    else:
        dist = dist_or_probs
        prob_vals = dist.pdf(graph.degree(node_list))
        prob_vals = np.array(prob_vals)
        prob_vals = prob_vals/prob_vals.sum()
    sampled_nodes = np.random.choice(node_list, size=n_samples, replace=False, p=prob_vals)
    return sampled_nodes

# subgraph
# randomize the graph... randomly shuffle the nodes/edges?
# shuffle the edges in the network, look at the set again and again. preserve the degree...

# TODO: estimate from whole graph
def randomize_graph(G):
    """
    Randomizes the given graph using edge swapping while preserving the degree distribution.

    Args:
    G (networkx.Graph): Input graph.

    Returns:
    networkx.Graph: Randomized graph.
    """
    G_rand = G.copy()
    edges = list(G_rand.edges)
    nodes = list(G_rand.nodes)
    n = len(edges)

    for _ in range(n):
        # Pick two edges and swap
        edge1 = random.choice(edges)
        edge2 = random.choice(edges)


import socket
import subprocess
import sys
import tempfile
import time

class MilterRunError(Exception):
    def __init__(self, return_code, command_line, stdout, stderr):
        self.return_code = return_code
        self.command_line = command_line
        self.stdout = stdout
        self.stderr = stderr

    def __str__(self):
        return f"""
Command line: {self.command_line}
Return code: {self.return_code}
Standard output:
=================================
{self.stdout.strip()}
=================================
Standard error:
=================================
{self.stderr.strip()}
=================================
"""

class MilterRunner(object):
    def __init__(self,
                 path,
                 *args,
                 env=None,
                 start_timeout=5,
                 host="127.0.0.1",
                 port=20025):
        self._path = path
        self._args = args
        self._env = env
        self._start_timeout = start_timeout
        self._host = host
        self._port = port
        self.connection_spec = f"inet:{self._port}@{self._host}"
        command_line = [
            sys.executable,
            self._path,
            "--connection-spec", self.connection_spec,
            *self._args,
        ]
        self.stdout = tempfile.NamedTemporaryFile(mode="w+",
                                                  suffix=".stdout.log")
        self.stderr = tempfile.NamedTemporaryFile(mode="w+",
                                                  suffix=".stderr.log")
        self.process = subprocess.Popen(command_line,
                                        env=self._env,
                                        stdout=self.stdout,
                                        stderr=self.stderr,
                                        text=True)
        start = time.time()
        while True:
            tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            try:
                tcp_client.connect((self._host, self._port))
                break
            except ConnectionRefusedError:
                if self.process.poll() is not None:
                    self.stdout.seek(0)
                    self.stderr.seek(0)
                    raise MilterRunError(self.process.returncode,
                                         self.process.args,
                                         self.stdout.read(),
                                         self.stderr.read())
                if time.time() - start > self._start_timeout:
                    raise
            finally:
                tcp_client.close()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, value, traceback):
        self.process.terminate()


import logging
import os
import shutil
import subprocess
from contextlib import contextmanager

from turingarena.driver.sandbox.connection import create_failed_connection
from turingarena.driver.sandbox.popen import create_popen_process_connection
from turingarena.driver.sandbox.rlimits import set_rlimits
from turingarena.driver.sandbox.runner import ProgramRunner

logger = logging.getLogger(__name__)

class GoProgramRunner(ProgramRunner):
    @property
    def executable_path(self):
        return os.path.join(self.temp_dir, "algorithm")

    @contextmanager
    def run_in_process(self):
        with open(os.path.join(self.temp_dir, "skeleton.go"), "w") as f:
            self.language.Generator().generate_to_file(self.interface, f)

        shutil.copyfile(self.program.source_path, os.path.join(self.temp_dir, "solution.go"))

        cli = [
            "go",
            "build",
            "-o", self.executable_path,
            os.path.join(self.temp_dir, "skeleton.go"),
            os.path.join(self.temp_dir, "solution.go"),
        ]
        logger.debug(f"Running {' '.join(cli)}")
        try:
            subprocess.run(
                cli,
                universal_newlines=True,
                check=True,
            )
        except subprocess.CalledProcessError:
            yield create_failed_connection("Compilation failed.")
        else:
            # sandbox_path = pkg_resources.resource_filename(__name__, "sandbox.py")
            yield create_popen_process_connection(
                [self.executable_path],
                preexec_fn=set_rlimits,
            )


#coding=utf-8

from collections import namedtuple

class NTNode:
    def __init__(self, start, end):
        if start < 0: raise IndexError('negative node start: %d' % start)
        if start >= end: raise ValueError('empty node: [%d, %d)' % (start, end))
        self.start = start
        self.end = end
    def isIn(self, pos):
        return (pos >= self.start) and (pos < self.end)
    def isStart(self, pos):
        return pos == self.start
    def isEnd(self, pos):
        return pos == self.end-1
    def isInner(self, pos):
        return (pos > self.start) and (pos < self.end-1)
#     @property
#     def start(self):
#         return self.start
#     @property
#     def end(self):
#         return self.end
    def length(self):
        return self.end-self.start
    def name(self):
        return u'[node%d-%d]'%(self.start, self.end)
    def description(self):
        print('\t'.join(['%s:%s' % item for item in self.__dict__.items()]))

class NTCGraph(NTNode):
    def __init__(self, start, end, text):
        NTNode.__init__(self, start, end)
        self.text = text
        self.Edge = namedtuple('Edge', ['start', 'end', 'length'])
        self.nodes=[]
        #self.nodes = [NTNode(start, end)]
        self.edges = []
        self.space = u'　'
    def text(self):
        return self.text
    def __findNodeIndex(self, pos):
        #返回pos在nodes列表位置i,i中的相对索引
#         print(pos)
        for i in range(len(self.nodes)):
            if self.nodes[i].isIn(pos):
                return (i, pos-self.nodes[i].start)
        raise IndexError('out of graph index: %d' % pos)
    def __addEdge(self, start, end, length, bidirection=False):
        #check duplicated edges!
        self.edges.append(self.Edge(start, end, length))
        if bidirection:
            assert(length == 0)
            self.edges.append(self.Edge(end, start, length))

    def split(self, pos, direction=0):
        '''
        split node on pos into two nodes:   pos-1 | pos
        pos: position
        direction: direct of edge. if None: no edge, 1: forward <---, 2: backward--->, 0: both<--->
        '''
        nodeIdx, offset = self.__findNodeIndex(pos)
        if offset == 0:
            raise IndexError('split at the beginning of node: %d' % pos)
        if isinstance(self.nodes[nodeIdx], NTCGraph):
            self.nodes[nodeIdx].split(pos, direction)
        else:
            newnode = NTNode(pos, self.nodes[nodeIdx].end)
            self.nodes[nodeIdx].end = pos
            self.nodes.insert(nodeIdx+1, newnode) #after nodeIdx

            if direction != None:
                if (direction == 1):
                    self.__addEdge(start = pos, end = pos-1, length = 0)
                else:
                    self.__addEdge(start = pos-1, end = pos, length = 0,
                                   bidirection = False if direction == 2 else True)

    def setRelation(self, start, end, length, direction=0):
        '''
        direction : 0,1: | start(beginning of node)
                    2: start(end of node) |
        '''
        if not self.isIn(start) or not self.isIn(end):
            raise IndexError('out of index: (%d, %d)' % (start, end))
        if (start == end):
            raise ValueError('add edge with same start and end: %d' % start)
        if length < 0:
            raise ValueError('negative length of naming: %d' % length)

        snodei, _ = self.__findNodeIndex(start)
        enodei, _ = self.__findNodeIndex(end)
        if isinstance(self.nodes[snodei], NTCGraph):
            if isinstance(self.nodes[enodei], NTCGraph):
                if snodei != enodei:
                    raise ValueError('failed to add edge between enclosed-clauses')
                else:
                    self.nodes[snodei].setRelation(start, end, length, direction)
                    return
            else:
                if self.nodes[snodei].isInner(start):
                    raise ValueError('edge start from inner of enclosed-clause')
        else:
            if isinstance(self.nodes[enodei], NTCGraph):
                if self.nodes[enodei].isInner(end):
                    raise ValueError('edge into enclosed-clause')
            elif self.nodes[snodei].isInner(start):
                self.split(start+1 if direction==2 else start, direction=None)
        self.__addEdge(start, end, length, True if direction == 0 else False)

    def addSubgraph(self, start, end):
        if (start < self.start) or (end > self.end) or (end-start >= self.end-self.start):
            raise ValueError('addSubgraph: subgraph should be sub of parent')
        snodei, soffset = self.__findNodeIndex(start)
        enodei, _ = self.__findNodeIndex(end-1)
        if snodei != enodei:
            raise ValueError('addSubgraph: cannot enclose two or more nodes')
        if self.nodes[snodei].isStart(start) and self.nodes[enodei].isEnd(end):
            raise ValueError('addSubgraph: node is already in list')
        if isinstance(self.nodes[snodei], NTCGraph):
            self.nodes[snodei].addSubgraph(start, end)
        else:
            if soffset == 0:
                self.nodes[snodei].start = end
                self.nodes.insert(snodei, NTCGraph(start, end, self.text))
            elif end < self.nodes[snodei].end:
                #node/subgraph/node
                newnode = NTNode(self.nodes[snodei].start, start)
                sg = NTCGraph(start, end, self.text)
                self.nodes[snodei].start = end
                self.nodes.insert(snodei, sg)
                self.nodes.insert(snodei, newnode)
            else:
                self.nodes[snodei].end = start
                self.nodes.insert(snodei+1, NTCGraph(start, end, self.text))

    def getDepends(self):
        dps = {}
        for ni in range(len(self.nodes)):
            dps[ni] = ([], []) #forward and backward
        for ei in range(len(self.edges)):
            edge = self.edges[ei]
            snodei, _ = self.__findNodeIndex(edge.start)
            enodei, _ = self.__findNodeIndex(edge.end)
#             print(edge)
            assert snodei != enodei  #边start end不能在同一顶点

            assert not self.nodes[snodei].isInner(edge.start)#只有头边和尾边
#             assert not (self.nodes[snodei].isStart(edge.start) and self.nodes[enodei].isStart(edge.end))

#             assert not (self.nodes[snodei].isEnd(edge.start) and self.nodes[enodei].isEnd(edge.end))
            #snodei --> enodei
            if (self.nodes[snodei].isStart(edge.start)):
                dps[snodei][0].append(ei)
            else:
                dps[snodei][1].append(ei)
        return dps

    def toNTClauses(self):
        '''
        transfer graph to NTClauses
        '''
        dps = self.getDepends()
        visited = [False] * len(self.nodes)

        def __expandNode(ni):
            '''
            expand node to clauses
            '''
            def __traverseForward(ei, suffix):
                '''
                dps: depends between nodes
                ei: edge index
                suffix: suffix in unicode list
                return: list of unicode list
                '''
                edge = self.edges[ei]
                nodei, _ = self.__findNodeIndex(edge.end)

                if edge.length > 0:
                    assert(not isinstance(self.nodes[nodei], NTCGraph))
                    suffix = list(self.text[edge.end-edge.length+1 : edge.end+1])+suffix
                    return [suffix]
                else:
                    if isinstance(self.nodes[nodei], NTCGraph):
                        suffix = list(self.nodes[nodei].name())+suffix
                    else:
                        suffix = list(self.text[self.nodes[nodei].start : edge.end+1])+suffix
                    ret = []
                    for e in dps[nodei][0]:
                        ret.extend(__traverseForward(e, suffix))
                    if len(ret) == 0:
                        return [suffix]
                    else:
                        return ret

            def __traverseBackward(ei, prefix):
                '''
                dps: depends between nodes
                ei: edge index
                suffix: suffix in unicode list
                return: list of unicode list
                '''
                edge = self.edges[ei]
                assert (edge.length == 0)
                nodei, offset = self.__findNodeIndex(edge.end)
                if (offset == 0): visited[nodei] = True
                if isinstance(self.nodes[nodei], NTCGraph):
                    prefix.extend(list(self.nodes[nodei].name()))
                else:
                    prefix.extend(list(self.text[edge.end : self.nodes[nodei].end]))
                ret = []
                for e in dps[nodei][1]:
                    ret.extend(__traverseBackward(e, prefix))
                if len(ret) == 0:
                    return [prefix]
                else:
                    return ret

            if isinstance(self.nodes[ni], NTCGraph):
                ret = [list(self.nodes[ni].name()+u':[')]
                ret.extend(self.nodes[ni].toNTClauses())
                ret.append(list(u']'))
                return ret

            if visited[ni]: return []
            visited[ni] = True

            pre = []
            for ei in dps[ni][0]:
                pre.extend(__traverseForward(ei, []))
            post = []
            for ei in dps[ni][1]:
                post.extend(__traverseBackward(ei, []))
            ret = []
            text = list(self.text[self.nodes[ni].start:self.nodes[ni].end])
            if len(pre) > 0:
                if len(post) > 0:
                    for preT in pre:
                        for postT in post:
                            ret.append(preT+text+postT)
                else:
                    ret = [preT+text for preT in pre]
            elif len(post) > 0:
                ret = [text+postT for postT in post]
            else:
                return [text]
            return ret

        clauses = []
        for ni in range(len(self.nodes)):
            for ltxt in __expandNode(ni):
                clause = u''.join(ltxt)
                clauses.append(clause)
        return clauses

    def toIndentedString(self):
        '''
        transfer graph to indented string
        '''
        dps = self.getDepends()

        def indentedPos(ni):
            if (len(dps[ni][0]) == 0):
                return 0
            else:
                mdis = self.end-self.start
                mlen = -1
                for ei in dps[ni][0]:
                    edge = self.edges[ei]
                    nodei, offset = self.__findNodeIndex(edge.end)
                    if (abs(edge.end-edge.start) < mdis):
                        mlen = offset+indentedPos(nodei)+1
                        mdis = abs(edge.end-edge.start)
                return mlen

        clauses = []
        for ni in range(len(self.nodes)):
            ip = indentedPos(ni)
            if isinstance(self.nodes[ni], NTCGraph):
                cs = [self.nodes[ni].name()+u':[']
                cs.extend(self.nodes[ni].toIndentedString())
                cs.append(u']')
                clauses.extend([self.space*ip+s for s in cs])
            else:
                clause = u''.join([self.space]*ip+list(self.text[self.nodes[ni].start:self.nodes[ni].end]))
                clauses.append(clause)
        return clauses

    def description(self):
        print('g.start: %d' % self.start,'\t','g.end: %d' % self.end)
        print(self.text[self.start:self.end])
        print('nodes: %d' % len(self.nodes))
        for node in self.nodes:
            node.description()
        print('edges: %d' % len(self.edges))
        for e in self.edges:
            print(e)


from genericpath import exists
from os import makedirs
from os.path import exists
from PIL import Image, ImageDraw
from math import ceil
from functools import reduce
import constants as const
import utils

def main():
  file_names = utils.get_filenames(const.FOLDER_PATH, const.DB)
  if len(file_names) == 0:
    print('Нет ниодного изображения. Выход...')
    return
  pillow_images = utils.get_images(file_names, const.FOLDER_PATH)
  images = utils.resize_images(pillow_images, const.IMAGE_WIDTH, const.BLOCK_HEIGHT, const.IMAGE_RESOLUTION)
  ll = len(images)
  total_amount = reduce(lambda s, img: s + img.amount, images, 0)
  print(f' Всего изображений: {ll}\n Количество строк: {const.VERTICAL_AMOUNT}\n Количество столбцев: {const.HORIZONTAL_AMOUNT}')
  total_weight = input('- Введите вес подарка: ')
  total_price = input('- Введите цену подарка: ')
  layout_name = input('- Введите название сгенерированной картинки: ') or 'test'
  if not exists('result'):
    makedirs('result')

  layout_number = 1
  layouts_amount = ceil(ll / (const.HORIZONTAL_AMOUNT * const.VERTICAL_AMOUNT))
  pasted_counter = 0
  is_ended = False
  try:
    bg = Image.open(const.LAYOUT_BACKGROUND).resize((const.LAYOUT_WIDTH, const.LAYOUT_HEIGHT))
  except:
    print(f' Не удалось загрузить фон {const.LAYOUT_BACKGROUND}')

  while not is_ended:
    x_offset = const.LAYOUT_PADDING_HORIZONTAL + const.BLOCK_MARGIN
    y_offset = const.LAYOUT_PADDING_TOP + const.BLOCK_MARGIN
    layout = Image.new('RGB', [const.LAYOUT_WIDTH, const.LAYOUT_HEIGHT], (255, 255, 255))
    layout.paste(bg, (0, 0))
    # Layout number / layouts amount
    drawer_font = const.TEXT['NUMB']
    drawer_text = f'{layout_number} / {layouts_amount}'
    drawer_text_width = drawer_font.getsize(drawer_text)[0]
    drawer_size = (150, 60)
    drawer_position = (ceil(const.LAYOUT_WIDTH * 0.923), ceil(const.LAYOUT_HEIGHT * 0.95))
    layout.paste(utils.text_drawer(drawer_text, drawer_size, drawer_font, ((drawer_size[0] - drawer_text_width) // 2, 0)), drawer_position)
    # Price
    drawer_fill = (255, 25, 31)
    drawer_size = (400, 80)
    drawer_font = const.TEXT['INFO']
    drawer_text = total_price
    drawer_text_size = drawer_font.getsize(drawer_text)
    drawer_text_pos = ((drawer_size[0] - drawer_text_size[0]) // 2, (drawer_size[1] - drawer_text_size[1]) // 2)
    drawer_position = (ceil(const.LAYOUT_WIDTH * 0.8), ceil(const.LAYOUT_HEIGHT * 0.07))
    layout.paste(utils.text_drawer(drawer_text, drawer_size, drawer_font, drawer_text_pos, drawer_fill), drawer_position)
    # Weight
    drawer_text = total_weight
    drawer_text_size = drawer_font.getsize(drawer_text)
    drawer_text_pos = ((drawer_size[0] - drawer_text_size[0]) // 2, (drawer_size[1] - drawer_text_size[1]) // 2)
    drawer_position = (ceil(const.LAYOUT_WIDTH * 0.6357), ceil(const.LAYOUT_HEIGHT * 0.07))
    layout.paste(utils.text_drawer(drawer_text, drawer_size, drawer_font, drawer_text_pos, drawer_fill), drawer_position)
    # Amount
    drawer_text = f'{total_amount} штук'
    drawer_text_size = drawer_font.getsize(drawer_text)
    drawer_text_pos = ((drawer_size[0] - drawer_text_size[0]) // 2, (drawer_size[1] - drawer_text_size[1]) // 2)
    drawer_position = (ceil(const.LAYOUT_WIDTH * 0.4775), ceil(const.LAYOUT_HEIGHT * 0.07))
    layout.paste(utils.text_drawer(drawer_text, drawer_size, drawer_font, drawer_text_pos, drawer_fill), drawer_position)

    row = col = 0
    while not is_ended and col < const.HORIZONTAL_AMOUNT:
      while not is_ended and row < const.VERTICAL_AMOUNT:
        img = images[pasted_counter]
        x_offset_centred = x_offset + (const.IMAGE_WIDTH - img.size[0]) // 2
        y_offset_centred = y_offset + (const.BLOCK_HEIGHT - img.size[1]) // 2
        txt = utils.db_name(img.key, const.DB)
        sire = txt['sire']
        name = txt['name'].replace('гр)', 'г)').replace(' г)', 'г)')
        desc = txt.get('description', None)
        weight = txt.get('weight', None)
        if weight is not None and weight not in name:
          name += f' ({weight}г)'

        try:
          layout.paste(img, (x_offset_centred, y_offset_centred), mask=img.split()[3])
        except:
          layout.paste(img, (x_offset_centred, y_offset_centred))
        img.close()

        text = Image.new('RGBA', [const.TEXT_WIDTH, const.BLOCK_HEIGHT], (255, 255, 255))
        drawer = ImageDraw.Draw(text)

        text_x = text_y = 0
        drawer.text((text_x, text_y), sire, font=const.TEXT['SIRE'], fill=(120, 120, 120))

        text_x = const.TEXT['SIRE'].getsize(sire)[0] + 10
        drawer.text((text_x, text_y), f'{img.amount} шт', font=const.TEXT['ENUM'], fill=(18,183,45))

        text_x = 0
        text_y += const.TEXT['ENUM'].getsize(sire)[1] + const.TEXT_MARGIN
        name, shift = utils.to_multiline(name, const.TEXT['NAME'], const.TEXT_WIDTH, const.NAME_LINES)
        drawer.multiline_text((text_x, text_y), name, font=const.TEXT['NAME'], fill=(3, 3, 3))

        if desc is not None:
          text_y += shift + const.TEXT_MARGIN
          desc, shift = utils.to_multiline(desc, const.TEXT['DESC'], const.TEXT_WIDTH)
          drawer.multiline_text((text_x, text_y), desc, font=const.TEXT['DESC'], fill=(3, 3, 3))
          text_height = text_y + shift

        text_x = x_offset + const.IMAGE_WIDTH + const.IMAGE_TEXT_MARGIN
        text_y = y_offset + (const.BLOCK_HEIGHT - text_height) // 2
        layout.paste(text, (text_x, text_y))
        text.close()

        y_offset += const.BLOCK_HEIGHT + const.BLOCK_MARGIN
        pasted_counter += 1
        row += 1

        if pasted_counter >= ll:
          is_ended = True

      x_offset += const.BLOCK_WIDTH + const.BLOCK_MARGIN
      y_offset = const.LAYOUT_PADDING_TOP + const.BLOCK_MARGIN
      col += 1
      row = 0

    layout.convert('RGB').save(f"result/{layout_name}-{layout_number}.jpg")
    layout.close()
    print(f" Файл '{layout_name}-{layout_number}.jpg' сохранен в папке 'result'")
    layout_number += 1
  bg.close()

if __name__ == '__main__':
  main()
  input("\n Программа закончила работу\n Нажмите 'Enter' чтобы закрыть программу")


import speech_recognition as sr # recognise speech
import playsound # to play an audio file
from gtts import gTTS # google text to speech
import time
from time import sleep
import random
import os  # to remove created audio files
import requests
import pyttsx3
import paho.mqtt.client as mqtt
import sys
from vosk import Model, KaldiRecognizer
import pyaudio
import cv2
model = Model('vosk-model')
recognizer = KaldiRecognizer(model, 16000)
cap = pyaudio.PyAudio()
stream = cap.open(format=pyaudio.paInt16, channels=1, rate=16000, input=True, frames_per_buffer=8192)
import subprocess, signal
camera = ["python3", "VideoStream.py"]
regular_camera = subprocess.Popen(camera)

def check_internet():
    url = "http://www.google.com"
    timeout = 5
    try:
        request = requests.get(url, timeout=timeout)
        if request.status_code == 200:
            return 'online'
    except:
        pass
    return 'offline'

def there_exists(terms):
    for term in terms:
        if term in voice_data:
            return True

def recognize():
    if check_internet() == 'online':
        voice_data=listen()
    else:
        voice_data = offline_listen()
        print(f"Offline>> {voice_data.lower()}")  # print what user said
    return voice_data

def listen():
    speak("Listening to your order")
    r = sr.Recognizer()  # initialise a recogniser
    # listen for audio and convert it to text:
    with sr.Microphone() as source: # microphone as source

        r.pause_threshold = 1
        r.adjust_for_ambient_noise(source)
        print("Online Listening...")
        audio = r.listen(source)  # listen for the audio via source
        voice_data = ''

        try:
            print("Online Recognizing...")
            voice_data = r.recognize_google(audio)  # convert audio to text
        except sr.UnknownValueError: # error: recognizer does not understand
            speak('I did not get that')
        except sr.RequestError:
            speak('Sorry, the service is down') # error: recognizer is not connected
        print(f"Online>> {voice_data.lower()}") # print what user said
        return voice_data.lower()

def offline_speak(str):
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    engine.setProperty('voice', voices[0].id)
    engine.setProperty('rate', 150)
    engine.say(str)
    time.sleep(1)
    engine.runAndWait()

def offline_listen():
    stream.start_stream()
    while True:
        voice_data=''
        data = stream.read(4096, exception_on_overflow = False)
        if recognizer.AcceptWaveform(data):
            result = recognizer.Result()
            res = eval(result)
            voice_data = res['text']
        return voice_data

# get string and make a audio file to be played
def speak(audio_string):
    if check_internet() == 'online':
        tts = gTTS(text=audio_string, lang='en')  # text to speech(voice)
        r = random.randint(1,20000000)
        audio_file = 'audio' + str(r) + '.mp3'
        tts.save(audio_file) # save as mp3
        playsound.playsound(audio_file) # play the audio file
        print(f"Android: {audio_string}") # print what app said
        os.remove(audio_file) # remove audio file
    else:
        print ("We are offline")
        offline_speak(audio_string)
        print(f"Android: {audio_string}")  # print what app said

def start_face_tracking_subprocess():
    # start the subprocess
    subprocess_args = ["python3", "FaceTrackingFunction.py"]
    face_tracking_obj = subprocess.Popen(subprocess_args)
    return face_tracking_obj

def start_obstacle_avoid_subprocess():
    # start the subprocess
    subprocess_args = ["python3", "ObstacleAvoidanceFunction.py"]
    obstacle_avoidance_obj = subprocess.Popen(subprocess_args)
    return obstacle_avoidance_obj

def start_line_follow_subprocess():
    # start the subprocess
    subprocess_args = ["python3", "LineFollowFunction.py"]
    line_follow_obj = subprocess.Popen(subprocess_args)
    return line_follow_obj

def start_lane_follow_subprocess():
    # start the subprocess
    subprocess_args = ["python3", "LaneFollowFunction.py"]
    lane_follow_obj = subprocess.Popen(subprocess_args)
    return lane_follow_obj

def respond():
    # 1: greeting
    if there_exists(['android']):
        greetings = ["hey, how can I help you boss", "hey, what's up? boss", "I'm listening boss", "how can I help you? boss", "hello boss"]
        greet = greetings[random.randint(0, len(greetings)-1)]
        speak(greet)

    # 2: forward
    if there_exists(["forward", "go forward"]):
        speak("Going forward")
        return "forward"

    # 3: backward
    if there_exists(["backward", "go backward", "go back", "back", "beck"]):
        speak("Going backward")
        return 'backward'

    # 4: left
    if there_exists(["left", "go left", "turn left"]):
        speak("Turning left")
        return 'left'

    # 5: right
    if there_exists(["right", "go right", "turn right"]):
        speak("Turning right")
        return 'right'

    # 6: stop
    if there_exists(["stop", "finish"]):
        speak("Stopping")
        return 'stop'

    # 7: Line following
    if there_exists(["enable line", "line follow"]):
        speak("Enabling line follow")
        return 'line_on'

    if there_exists(["disable line", "line unfollow"]):
        speak("Disabling line follow")
        return 'line_off'

    # 8: Lane following
    if there_exists(["enable lane", "lane follow"]):
        speak("Enabling lane follow")
        return 'lane_on'

    if there_exists(["disable lane", "lane unfollow"]):
        speak("Disabling lane follow")
        return 'lane_off'

    # 9: Obstacle Avoidance
    if there_exists(["enable ultrasonic", "ultrasonic on"]):
        speak("Enabling ultrasonic")
        return 'ultrasonic_on'

    if there_exists(["disable ultrasonic", "ultrasonic unfollow"]):
        speak("Disabling ultrasonic")
        return 'ultrasonic_off'

    # 10: Face tracking
    if there_exists(["face", "face tracking"]):
        speak("Face tracking")
        return "face_on"

    if there_exists(["no tracking"]):
        speak("Stopping Face tracking")
        return "face_off"

    # 11: wait
    if there_exists(["wait", "hold on"]):
        speak("waiting")
        time.sleep(60)

    # 12: finish
    if there_exists(["exit", "quit", "goodbye"]):
        speak("going offline")
        sys.exit()

time.sleep(1)

client = mqtt.Client()
client.connect("test.mosquitto.org", 1883, 60)

# Define the cleanup function
def cleanup():
    regular_camera.terminate()
    face_track.send_signal(signal.SIGTERM)
    line_follow.send_signal(signal.SIGTERM)
    obstacle_avoid.send_signal(signal.SIGTERM)
    lane_follow.send_signal(signal.SIGTERM)

try:
    while True:
        voice_data = recognize() # get the voice input
        message = respond()
        print (message)
        if message == "face_on":
            regular_camera.terminate()
            face_track = start_face_tracking_subprocess()
        elif message == "face_off":
            face_track.terminate()
            face_track.send_signal(signal.SIGTERM)
        elif message == "line_on":
            regular_camera.terminate()
            line_follow = start_line_follow_subprocess()
        elif message == "line_off":
            line_follow.terminate()
            line_follow.send_signal(signal.SIGTERM)
        elif message == "ultrasonic_on":
            regular_camera.terminate()
            obstacle_avoid = start_obstacle_avoid_subprocess()
        elif message == "ultrasonic_off":
            obstacle_avoid.send_signal(signal.SIGTERM)
        elif message == "lane_on":
            regular_camera.terminate()
            lane_follow = start_lane_follow_subprocess()
        elif message == "lane_off":
            lane_follow.terminate()
            lane_follow.send_signal(signal.SIGTERM)
        else:
            client.publish("pibot/move", str(message), qos=1)
except KeyboardInterrupt:
    print("Keyboard interrupt detected, cleaning up...")
    cleanup()
finally:
    cleanup()


from rest_framework.generics import RetrieveUpdateDestroyAPIView, ListCreateAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet
from django_filters import rest_framework as filters
from .models import Movie, Saloon, Ticket
from .permissions import IsOwnerOrReadOnly, IsSuperuser, ReadOnly, IsStaff
from .serializers import MovieSerializer, SaloonSerilizer, TicketSerializer
from .pagination import CustomPagination
from .filters import MovieFilter

class ListCreateMovieAPIView(ListCreateAPIView):
    serializer_class = MovieSerializer
    queryset = Movie.objects.all()
    pagination_class = CustomPagination
    permission_classes = [ReadOnly | IsSuperuser]
    filter_backends = (filters.DjangoFilterBackend,)
    filterset_class = MovieFilter

    def perform_create(self, serializer):
        # Assign the user who created the movie
        serializer.save(creator=self.request.user)

class RetrieveUpdateDestroyMovieAPIView(RetrieveUpdateDestroyAPIView):
    serializer_class = MovieSerializer
    queryset = Movie.objects.all()
    permission_classes = [ReadOnly | IsSuperuser]

class SaloonViewSet(ModelViewSet):
    serializer_class = SaloonSerilizer
    queryset = Saloon.objects.all()

class TicketViewSet(ModelViewSet):
    serializer_class = TicketSerializer
    queryset = Ticket.objects.all()
    permission_classes = [ReadOnly | IsStaff]

    def perform_create(self, serializer):
        # Assign the user who created the ticket
        serializer.save(creator=self.request.user)


import pickle

import pytest

from bonobo import Bag, PickleReader, PickleWriter, settings
from bonobo.constants import BEGIN, END
from bonobo.execution.node import NodeExecutionContext
from bonobo.util.testing import CapturingNodeExecutionContext, FilesystemTester

pickle_tester = FilesystemTester('pkl', mode='wb')
pickle_tester.input_data = pickle.dumps([['a', 'b', 'c'], ['a foo', 'b foo', 'c foo'], ['a bar', 'b bar', 'c bar']])

def test_write_pickled_dict_to_file(tmpdir):
    fs, filename, services = pickle_tester.get_services_for_writer(tmpdir)

    with NodeExecutionContext(PickleWriter(filename, ioformat=settings.IOFORMAT_ARG0), services=services) as context:
        context.write(BEGIN, Bag({'foo': 'bar'}), Bag({'foo': 'baz', 'ignore': 'this'}), END)
        context.step()
        context.step()

    with fs.open(filename, 'rb') as fp:
        assert pickle.loads(fp.read()) == {'foo': 'bar'}

    with pytest.raises(AttributeError):
        getattr(context, 'file')

def test_read_pickled_list_from_file(tmpdir):
    fs, filename, services = pickle_tester.get_services_for_reader(tmpdir)

    with CapturingNodeExecutionContext(
        PickleReader(filename, ioformat=settings.IOFORMAT_ARG0), services=services
    ) as context:
        context.write(BEGIN, Bag(), END)
        context.step()

    assert len(context.send.mock_calls) == 2

    args0, kwargs0 = context.send.call_args_list[0]
    assert len(args0) == 1 and not len(kwargs0)
    args1, kwargs1 = context.send.call_args_list[1]
    assert len(args1) == 1 and not len(kwargs1)

    assert args0[0].args[0] == {
        'a': 'a foo',
        'b': 'b foo',
        'c': 'c foo',
    }
    assert args1[0].args[0] == {
        'a': 'a bar',
        'b': 'b bar',
        'c': 'c bar',
    }


import json
import unittest
import HtmlTestRunner
import requests
from pprint import pprint
import configparser
import os
from requests import HTTPError

def read_json(data_source):
    with open(data_source) as datafile:
        data = json.load(datafile)
        return data

def config_parser(header, parameter):
    config = configparser.ConfigParser()
    config.read("configuration/config.ini")
    return config.get(header, parameter)

def download(url, output_file_path):
    response = requests.get(url, stream=True)
    handle = open(output_file_path, 'wb')
    for chunk in response.iter_content(chunk_size=512):
        handle.write(chunk)
    print(os.path.getsize(output_file_path))
    assert os.path.getsize(output_file_path) is not None

class TestRequests(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.api_base_url = "https://reqres.in"

    def test_get(self):
        usn = config_parser("Credentials", "username")
        pas = config_parser("Credentials", "password")
        r = requests.get(self.api_base_url + "/api/login",
                         auth=(usn, pas))
        try:
            assert r.status_code == 200
        except AssertionError as error:
            print("GET API Failed.", error)

    def test_get_singleUser(self):
        r = requests.get(self.api_base_url + "/api/users/2")
        try:
            assert r.status_code == 200
        except AssertionError as error:
            print("GET User API Failed.", error)
        print(r.content)

    def test_post(self):
        data = read_json('data/post_data.json')
        r = requests.post(self.api_base_url + "/api/users", data)
        pprint(r.text)
        try:
            assert r.status_code == 201
        except AssertionError as error:
            print("POST API Failed.", error)

    def test_put(self):
        data = read_json("data/patch_data.json")
        r = requests.put(self.api_base_url + "/api/users/2", data)
        pprint(r.text)
        try:
            assert r.status_code == 200
        except AssertionError as error:
            print("PUT API Failed.", error)

    def test_patch(self):
        data = read_json("data/patch_data.json")
        r = requests.patch(self.api_base_url + "/api/users/2", data)
        pprint(r.text)
        try:
            assert r.status_code == 200
        except AssertionError as error:
            print("PUT API Failed.", error)

    def test_delete(self):
        r = requests.delete(self.api_base_url + '/api/users/2')
        try:
            assert r.status_code == 204
        except AssertionError as error:
            print("DELETE API Failed.", error)

    def test_pass_parameter_url(self):
        payload = {'page': '2'}
        r = requests.get(self.api_base_url + '/api/users', params=payload)
        print(r.headers)
        print("------------------------------------------------------------------")
        print(r.request.headers)

    def test_response_code(self):
        r = requests.get(
            'https://api.github.com/repos/requests/requests/git/commits/a050faf084662f3a352dd1a941f2c7c9f886d4ad')
        if r.status_code == requests.codes.ok:
            print(r.headers['content-type'])
        commit_data = r.json()
        pprint(commit_data['committer'])
        pprint(commit_data['message'])

    # information about the communication option available for a resource.
    def test_options_usage(self):
        verbs = requests.options('http://www.prideparrot.com/aboutme')
        pprint(verbs.headers["Server"])

    # status of the resource and HTTP header information
    def test_head_usage(self):
        verbs = requests.head('http://www.prideparrot.com/aboutme')
        pprint(verbs.headers)

    def test_parse_response(self):
        r = requests.get('https://api.github.com/repos/requests/requests/issues/482')
        #  issue = json.loads(r.text)
        #  print(issue['title'])
        #  print(issue['comments'])
        comments = r.json()
        self.assertNotEqual(comments['labels'][0]['name'], None)
        assert comments['labels'][0]['name'] is not None

    def test_post_multipart_encoded_file_upload(self):
        url = 'http://httpbin.org/post'
        files = {'file': open('google_logo.jpg', 'rb')}
        r = requests.post(url, files=files)
        assert r.status_code == 200

    def test_download_file(self):
        demo_url = "https://demo.silverstripe.org/Security/login?BackURL=%2Fadmin%2Fpages%2F"
        google_url = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png'
        download(demo_url, "downloaded_data.html")
        download(google_url, 'google_logo.jpg')

    def test_mock_api(self):
        try:
            url = "http://demo8060361.mockable.io/api/users/{0}".format(1)
            r = requests.options(url=url)
            print(r.text)
            print(r.headers["allow"])
        except HTTPError as error:
            print(error)

    def test_parse_json(self):
        with open("data/json_data.json") as f:
            data = json.load(f)
        assert (data["members"][1]["powers"][2]) == "Superhuman reflexes"

    def test_post_api(self):
        data = {'name': ['football', 'basketball'], 'job': ['leader', 'follower']}
        r = requests.post(self.api_base_url + "/api/users", data, verify=False)
        print(r.text)
        try:
            assert r.status_code == 201
        except AssertionError as error:
            print("POST API Failed.", error)

    @unittest.skip
    def test_get_api(self):
        url = 'http://pjody.mocklab.io/json/1'
        r = requests.get(url=url)
        self.assertEqual(r.status_code, 200)

if __name__ == '__main__':
    unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(output="example_dir"))


# !/usr/bin/env python3
# -*- coding: utf-8 -*-

import datetime
from datetime import datetime
from getpass import getpass
import googlemaps
from googlemaps import Client

# (API key) : Google maps API key
GOOGLE_MAP_API_KEY = ''

def get_direction(
            api_key,
            from_address,
            to_address,
            travelling_mode
        ):
    """Returns all the direction informations between two points

    Arguments:
        - api_key         : (str) googlemaps API key
        - from_address    : (str) departure location
        - to_address      : (str) arrival location
        - travelling_mode : (str) conveyance

    Returns:
        - client.directions(...) : (list) information
                                          about the journey
    """
    client = Client(api_key)
    return client.directions(
                from_address,
                to_address,
                mode=travelling_mode,
                departure_time=datetime.now()
            )

def get_direction_time(direction, travelling_mode):
    """Get the duration of the journey

    Arguments:
        - direction      : (list) information about the journey
        - travelling_mode: (str) conveyance

    Returns:
        - time : (str) duration of the journey
    """
    time = None
    if travelling_mode == 'driving':
        time = \
            direction[0]['legs'][0]['duration_in_traffic']['text']
    else:
        time = \
            direction[0]['legs'][0]['duration']['text']
    return time

def main():
    api_key = GOOGLE_MAP_API_KEY
    from_address = ''
    to_address = ''
    travelling_mode = ''
    message = '\nPlease select your travelling mode: \n'
    message += '(driving, walking, bicycling): '

    if not api_key:
        api_key = getpass('Enter your API Key: ')
    while not from_address:
        from_address = input('\nBeginning of the journey: ')
    while not to_address:
        to_address = input('\nArrival of the journey: ')
    while travelling_mode not in ['bicycling', 'driving', 'walking']:
        travelling_mode = input(message)

    direction = get_direction(
                    api_key,
                    from_address,
                    to_address,
                    travelling_mode
                )

    time_to_destination = get_direction_time(direction, travelling_mode)

    print("\nIt will take %s to travel from %s to %s."
          % (time_to_destination, from_address, to_address))

if __name__ == '__main__':
    main()