#!/usr/bin/env python3

import os
import sys
import csv
import glob
import time
import serial
import qdarkstyle
import numpy as np
import subprocess as sp

from PySide2.QtWidgets import *
from PySide2 import QtGui
from PySide2 import QtCore
from image import ImageWindow, CenterPointWindow, FocusWindow
from SimpleFNN import FNN
import torch
import gphoto2 as gp

class MainWindow(QWidget):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        # Set a few fonts to be used
        self.small_text = QtGui.QFont()
        self.small_text.setFamily('Helvetica')
        self.small_text.setPointSize(15)

        self.large_text = QtGui.QFont()
        self.large_text.setFamily('Helvetica')
        self.large_text.setPointSize(30)

        # Group section counter
        self.section_count = 0
        self.section_start = 0
        self.started_sects = False

        # Set trackers for what sections have been taken
        self.section_a = False
        self.section_b = False
        self.section_c = False

        # Indicator for if the image has to be taken again
        self.retake = False

        # Image counter
        self.image_count = None

        self.trim_first = True
        self.sect_first = True

        self.model = self.init_cnn_model()

        # Create the arduino connection
        # Try all three USB ports
        # global arduino
        # try:
        #     arduino = serial.Serial('/dev/ttyACM0', 9600)     #dev/cu.usbmodem14201
        # except IOError:
        #     try:
        #         arduino = serial.Serial('/dev/cu.usbmodem14301', 9600)
        #     except IOError:
        #         try:
        #             arduino = serial.Serial('/dev/cu.usbmodem14401', 9600)
        #         except IOError:
        #             print("You don't have the right arduino port.")
        #             sys.exit()
        # Set main window properties
        self.setGeometry(300, 300, 1200, 800)
        self.setWindowTitle('Slice-n-Snap')

        # Call the functions for initialzing the layouts
        directoryLayout = self._initDirsLayout()
        tableLayout = self._initTableLayout()
        hitLayout = self._initHitLayout()

        # Add the initialized layouts to the main layout
        mainLayout = QGridLayout()
        mainLayout.addLayout(directoryLayout, 0, 0)
        mainLayout.addLayout(tableLayout, 1, 0)
        mainLayout.addLayout(hitLayout, 2, 0)

        # Set the layout
        self.setLayout(mainLayout)

    def selectDirectory(self):
        '''Function for selecting an existing directory'''
        return QFileDialog.getExistingDirectory(self)

    def image_dir_buttonClick(self):
        # Have the user select the directory for the images
        self.image_dir = self.selectDirectory()
        # Update the label with the directory they chose
        self.image_dir_label.setText(self.image_dir + '/')
        # Disable the button after the directory has been chosen
        self.image_dir_button.setDisabled(True)
        # Start monitoring the directory
        # self._startWatcher()

        # Do I need to have a file list on this object?
        # if not self.backup_dir_button.isEnabled():
            # self.main_table.setDisabled(False)
            # self.hit_button.setDisabled(False)
            # self.iso_button_surface.setDisabled(False)
            # self.fstop_button_surface.setDisabled(False)
            # self.ss_button_surface.setDisabled(False)
            # msg = "Everything Nominal"
            # self.main_label.setText(msg)
        self.backup_dir_button.setEnabled(True)
        # Populate the table
        self.populateTable()
            # self.calibrationImage()

    def backup_dir_buttonClick(self):
        # Have the user select the directory
        self.backup_dir = self.selectDirectory()
        # Update the label with the directory they chose
        self.backup_dir_label.setText(self.backup_dir + '/')
        # Disable the button so they can't change the directory
        self.backup_dir_button.setDisabled(True)
        # Need to check that the backup directory is the same as the image directory
        self.backup()

        if not os.path.exists(self.backup_dir + '/csv_files/'):
            os.makedirs(self.backup_dir + '/csv_files/')

        # Only if both directories have been selected do we enable the rest of the GUI
        # if not self.image_dir_button.isEnabled():
        self.main_table.setDisabled(False)
        self.trim_button.setDisabled(False)
        self.sect_button.setDisabled(False)
        self.iso_button_surface.setDisabled(False)
        self.fstop_button_surface.setDisabled(False)
        self.ss_button_surface.setDisabled(False)
        self.iso_button_scatter.setDisabled(False)
        self.fstop_button_scatter.setDisabled(False)
        self.ss_button_scatter.setDisabled(False)
        self.capture_button.setDisabled(False)
        self.main_button.setDisabled(False)
        temp = "Everything Nominal"
        self.main_label.setText(temp)
            # Populate the table
            # self.populateTable()
            # self.calibrationImage()

    def backup(self):
        '''Function for backing up the image directory'''
        # Create a list of the image files is the main and backup dirs
        image_list = glob.glob(self.image_dir + '/*.nef')
        backup_list = glob.glob(self.backup_dir + '/*.nef')

        # Add the JPEGs
        image_list += glob.glob(self.image_dir + '/*.jpg')
        backup_list += glob.glob(self.backup_dir + '/*.jpg')

        # Create a list of CSV files
        csv_main_list = glob.glob(self.image_dir + '/csv_files/*')
        csv_backup_list = glob.glob(self.backup_dir + '/csv_files/*')

        # Remove any duplicates
        image_names = [x.split('/')[-1] for x in image_list]
        backup_names = [x.split('/')[-1] for x in backup_list]

        csv_main_names = [x.split('/')[-1] for x in csv_main_list]
        csv_backup_names = [x.split('/')[-1] for x in csv_backup_list]

        # Make a list of images to copy
        copy_names = [x for x in image_names if x not in backup_names]
        csv_names = [x for x in csv_main_names if x not in csv_backup_names]

        # Copy the images to the backup directoy
        for image in copy_names:
            sp.Popen(["cp", self.image_dir + '/' + image, self.backup_dir])

        for file in csv_names:
            sp.Popen(["cp", self.image_dir + '/csv_files/' +
                      file, self.backup_dir + '/csv_files/'])
        # Do we need to wait here?

    def trim_buttonClick(self):
        print(self.trim_first)
        self.sect_button.setEnabled(False)

        # check if distance is changed if first click
        if (self.trim_first == True):
            self.trim_first = False
            # print(self.main_table.rowCount())
            print(self.total_distance, int(self.main_table.item(self.main_table.rowCount()-1,1).text()))
            if (self.total_distance != int(self.main_table.item(self.main_table.rowCount()-1,1).text())):
                reply = QMessageBox.question(self, 'Total Distance Not Match', 'Are you sure you want to change the total distance?',
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

                if reply == QMessageBox.Yes:
                    self.total_distance = int(self.main_table.item(self.main_table.rowCount()-1,1).text())
                    # temp = "Everything Nominal"
                    # self.main_label.setText(temp)
                else:
                    temp = "Please correct the Distance"
                    self.main_label.setText(temp)
                    self.trim_first = True
                    # self.trim_button.setEnabled(False)
                    # self.sect_button.setEnabled(False)
                    # self.capture_button.setEnabled(False)
                    return
        temp = "Everything Nominal"
        self.main_label.setText(temp)

        self.image_distance = self.image_distance + 10
        self.total_distance = self.total_distance + 10
        self.image_dist_label.setText('Image Distance: ' + str(self.image_distance) + ' μm')
        self.total_dist_label.setText('Total Distance: ' + str(self.total_distance) + ' μm')

        if self.image_distance == 50:
            msg = "Take An Image!"
            self.trim_button.setText(msg)
            self.trim_button.setStyleSheet("color: red")
            self.sect_button.setText(msg)
            self.sect_button.setStyleSheet("color: red")
            self.main_table.setEnabled(False)
            self.trim_button.setEnabled(False)
            self.sect_button.setEnabled(False)

    def sect_buttonClick(self):

        print(self.sect_first)
        self.trim_button.setEnabled(False)

        # check if distance is changed if first click
        if (self.sect_first == True):
            self.sect_first = False
            # print(self.main_table.rowCount())
            print(self.total_distance, int(self.main_table.item(self.main_table.rowCount()-1,1).text()))
            if (self.total_distance != int(self.main_table.item(self.main_table.rowCount()-1,1).text())):
                reply = QMessageBox.question(self, 'Total Distance Not Match', 'Are you sure you want to change the total distance?',
                    QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

                if reply == QMessageBox.Yes:
                    self.total_distance = int(self.main_table.item(self.main_table.rowCount()-1,1).text())
                    # print('Total distanc changed')
                else:
                    temp = "Please correct the Distance"
                    self.main_label.setText(temp)
                    self.sect_first = True
                    # self.trim_button.setEnabled(False)
                    # self.sect_button.setEnabled(False)
                    # self.capture_button.setEnabled(False)
                    return
        temp = "Everything Nominal"
        self.main_label.setText(temp)

        self.image_distance = self.image_distance + 5
        self.total_distance = self.total_distance + 5
        self.image_dist_label.setText('Image Distance: ' + str(self.image_distance) + ' μm')
        self.total_dist_label.setText('Total Distance: ' + str(self.total_distance) + ' μm')
        self.sect_first = False

        if self.image_distance == 50:
            msg = "Take An Image!"
            self.trim_button.setText(msg)
            self.trim_button.setStyleSheet("color: red")
            self.sect_button.setText(msg)
            self.sect_button.setStyleSheet("color: red")
            self.main_table.setEnabled(False)
            self.trim_button.setEnabled(False)
            self.sect_button.setEnabled(False)

    def populateTable(self):

        # Check for CSV files
        self.csv_list = sorted(glob.glob(self.image_dir + '/csv_files/*.csv'))

        # If the directory is empty then make a folder
        if self.csv_list == []:
            if not os.path.exists(self.image_dir + '/csv_files/'):
                try:
                    os.makedirs(self.image_dir + '/csv_files/')
                except FileExistsError:
                    pass
            # Take away all of the rows
            self.main_table.setRowCount(0)
            self.main_table.setColumnCount(7)

            # initiate the table_list as empty
            self.table_list = []

        else:
            # Open the last saved csv file
            with open(self.csv_list[-1], 'r') as f:
                reader = csv.reader(f, delimiter=',')
                self.table_list = []
                for row in reader:
                    self.table_list.append(row)

            # Get the shape of the table and set the table to reflect that
            shape = np.shape(self.table_list)
            self.main_table.setRowCount(shape[0])
            self.main_table.setColumnCount(shape[1])

            # Update the total_distance
            self.total_distance = int(self.table_list[-1][1])
            self.total_dist_label.setText('Total Distance: ' + str(self.total_distance) + ' μm')

            # Try and get the most recent secntion counter
            section_list = []
            total_distance = []
            for i in range(0, len(self.table_list)):
                if self.table_list[i][2] == 'None':
                    pass
                else:
                    section_list += [self.table_list[i][2]]
                    total_distance += [self.table_list[i][1]]

            if section_list == []:
                self.section_count = 0
                self.section_start = 0
            else:
                self.started_sects = True
                # Format: *a, *b, *c. There is no guarantee the sectioning is less than 10
                last_section_number = int(section_list[-1].split(';')[0][:-1])        # Modified, remove the last letter.
                self.section_start = int(total_distance[0]) - 50
                # print(self.total_distance - self.section_start, self.started_sects)
                # print(last_section_number)
                # quit()
                # After each load, check if need message sectioning
                if (self.total_distance - self.section_start) % 250 == 0:
                    msg = "You should be taking sections"
                    self.main_label.setText(msg)

            # Iterate through the items in the list and set the appropriate table items
            for i, row in enumerate(self.table_list):
                for j, col in enumerate(row):
                    item = QTableWidgetItem(col)
                    self.main_table.setItem(i, j, item)

    def save(self):
        path = self.image_dir + '/csv_files/'
        # Append the image name to the csv - will make it different
        # name = 'csv_depth_{0}_'.format(str(self.total_distance).zfill(4))

        if (type(self.table_list[-1][0]) == type("string")):
            table_list_1_name = str(self.table_list[-1][0][2:18])

        else:
            table_list_1_name = str(self.table_list[-1][0][0])

        name = "csv_" + table_list_1_name + ".csv"
        print(name)

        # Each time we call save(), using the data in main_table. time ok?
        save_table_list = self.table_list.copy()

        for i, row in enumerate(self.table_list):
            for j, col in enumerate(row):
                save_table_list[i][j] = self.main_table.item(i,j).text()

        with open(path + name, mode='w') as f:
            writer = csv.writer(f, delimiter=',')
            for row in save_table_list:
                writer.writerow(row)

        # Backup to make sure the CSV file gets copied
        self.backup()

    def closeEvent(self, event):
        self.save()
        event.accept()
        # reply = QMessageBox.question(self, 'Window Close', 'Are you sure you want to close the window?',
        #         QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        # if reply == QMessageBox.Yes:
        #     event.accept()
        #     print('Window closed')
        # else:
        #     event.ignore()

    # def save_amendment_and_exit(self):
    #     # change & save before capture
    #     # print(self.table_list[-1][0], type(self.table_list[-1][0]))
    #     if (type(self.table_list[-1][0] == type("string"))):
    #         table_list_1_name = []
    #         table_list_1_name.append(self.table_list[-1][0][2:18])
    #         # print(table_list_1_name)
    #     else:
    #         table_list_1_name = self.table_list[-1][0]

    #     for i, row in enumerate(self.table_list):
    #         for j, col in enumerate(row):
    #             self.table_list[i][j] = self.main_table.item(i,j).text()

    #     # check here if the talbe_list is a string

    #     self.table_list[-1][0] = table_list_1_name
    #     # print(self.table_list[-1][0])
    #     # print(self.table_list[-1][0][0])
    #     self.save()

    #     self.close()
    #     self.destroy()

    # Open a window for user to provide the center info of the tissure
    def centerCapture(self):
        self.setEnabled(False)
        self.center_window = CenterPointWindow(self.image_dir, self.backup_dir, self)

        self.center_window.show()
        app.processEvents()
        self.center_window.getCenter()
        self.backup()

        # wait till accept the center point.
        self.center_window.loop.exec_()

    def imageWindowLauncher(self):

        # Need a catch in here to only call once

        self.setEnabled(False)
        self.image_window = ImageWindow(self.image_dir, self.backup_dir, self)
        self.image_window.show()
        app.processEvents()
        self.image_window.loadImage()
        self.backup()

    def _getImageName(self):

        if self.image_count == None:
            image_list = sorted(glob.glob(self.image_dir + '/*.nef'))
            if not image_list:
                self.image_count = 1
            else:
                self.image_count = int(image_list[-1].split('_')[-2])
                self.image_count += 1
        # else:
        #     #Determine what number we are on
        #     # image_list = sorted(glob.glob(self.image_dir + '/*.NEF'))
        #     self.image_count = int(image_list[-1].split('_')[-2])
        #     self.image_count += 1

        # Now create a new name
        self.surface_path = f"IMG_{self.image_count:04}_surface"
        self.scatter_path = f"IMG_{self.image_count:04}_scatter"

        self.image_count += 1

    def center_point_txt(self):
        try:
            self.center_point_f = open(self.image_dir + '/csv_files/center_point.txt', 'r')
            self.position_list = self.center_point_f.readline().split(',')
            self.position = [np.float(self.position_list[0]), np.float(self.position_list[1])]
            self.cropped_size = [np.float(self.position_list[2]), np.float(self.position_list[3])]
            self.block_size_rescale = np.float(self.position_list[4])
        except FileNotFoundError:
            print("'center_point.txt' not exist, creating...")
            try:
                self.center_point_f = open(self.image_dir + '/csv_files/center_point.txt', 'a+')
            except FileExistsError:
                print("Fail to create {}, try to configure manually!".format(self.image_dir + '/csv_files/center_point.txt'))
                quit()

        if(int(self.scatter_path.split("_")[1]) != 1 and self.position==""):
            print("Missing center_point!")
            quit()

    def init_cnn_model(self):
        model_path = '/home2/tester/Desktop/recordingGui-master/best_model.pth'
        patch_size = 1
        n_input = 3 * ((2 * patch_size + 1) ** 2)
        n_out = 3
        n_hidden_layers = 1
        npl = 32
        model = FNN(n_input, n_out, n_hidden_layers, npl)
        model.load(model_path)
        model = model.cuda()
        return model

    def changeSurfaceSS(self):
        self.ss_drop_surface.setDisabled(False)

    def changedSurfaceSS(self):
        self.ss_drop_surface.setDisabled(True)

    def changeSurfaceISO(self):
        self.iso_drop_surface.setDisabled(False)

    def changedSurfaceISO(self):
        self.iso_drop_surface.setDisabled(True)

    def changeSurfaceFStop(self):
        self.fstop_drop_surface.setDisabled(False)

    def changedSurfaceFStop(self):
        self.fstop_drop_surface.setDisabled(True)

    def changeScatterSS(self):
        self.ss_drop_scatter.setDisabled(False)

    def changedScatterSS(self):
        self.ss_drop_scatter.setDisabled(True)

    def changeScatterISO(self):
        self.iso_drop_scatter.setDisabled(False)

    def changedScatterISO(self):
        self.iso_drop_scatter.setDisabled(True)

    def changeScatterFStop(self):
        self.fstop_drop_scatter.setDisabled(False)

    def save_info(self):
        try:
            camera_info = open('/home2/tester/Desktop/recordingGui-master/camera_info.txt', 'w')
            camera_info.write(str(self.ss_drop_surface.currentText())+' ')
            camera_info.write(str(self.ss_drop_scatter.currentText())+' ')
            camera_info.write(str(self.fstop_drop_surface.currentText())+' ')
            camera_info.write(str(self.fstop_drop_scatter.currentText())+' ')
            camera_info.write(str(self.iso_drop_surface.currentText())+' ')
            camera_info.write(str(self.iso_drop_scatter.currentText()))
            # camera_info.write(str(self.block_size_drop.currentText()))

            camera_info.close()
        except FileExistsError:
            print("Fail to create 'camera_info.txt'!")

    def changedScatterFStop(self):
        self.fstop_drop_scatter.setDisabled(True)

    # def _updateCapture(self):
    #     self.capture_button.setText("Capturing ...")
    #     self.capture_button.setFont(self.large_text)

    def capture_buttonClick(self):

        self.trim_first = True
        self.sect_first = True

        # Turn off both lights
        arduino.write(b'c')
        # time.sleep(0.1)

        # Get the image names
        self._getImageName()
        # self._updateCapture()

        self.center_point_txt()

        surface_iso = self.iso_drop_surface.currentText()
        surface_ss = self.ss_drop_surface.currentText()
        surface_fstop = self.fstop_drop_surface.currentText()

        # scatter_iso = self.iso_drop_scatter.currentIndex()
        # scatter_ss = self.ss_drop_scatter.currentIndex()
        # scatter_fstop = self.fstop_drop_scatter.currentIndex()

        scatter_iso = self.iso_drop_scatter.currentText()
        scatter_ss = self.ss_drop_scatter.currentText()
        scatter_fstop = self.fstop_drop_scatter.currentText()

        # Take the surface images
        ## Command to turn on light a
        # time_A = time.time()

        arduino.write(b'a')
        time.sleep(0.1)
        print('image 1: ')

        context = gp.Context()
        camera = gp.Camera()
        camera.init(context)

        # config_tree = camera.get_config(context)
        # print('=======')

        # quality = config_tree.get_child_by_name('imagequality')
        # for k in range(quality.count_choices()):
        #     choice = quality.get_choice(k)
        #     text_choice = '         - ' + choice
        #     print(text_choice)

        # total_child = config_tree.count_children()
        # for i in range(total_child):
        #     child = config_tree.get_child(i)
        #     text_child = '# ' + child.get_label() + ' ' + child.get_name()
        #     print(text_child)

        #     for a in range(child.count_children()):
        #         grandchild = child.get_child(a)
        #         text_grandchild = '    * ' + grandchild.get_label() + ' -- ' + grandchild.get_name()
        #         print(text_grandchild)

        #         try:
        #             text_grandchild_value = '        Setted: ' + grandchild.get_value()
        #             print(text_grandchild_value)
        #             # print('        Possibilities:')
        #             # for k in range(grandchild.count_choices()):
        #             #     choice = grandchild.get_choice(k)
        #             #     text_choice = '         - ' + choice
        #             #     print(text_choice)
        #         except:
        #             pass
        #         print()
        #     print()

        print('Capturing image')
        # # print(gp.GP_CAPTURE_IMAGE)
        config = camera.get_config(context)

        print(surface_fstop, surface_ss, surface_iso)

        capture_target_f = config.get_child_by_name('f-number')
        capture_target_f.set_value(surface_fstop)

        print(config.get_child_by_name('f-number').get_value())
        # # quit()
        # gp.check_result(gp.gp_widget_get_child_by_name(config, 'f-number'))        #    shutterspeed        iso
        # gp.gp_widget_set_value(capture_target_f, str(surface_fstop) )
        # # # print(capture_target_f.set_value('f/8'))

        capture_target_s = config.get_child_by_name('shutterspeed')
        capture_target_s.set_value(surface_ss)
        print(config.get_child_by_name('shutterspeed').get_value())

        capture_target_i = config.get_child_by_name('iso')
        capture_target_i.set_value(surface_iso)

        # # file_path = camera.capture(gp.GP_CAPTURE_IMAGE)

        camera.set_config(config)
        file_path = camera.trigger_capture()
        cnt = 0
        while cnt<2:
            event_type, event_data = camera.wait_for_event(10)
            if (event_type == gp.GP_EVENT_FILE_ADDED):
                cnt += 1
                print('Camera file path: {0}/{1}'.format(event_data.folder, event_data.name))
                target = os.path.join(self.image_dir, self.surface_path + '.' + event_data.name.split('.')[-1])

                print('Copying image to', target)
                cam_file = camera.file_get(event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)

                print(cam_file)
                cam_file.save(target)

        # # sp.call(['xdg-open', target])
        # # camera.exit()
        # print("finished")
        # quit()

        # camera = gp.check_result(gp.gp_camera_new())
        # gp.check_result(gp.gp_camera_init(camera))
        # get configuration tree

        # print(gp.gp_widget_count_children(config))

        # p = sp.Popen(["gphoto2",
        #               "--set-config-index", f"iso={surface_iso}",
        #               "--set-config-index", f"shutterspeed={surface_ss}",
        #               "--set-config-index", f"f-number={surface_fstop}",
        #               f"--filename={self.surface_path + '.%C'}",
        #               "--set-config", "viewfinder=1",
        #               # "--wait-event=0.5s",
        #               "--capture-image-and-download"],
        #               stdout=sp.PIPE, cwd=f"{self.image_dir}/")
        # print("image1 mid")
        # sout, _ = p.communicate()
        # p.wait()
        print("image1 finished")

        arduino.write(b'b')
        time.sleep(0.1)
        # print('image 2: ' + scatter_iso)

        print('Capturing scatter image')

        print(scatter_fstop, scatter_ss, scatter_iso)

        capture_target_f.set_value(scatter_fstop)
        capture_target_s.set_value(scatter_ss)
        capture_target_i.set_value(scatter_iso)

        camera.set_config(config)
        file_path = camera.trigger_capture()
        cnt = 0
        while cnt<2:
            event_type, event_data = camera.wait_for_event(10)
            if (event_type == gp.GP_EVENT_FILE_ADDED):
                cnt += 1
                print('Camera file path: {0}/{1}'.format(event_data.folder, event_data.name))
                target = os.path.join(self.image_dir, self.scatter_path + '.' + event_data.name.split('.')[-1])

                print('Copying image to', target)
                cam_file = camera.file_get(event_data.folder, event_data.name, gp.GP_FILE_TYPE_NORMAL)

                print(cam_file)
                cam_file.save(target)
        camera.exit(context)

        # p = sp.Popen(["gphoto2",
        #               "--set-config-index", f"iso={scatter_iso}",
        #               "--set-config-index", f"shutterspeed={scatter_ss}",
        #               "--set-config-index", f"f-number={scatter_fstop}",
        #               f"--filename={self.scatter_path + '.%C'}",
        #               "--set-config", "viewfinder=1",
        #               # "--wait-event=0.5s",
        #               "--capture-image-and-download"],
        #               stdout=sp.PIPE, cwd=f"{self.image_dir}/")
        # sout, _ = p.communicate()
        # p.wait()

        print("finished")

        # Turn off both lights
        arduino.write(b'c')
        # self.capture_button.setText("Capture Image")
        # self.capture_button.setFont(self.large_text)

        # time_B = time.time()
        # print("time cost: ", time_B-time_A)
        #  # time cost:  8.700865745544434
        #  # time cost:  15.92504096031189

        # quit()

        ######## If 1st capture, collect the center point #######
        # print(self.surface_path)
        if(int(self.scatter_path.split("_")[1]) == 1):
            self.centerCapture()

        self.center_point_f.close()

        self.imageWindowLauncher()

        self.backup()

    def main_button_clicked(self):
        self.section_a = False
        self.section_b = False
        self.section_c = False
        self.section_count += 1

    def _initTableLayout(self):
        '''Initalize the table layout'''
        # Initalize the table assuming no CSV file
        self.main_table = QTableWidget(10,7)
        self.columnLabels = ["File Name","Distance","Sections",
                             "Shutter Speed", "ISO", "Aperature", "Notes"]
        self.main_table.setHorizontalHeaderLabels(self.columnLabels)
        # self.save_and_exit = QPushButton("save & exit")

        # Set buttons for the camera settings for the surface image
        surface_label = QLabel("Surface Image Settings")
        self.iso_button_surface = QPushButton("ISO")
        self.fstop_button_surface = QPushButton("FStop")
        self.ss_button_surface = QPushButton("SS")

        # Set buttons for the camera settings for the scatter image
        scatter_label = QLabel("Scatter Image Settings")
        self.iso_button_scatter = QPushButton("ISO")
        self.fstop_button_scatter = QPushButton("FStop")
        self.ss_button_scatter = QPushButton("SS")

        self.save_info_button = QPushButton("Save")

        # self.save_and_exit.clicked.connect(self.save_amendment_and_exit)

        # Connect the buttons for the surface image
        self.iso_button_surface.clicked.connect(self.changeSurfaceISO)
        self.fstop_button_surface.clicked.connect(self.changeSurfaceFStop)
        self.ss_button_surface.clicked.connect(self.changeSurfaceSS)

        # Connect the buttons for the scatter image
        self.iso_button_scatter.clicked.connect(self.changeScatterISO)
        self.fstop_button_scatter.clicked.connect(self.changeScatterFStop)
        self.ss_button_scatter.clicked.connect(self.changeScatterSS)
        self.save_info_button.clicked.connect(self.save_info)

        # Set the line edits for the settings
        self.ss_values = ['0.0001s', '0.0002s', '0.0003s', '0.0004s', '0.0005s', '0.0006s', '0.0008s', '0.0010s',
                          '0.0012s', '0.0015s', '0.0020s', '0.0025s', '0.0031s', '0.0040s', '0.0050s', '0.0062s',
                          '0.0080s', '0.0100s', '0.0125s', '0.0166s', '0.0200s', '0.0250s', '0.0333s', '0.0400s',
                          '0.0500s', '0.0666s', '0.0769s', '0.1000s', '0.1250s', '0.1666s', '0.2000s', '0.2500s',
                          '0.3333s', '0.4000s', '0.5000s', '0.6250s', '0.7692s', '1.0000s', '1.3000s', '1.6000s',
                          '2.0000s', '2.5000s', '3.0000s', '4.0000s', '5.0000s', '6.0000s', '8.0000s', '10.0000s',
                          '13.0000s', '15.0000s', '20.0000s', '25.0000s', '30.0000s']
        self.ss_drop_surface = QComboBox()
        self.ss_drop_surface.addItems(self.ss_values)
        # self.ss_drop_surface.setCurrentIndex(self.ss_values.index('0.0250s'))
        # self.ss_drop_surface.currentIndexChanged.connect(self.changedSurfaceSS)

        self.ss_drop_scatter = QComboBox()
        self.ss_drop_scatter.addItems(self.ss_values)
        # self.ss_drop_scatter.setCurrentIndex(self.ss_values.index('0.0031s'))
        # self.ss_drop_scatter.currentIndexChanged.connect(self.changedScatterSS)

        self.fstop_values = ['f/3', 'f/3.2', 'f/3.5', 'f/4', 'f/4.5', 'f/5', 'f/5.6', 'f/6.3', 'f/7.1', 'f/8', 'f/9',
                             'f/10', 'f/11', 'f/13', 'f/14', 'f/16', 'f/18', 'f/20', 'f/22', 'f/25', 'f/29', 'f/32']

        self.fstop_drop_surface = QComboBox()
        self.fstop_drop_surface.addItems(self.fstop_values)
        # self.fstop_drop_surface.setCurrentIndex(self.fstop_values.index('f/8'))
        # self.fstop_drop_surface.currentIndexChanged.connect(self.changedSurfaceFStop)

        self.fstop_drop_scatter = QComboBox()
        self.fstop_drop_scatter.addItems(self.fstop_values)
        # self.fstop_drop_scatter.setCurrentIndex(self.fstop_values.index('f/8'))
        # self.fstop_drop_scatter.currentIndexChanged.connect(self.changedScatterFStop)

        self.iso_values = ['100', '125', '160', '200', '250', '320', '400', '500', '640', '800', '1000', '1250',
        				   '1600', '2000', '2500', '3200', '4000', '5000', '6400', '8000', '10000', '12800', '25600']

        self.iso_drop_surface = QComboBox()
        self.iso_drop_surface.addItems(self.iso_values)
        # self.iso_drop_surface.setCurrentIndex(self.iso_values.index('100'))
        # self.iso_drop_surface.currentIndexChanged.connect(self.changedSurfaceISO)

        self.iso_drop_scatter = QComboBox()
        self.iso_drop_scatter.addItems(self.iso_values)
        # self.iso_drop_scatter.setCurrentIndex(self.iso_values.index('100'))
        # self.iso_drop_scatter.currentIndexChanged.connect(self.changedScatterISO)

        # Getting previous saved camera info
        # ss_value_surface ss_value_scatter fstop_value_surface fstop_value_scatter iso_value_surface iso_value_scatter
        try:
            camera_info = open('/home2/tester/Desktop/recordingGui-master/camera_info.txt', 'r')
            camera_info_list = camera_info.readline().split(' ')

            self.ss_drop_surface.setCurrentIndex(self.ss_values.index(camera_info_list[0]))
            self.ss_drop_surface.currentIndexChanged.connect(self.changedSurfaceSS)

            self.ss_drop_scatter.setCurrentIndex(self.ss_values.index(camera_info_list[1]))
            self.ss_drop_scatter.currentIndexChanged.connect(self.changedScatterSS)

            self.fstop_drop_surface.setCurrentIndex(self.fstop_values.index(camera_info_list[2]))
            self.fstop_drop_surface.currentIndexChanged.connect(self.changedSurfaceFStop)

            self.fstop_drop_scatter.setCurrentIndex(self.fstop_values.index(camera_info_list[3]))
            self.fstop_drop_scatter.currentIndexChanged.connect(self.changedScatterFStop)

            self.iso_drop_surface.setCurrentIndex(self.iso_values.index(camera_info_list[4]))
            self.iso_drop_surface.currentIndexChanged.connect(self.changedSurfaceISO)

            self.iso_drop_scatter.setCurrentIndex(self.iso_values.index(camera_info_list[5]))
            self.iso_drop_scatter.currentIndexChanged.connect(self.changedScatterISO)

            camera_info.close()

        except FileNotFoundError:
            print("'camera_info.txt' not exist, using default value...")
            self.ss_drop_surface.setCurrentIndex(self.ss_values.index('0.0250s'))
            self.ss_drop_surface.currentIndexChanged.connect(self.changedSurfaceSS)

            self.ss_drop_scatter.setCurrentIndex(self.ss_values.index('0.0031s'))
            self.ss_drop_scatter.currentIndexChanged.connect(self.changedScatterSS)

            self.fstop_drop_surface.setCurrentIndex(self.fstop_values.index('f/8'))
            self.fstop_drop_surface.currentIndexChanged.connect(self.changedSurfaceFStop)

            self.fstop_drop_scatter.setCurrentIndex(self.fstop_values.index('f/8'))
            self.fstop_drop_scatter.currentIndexChanged.connect(self.changedScatterFStop)

            self.iso_drop_surface.setCurrentIndex(self.iso_values.index('100'))
            self.iso_drop_surface.currentIndexChanged.connect(self.changedSurfaceISO)

            self.iso_drop_scatter.setCurrentIndex(self.iso_values.index('100'))
            self.iso_drop_scatter.currentIndexChanged.connect(self.changedScatterISO)

        # Set the font for the buttons
        self.iso_button_surface.setFont(self.small_text)
        self.fstop_button_surface.setFont(self.small_text)
        self.ss_button_surface.setFont(self.small_text)

        self.iso_button_scatter.setFont(self.small_text)
        self.fstop_button_scatter.setFont(self.small_text)
        self.ss_button_scatter.setFont(self.small_text)

        # Initially disable the table until the directories have been chosen
        self.main_table.setDisabled(True)
        self.iso_button_surface.setDisabled(True)
        self.fstop_button_surface.setDisabled(True)
        self.ss_button_surface.setDisabled(True)

        self.iso_button_scatter.setDisabled(True)
        self.fstop_button_scatter.setDisabled(True)
        self.ss_button_scatter.setDisabled(True)

        # make the table expand over the button
        self.main_table.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)

        # Center the lables
        scatter_label.setAlignment(QtCore.Qt.AlignCenter)
        surface_label.setAlignment(QtCore.Qt.AlignCenter)

        self.iso_drop_surface.setDisabled(True)
        self.fstop_drop_surface.setDisabled(True)
        self.ss_drop_surface.setDisabled(True)

        self.iso_drop_scatter.setDisabled(True)
        self.fstop_drop_scatter.setDisabled(True)
        self.ss_drop_scatter.setDisabled(True)

        header = self.main_table.horizontalHeader()

        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setSectionResizeMode(6, QHeaderView.Stretch)
        # header.setSectionResizeMode(2, QHeaderView.ResizeToContents)

        settings_layout = QGridLayout()
        settings_layout.addWidget(surface_label, 0, 0, 1, 2)
        settings_layout.addWidget(self.iso_button_surface, 1, 0)
        settings_layout.addWidget(self.fstop_button_surface, 2, 0)
        settings_layout.addWidget(self.ss_button_surface, 3, 0)
        settings_layout.addWidget(self.iso_drop_surface, 1, 1)
        settings_layout.addWidget(self.fstop_drop_surface, 2, 1)
        settings_layout.addWidget(self.ss_drop_surface, 3, 1)
        settings_layout.addWidget(scatter_label, 4, 0, 1, 2)
        settings_layout.addWidget(self.iso_button_scatter, 5, 0)
        settings_layout.addWidget(self.fstop_button_scatter, 6, 0)
        settings_layout.addWidget(self.ss_button_scatter, 7, 0)
        settings_layout.addWidget(self.iso_drop_scatter, 5, 1)
        settings_layout.addWidget(self.fstop_drop_scatter, 6, 1)
        settings_layout.addWidget(self.ss_drop_scatter, 7, 1)
        settings_layout.addWidget(self.save_info_button, 8, 0, 2, 1)
        settings_layout.setColumnStretch(0, 10)
        settings_layout.setColumnStretch(1, 30)
        # settings_layout.setColumnStretch(2, 10)

        # settings_layout.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Preferred)
        # Make the talbe have its own layout
        # Not sure this is necessary as the table isn't that complicated
        table_layout = QGridLayout()
        table_layout.addWidget(self.main_table, 0, 0)
        # table_layout.addWidget(self.save_and_exit, 1, 0)

        layout = QGridLayout()
        layout.addLayout(table_layout, 0, 0)
        layout.addLayout(settings_layout, 0, 1)
        layout.setColumnStretch(0, 50)
        layout.setColumnStretch(1, 10)

        return layout

    def _initDirsLayout(self):

        # Add the directory buttons
        self.image_dir_button =  QPushButton("Select Image Folder")
        self.backup_dir_button =  QPushButton("Select Backup Folder")
        self.capture_button = QPushButton("Capture Images")

        # Add the directory labels
        self.image_dir_label = QLabel()
        self.backup_dir_label = QLabel()
        self.image_dist_label = QLabel()
        self.main_label = QLabel()
        self.main_button = QPushButton("Reset Sections")

        # Change the font of all the lables and buttons
        self.image_dir_label.setFont(self.small_text)
        self.backup_dir_label.setFont(self.small_text)
        self.image_dist_label.setFont(self.small_text)
        self.main_label.setFont(self.small_text)
        self.main_button.setFont(self.small_text)
        self.image_dir_button.setFont(self.small_text)
        self.backup_dir_button.setFont(self.small_text)
        self.capture_button.setFont(self.large_text)

        # Set the size policy for the label so it expands vertically
        self.main_label.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)
        self.main_button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.capture_button.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)

        # Initalize the messages for the main label and the directories
        msg = "Please select the Image and Backup Directories"
        self.image_dir = "/Path/to/image/directoy/"
        self.backup_dir = "/Path/to/backup/directoy/"
        self.main_message = msg

        # Set the labels with the inital messages
        self.image_dir_label.setText(self.image_dir)
        self.backup_dir_label.setText(self.backup_dir)
        self.main_label.setText(self.main_message)

        # Set the alignment of the main text
        self.main_label.setAlignment(QtCore.Qt.AlignCenter)
        self.main_label.setFont(self.large_text)

        # Connect the buttons to their functions
        self.image_dir_button.clicked.connect(self.image_dir_buttonClick)
        self.backup_dir_button.clicked.connect(self.backup_dir_buttonClick)
        self.capture_button.clicked.connect(self.capture_buttonClick)
        self.main_button.clicked.connect(self.main_button_clicked)

        # Diable the backup directory button at first
        self.backup_dir_button.setEnabled(False)
        self.capture_button.setEnabled(False)
        self.main_button.setDisabled(True)

        # Define the layout for the main window
        layout = QGridLayout()
        layout.setColumnStretch(0, 0)
        layout.setColumnStretch(1, 1)
        layout.setColumnStretch(2, 1)
        layout.addWidget(self.image_dir_button, 0, 0)
        layout.addWidget(self.image_dir_label, 0, 1)
        layout.addWidget(self.backup_dir_button, 1, 0)
        layout.addWidget(self.backup_dir_label, 1, 1)
        layout.addWidget(self.capture_button, 0, 2, 2, 1)
        layout.addWidget(self.main_label, 2, 1, 1, 2)
        layout.addWidget(self.main_button, 2, 0, 1, 1)

        return layout

    def _initHitLayout(self):

        # Make the hit button and the labels for the section distances
        self.trim_button = QPushButton("TRIM (10 μm)")
        self.sect_button = QPushButton("SECTION (5 μm)")

        self.image_dist_label = QLabel()
        self.total_dist_label = QLabel()

        # Set the font for the labels and button
        self.image_dist_label.setFont(self.large_text)
        self.total_dist_label.setFont(self.large_text)
        self.trim_button.setFont(self.large_text)
        self.sect_button.setFont(self.large_text)

        # Set the size policies for the buttons and labels
        self.trim_button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.sect_button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
        self.image_dist_label.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)
        self.total_dist_label.setSizePolicy(QSizePolicy.Preferred,QSizePolicy.Expanding)

        # Initalize the distances
        self.image_distance = 0
        self.total_distance = 0

        # Make the labels for the distances
        # Keep the number separate from the text so they can easily be used elsewhere
        self.image_dist_label.setText('Image Distance : ' + str(self.image_distance) + ' μm')
        self.total_dist_label.setText('Total Distance : ' + str(self.total_distance) + ' μm')

        # Connect the hit button
        self.trim_button.clicked.connect(self.trim_buttonClick)
        self.sect_button.clicked.connect(self.sect_buttonClick)

        # Disable the hit button initially
        self.trim_button.setDisabled(True)
        self.sect_button.setDisabled(True)

        # Create the layout
        layout = QGridLayout()
        layout.addWidget(self.trim_button, 0, 0, 1, 2)
        layout.addWidget(self.sect_button, 1, 0, 1, 2)

        # layout.addWidget(self.trim_button, 0, 1, 1, 1)
        # layout.addWidget(self.sect_button, 1, 1, 1, 1)

        layout.addWidget(self.image_dist_label, 0, 2, 1, 2)
        layout.addWidget(self.total_dist_label, 1, 2, 1, 2)
        layout.setColumnStretch(0, 20)
        layout.setColumnStretch(1, 15)

        return layout

    def errorMessage(self, error):
       msg = QMessageBox()
       msg.setIcon(QMessageBox.Critical)
       msg.setText(error)
       # msg.setInformativeText("This is additional information")
       msg.setWindowTitle("ERROR")
       # msg.setDetailedText("The details are as follows:")
       msg.setStandardButtons(QMessageBox.Ok)
       # msg.buttonClicked.connect(msgbtn)

       msg.exec_()
       # print "value of pressed message box button:", retval

if __name__ == '__main__':

    app = QApplication(sys.argv)
    app.setStyleSheet(qdarkstyle.load_stylesheet_pyside2())

    global arduino
    try:
        arduino = serial.Serial('/dev/ttyACM0', 9600)     #dev/cu.usbmodem14201
    except IOError:
        try:
            arduino = serial.Serial('/dev/cu.usbmodem14301', 9600)
        except IOError:
            try:
                arduino = serial.Serial('/dev/cu.usbmodem14401', 9600)
            except IOError:
                print("You don't have the right arduino port.")
                sys.exit()

    window = MainWindow()

    window.show()

    # Focus here
    p = sp.Popen(["/home2/tester/Desktop/recordingGui-master/focus.sh"], shell=True, stdout=sp.PIPE)

    # time.sleep(1)
    # arduino.write(b'a')

    # focus_window = FocusWindow(window)
    # focus_window.show()

    app.exec_()

    # window.watcher.stop()
    # window.thread.quit()
    # window.thread.wait()


import numpy as np
from os import path as osp
from PIL import Image
import torch
from tqdm import tqdm
from multiprocessing import Pool
from functools import partial
import mmcv
from shapely.geometry import LineString
from .mean_ap import average_precision
from .tpfp import tpfp_gen, custom_tpfp_gen, custom_tpfp_gen_1
from sklearn.metrics import precision_score,f1_score,recall_score
from mmcv.utils import print_log
from terminaltables import AsciiTable
import json
import os
from scipy.spatial import cKDTree
from skimage import measure
from scipy.sparse.csgraph import dijkstra
import pickle
import multiprocessing
from functools import partial

def update_graph(start_vertex,end_vertex,graph,set_value=1):
    start_vertex = np.array([int(start_vertex[0]),int(start_vertex[1])])
    end_vertex = np.array([int(end_vertex[0]),int(end_vertex[1])])
    p = start_vertex
    d = end_vertex - start_vertex
    N = np.max(np.abs(d))
    graph[start_vertex[0],start_vertex[1]] = set_value
    graph[end_vertex[0],end_vertex[1]] = set_value
    if N:
        s = d / (N)
        for i in range(0,N):
            p = p + s
            graph[int(round(p[0])),int(round(p[1]))] = set_value
    return graph

def eval_metric(graph, mask, name, gt_data):
    def tuple2list(t):
        return [[t[0][x],t[1][x]] for x in range(len(t[0]))]

    graph = graph.cpu().detach().numpy().squeeze()
    gt_image = mask
    gt_points = tuple2list(np.where(gt_image!=0))
    graph_points = tuple2list(np.where(graph!=0))
    if not len(graph_points):
        return [0, 0, 0], [0, 0, 0], [0, 0, 0], 0, 0, 0, 1
    graph_acc = []
    graph_recall = []
    r_f = []
    apls = 0
    entropy_conn = 0
    naive_conn = 0
    gt_tree = cKDTree(gt_points)
    for c_i, thre in enumerate([2, 5, 10]):
        graph_acc_num = 0
        graph_recall_num = 0
        if len(graph_points):
            graph_tree = cKDTree(graph_points)
            dis_gt2graph,_ = graph_tree.query(gt_points, k=1)
            dis_graph2gt,_ = gt_tree.query(graph_points, k=1)
            graph_recall_num = len([x for x in dis_gt2graph if x<thre])/len(dis_gt2graph)
            graph_acc_num = len([x for x in dis_graph2gt if x<thre])/len(dis_graph2gt)
        if graph_acc_num*graph_recall_num:
            r_f_num = 2*graph_recall_num * graph_acc_num / (graph_acc_num+graph_recall_num)
        else:
            r_f_num = 0
        graph_acc.append(graph_acc_num)
        graph_recall.append(graph_recall_num)
        r_f.append(r_f_num)

    pre_image = graph
    gt_instance_map = measure.label(gt_image / 255,background=0)
    gt_instance_indexes = np.unique(gt_instance_map)[1:]
    gt_assigned_lengths = [[] for x in range(len(gt_instance_indexes))]
    gt_instance_length = []
    gt_instance_points = []
    gt_covered = []
    for index in gt_instance_indexes:
        instance_map = (gt_instance_map == index)
        instance_points = np.where(instance_map==1)
        instance_points = [[instance_points[0][i],instance_points[1][i]] for i in range(len(instance_points[0]))]
        gt_instance_length.append(len(instance_points))
        gt_covered.append(np.zeros((len(instance_points))))
        gt_instance_points.append(instance_points)
    pre_instance_map = measure.label(pre_image, background=0)
    pre_instance_indexes = np.unique(pre_instance_map)[1:]
    gt_points = np.where(gt_image!=0)
    gt_points = [[gt_points[0][i],gt_points[1][i]] for i in range(len(gt_points[0]))]
    tree = cKDTree(gt_points)
    # each pre_index is an predicted instance
    for index in pre_instance_indexes:
        votes = []
        instance_map = (pre_instance_map == index)
        instance_points = np.where(instance_map==1)
        instance_points = [[instance_points[0][i],instance_points[1][i]] for i in range(len(instance_points[0]))]
        if instance_points:
            # Each predicted point of the current pre-instance finds its closest gt point and votes
            # to the gt-instance that the closest gt point belongs to.
            _, iis = tree.query(instance_points,k=[1])
            closest_gt_points = [[gt_points[x[0]][0],gt_points[x[0]][1]] for x in iis]
            votes = [gt_instance_map[x[0],x[1]] for x in closest_gt_points]
        # count the voting results
        votes_summary = np.zeros((len(gt_instance_indexes)))
        for j in range(len(gt_instance_indexes)):
            # the number of votes made to gt-instance j+1
            votes_summary[j] = votes.count(j+1)
        # find the gt-instance winning the most vote and assign the current pre-instance to it
        if np.max(votes_summary):
            vote_result = np.where(votes_summary==np.max(votes_summary))[0][0]
            # the length of the pre-instance assigned to corresponding gt-instance
            gt_assigned_lengths[vote_result].append(len(instance_points))
            # calculate projection of the predicted instance to corresponding gt-instance
            instance_tree = cKDTree(gt_instance_points[vote_result])
            _, iis = instance_tree.query(instance_points,k=[1])
            gt_covered[vote_result][np.min(iis):np.max(iis)+1] = 1

    # calculate ECM
    # iterate all gt-instances, calculate connectivity of each of them
    for j,lengths in enumerate(gt_assigned_lengths):
        # lengths are the length of assigned pre-instances to the current gt-instance
        if len(lengths):
            lengths = np.array(lengths)
            # contribution of each assigned pre-instance
            probs = (lengths / np.sum(lengths)).tolist()
            C_j = 0
            for p in probs:
                C_j += -p*np.log2(p)
            entropy_conn += np.exp(-C_j) * np.sum(gt_covered[j]) / len(gt_points)
            naive_conn += 1 / len(lengths)
    if len(gt_assigned_lengths):
        naive_conn = naive_conn / len(gt_assigned_lengths)

    apls_counter = 0
    pre_data = generate_graph(graph)
    adjacent = pre_data['adj']
    pre_points = pre_data['vertices']
    if len(pre_points):

        tree = cKDTree(pre_points)
        for gt_data_instance in gt_data:
            gt_points_instance = gt_data_instance['vertices']
            gt_adjacent = gt_data_instance['adj']
            if len(gt_points_instance) > 5:
                # randomly find some pairs of points for APLS calculation
                for ii in range(len(gt_points_instance)//3):
                    select_index = np.random.choice(len(gt_points_instance),2,replace=False)
                    init_v = gt_points_instance[select_index[0]]
                    end_v = gt_points_instance[select_index[1]]
                    source = select_index[0]
                    target = select_index[1]
                    dist_matrix = dijkstra(gt_adjacent, directed=False, indices=[source],
                                            unweighted=False)
                    gt_length = dist_matrix[0,target]
                    # find corresponding pre_vertex by min-Euclidean distance
                    dds,iis = tree.query([init_v,end_v],k=1)
                    source = iis[0]
                    target = iis[1]
                    # vertex too far away is treated as not reachable
                    if np.max(dds) < 50:
                        dist_matrix = dijkstra(adjacent, directed=False, indices=[source],
                                            unweighted=False)
                        pre_length = dist_matrix[0,target]
                    else:
                        pre_length = 10000
                    apls += min(1,abs(gt_length - pre_length) / (gt_length))
                    apls_counter += 1

    if apls_counter:
        apls = 1 - apls / apls_counter
    return graph_acc, graph_recall, r_f, entropy_conn, naive_conn, apls, 0

class Vertex():
    def __init__(self,v):
        self.coord = v
        self.index = v[0] * 1000 + v[1]
        self.neighbors = []
        self.unprocessed_neighbors = []
        self.processed_neighbors = []
        self.sampled_neighbors = []
        self.key_vertex = False
    def compare(self,v):
        if self.coord[0] == v[0] and self.coord[1] == v[1]:
            return True
        return False
    def next(self,previous):
        neighbors = self.neighbors.copy()
        neighbors.remove(previous)
        return neighbors[0]
    def distance(self,v):
        return pow(pow(self.coord[0]-v.coord[0],2)+pow(self.coord[1]-v.coord[1],2),0.5)

class Graph():
    def __init__(self):
        self.vertices = []
        self.key_vertices = []
        self.sampled_vertices = []
    def find_vertex(self,index):
        for v in self.vertices:
            if index == v.index:
                return v
        return None
    def add_v(self,v,neighbors):
        self.vertices.append(v)
        for n in neighbors:
            index = n[0] * 1000 + n[1]
            u = self.find_vertex(index)
            if u is not None:
                u.neighbors.append(v)
                v.neighbors.append(u)
                u.unprocessed_neighbors.append(v)
                v.unprocessed_neighbors.append(u)
    def find_key_vertices(self):
        for v in self.vertices:
            if len(v.neighbors)!=2:
                v.key_vertex = True
                self.key_vertices.append(v)
                self.sampled_vertices.append(v)

def generate_graph(skeleton):
    def find_neighbors(v,img,remove=False):
        output_v = []
        def get_pixel_value(u):
            if max(u) > 999 or min(u) < 0:
                return
            if img[u[0],u[1]]:
                output_v.append(u)

        get_pixel_value([v[0]+1,v[1]])
        get_pixel_value([v[0]-1,v[1]])
        get_pixel_value([v[0],v[1]-1])
        get_pixel_value([v[0],v[1]+1])
        get_pixel_value([v[0]+1,v[1]-1])
        get_pixel_value([v[0]+1,v[1]+1])
        get_pixel_value([v[0]-1,v[1]-1])
        get_pixel_value([v[0]-1,v[1]+1])
        if remove:
            img[v[0],v[1]] = 0
        return output_v

    graph = Graph()
    img = skeleton
    pre_points = np.where(img!=0)
    pre_points = [[pre_points[0][i],pre_points[1][i]] for i in range(len(pre_points[0]))]
    for point in pre_points:
        v = Vertex(point)
        graph.add_v(v,find_neighbors(point,img))
    graph.find_key_vertices()
    for key_vertex in graph.key_vertices:
        if len(key_vertex.unprocessed_neighbors):
            for neighbor in key_vertex.unprocessed_neighbors:
                key_vertex.unprocessed_neighbors.remove(neighbor)
                #
                curr_v = neighbor
                pre_v = key_vertex
                sampled_v = key_vertex
                counter = 1
                while(not curr_v.key_vertex):
                    if counter % 30 == 0:
                        sampled_v.sampled_neighbors.append(curr_v)
                        curr_v.sampled_neighbors.append(sampled_v)
                        sampled_v = curr_v
                        if not sampled_v.key_vertex:
                            graph.sampled_vertices.append(sampled_v)
                    next_v = curr_v.next(pre_v)
                    pre_v = curr_v
                    curr_v = next_v
                    counter += 1
                sampled_v.sampled_neighbors.append(curr_v)
                curr_v.sampled_neighbors.append(sampled_v)
                curr_v.unprocessed_neighbors.remove(pre_v)

    adjacent = np.ones((len(graph.sampled_vertices),len(graph.sampled_vertices))) * np.inf
    vertices = []
    for ii, v in enumerate(graph.sampled_vertices):
        v.index = ii
        vertices.append([int(v.coord[0]),int(v.coord[1])])
    for v in graph.sampled_vertices:
        for u in v.sampled_neighbors:
            dist = v.distance(u)
            adjacent[v.index,u.index] = dist
            adjacent[u.index,v.index] = dist
    return {'vertices':vertices,'adj':adjacent}

def evaluate_single(pred_result, mask_dir):

    pred_instances = pred_result['pred_instances']
    image_name = pred_result['image_path'].split('/')[-1]
    if image_name.endswith('tiff'):
        image_name = image_name.replace('tiff', 'png')
    else:
        image_name = image_name.replace('jpg', 'png')
    mask_image_path = osp.join(mask_dir, image_name)
    if image_name.endswith('png'):
        sample_seq_path = mask_image_path.replace('binary_map', 'sampled_seq').replace('png', 'pickle')
    else:
        sample_seq_path = mask_image_path.replace('binary_map', 'sampled_seq').replace('jpg', 'pickle')

    with open(sample_seq_path,'rb') as jf:
        gt_data = pickle.load(jf)
    mask = np.array(Image.open(mask_image_path))[:,:,0]
    graph = torch.zeros(1000,1000)
    for instance in pred_instances:
        for i in range(1, len(instance['data'])):
            # import pdb;pdb.set_trace()
            start_vertex, end_vertex = instance['data'][i-1], instance['data'][i]
            graph = update_graph(start_vertex, end_vertex, graph)
    return eval_metric(graph, mask, image_name, gt_data)

def evaluate_all(results_file, mask_dir):
    with open(results_file, 'r') as f:
        pred_results = json.load(f)
    total_image = len(pred_results)
    graph_acc, graph_recall, r_f =[0, 0, 0], [0, 0, 0], [0, 0, 0]
    ECM, Naive, APLS = 0, 0, 0
    results = []
    for pred_result in tqdm(pred_results.values(), desc='evaluate metric'):
        results.append(evaluate_single(pred_result, mask_dir))
    # with multiprocessing.Pool(processes = 16) as pool:
    #     partial_func = partial(evaluate_single, mask_dir=mask_dir)
    #     results = list(tqdm(pool.imap(partial_func, pred_results.values()), total=len(pred_results.values()), desc='evaluate the metric'))
    for acc_single, recall_single, r_f_single, entropy_conn, naive_conn, apls, non_pred_image in results:
        graph_acc  = [i + j for i, j in zip(graph_acc, acc_single)]
        graph_recall = [i + j for i, j in zip(graph_recall, recall_single)]
        r_f = [i + j for i, j in zip(r_f, r_f_single)]
        ECM += entropy_conn
        Naive += naive_conn
        APLS += apls
        total_image -= non_pred_image

    graph_acc_1, graph_recall_1, r_f_1 = graph_acc[0]/total_image, graph_recall[0]/total_image, r_f[0]/total_image
    graph_acc_2, graph_recall_2, r_f_2 = graph_acc[1]/total_image, graph_recall[1]/total_image, r_f[1]/total_image
    graph_acc_5, graph_recall_5, r_f_5 = graph_acc[2]/total_image, graph_recall[2]/total_image, r_f[2]/total_image
    ECM = ECM / total_image
    Naive = Naive /total_image
    APLS = APLS /total_image

    return {'accuracy_1':graph_acc_1, 'accuracy_2':graph_acc_2, 'accuracy_5':graph_acc_5, \
            'recall_1':graph_recall_1, 'recall_2':graph_recall_2, 'recall_5':graph_recall_5, \
            'f1_score_1': r_f_1, 'f1_score_2': r_f_2, 'f1_score_5': r_f_5, 'ECM':ECM, 'Naive':Naive, 'APLS':APLS}


from typing import List

class Solution:
    def minSwaps(self, data: List[int]) -> int:
        cnt = sum(data)
        MAX = m = sum(data[0:cnt])
        for i in range(cnt, len(data)):
            m += data[i] - data[i - cnt]
            MAX = max(MAX, m)
        return cnt - MAX

if __name__ == '__main__':
    assert Solution().minSwaps([1, 0, 1, 0, 1]) == 1
    assert Solution().minSwaps([0, 0, 0, 1, 0]) == 0
    assert Solution().minSwaps([1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1]) == 3

LeetCode-Algorithm/1151. Minimum Swaps to Group All 1's Together/1151.py


import pandas as pd #for reading csv
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import silhouette_score
from sklearn.neighbors import KNeighborsClassifier
from math import sqrt
import dash
import random
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_core_components as dcc
import base64
import os
#external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
ALLOWED_TYPES = (
    "text", "number", "password", "email", "search",
    "tel", "url", "range", "hidden",
)
app = dash.Dash(__name__)
dataframe=pd.read_csv('train-data.csv')

dataframe=dataframe.drop('Unnamed: 0',axis=1) #we dropped 'Unnamed: 0' column because it was irrelavent

#print(dataframe.isnull().sum()) #here we checked how many columns had null values

#let's drop rows with null values

#print(dataframe.shape)

dataframe=dataframe[dataframe['Mileage'].notna()]
dataframe=dataframe[dataframe['Engine'].notna()]
dataframe=dataframe[dataframe['Power'].notna()]
dataframe=dataframe[dataframe['Seats'].notna()]

#we'll drop new peices column since it has a lot of null values
dataframe=dataframe.drop('New_Price',axis=1) #we dropped 'Unnamed: 0' column because it was irrelavent

#the name column can be mined down into more important information
dataframe=dataframe.reset_index(drop=True)
for i in range(dataframe.shape[0]):
    dataframe.at[i, 'Company'] = dataframe['Name'][i].split()[0]
    dataframe.at[i, 'Mileage(km/kg)'] = dataframe['Mileage'][i].split()[0]
    dataframe.at[i, 'Engine(CC)'] = dataframe['Engine'][i].split()[0]
    dataframe.at[i, 'Power(bhp)'] = dataframe['Power'][i].split()[0]

#print(dataframe.head())

dataframe['Mileage(km/kg)'] = dataframe['Mileage(km/kg)'].astype(float)
#dataframe['Power(bhp)'] = dataframe['Power(bhp)'].astype(float)
dataframe['Engine(CC)'] = dataframe['Engine(CC)'].astype(float)

x = 'n'
count = 0
position = []
dataframe=dataframe.reset_index(drop=True)
for i in range(dataframe.shape[0]):
    if dataframe['Power(bhp)'][i]=='null':
        x = 'Y'
        count = count + 1
        position.append(i)
#        dataframe = dataframe.drop(dataframe.index[position])
 #       dataframe = dataframe.reset_index(drop=True)
dataframe = dataframe.drop(dataframe.index[position])
dataframe = dataframe.reset_index(drop=True)

dataframe['Power(bhp)'] = dataframe['Power(bhp)'].astype(float)

#dataframe.drop(["Name"],axis=1,inplace=True)
dataframe.drop(["Mileage"],axis=1,inplace=True)
dataframe.drop(["Engine"],axis=1,inplace=True)
dataframe.drop(["Power"],axis=1,inplace=True)

Location = dataframe[['Location']]
Location = pd.get_dummies(Location,drop_first=True)
#print(Location.head())
Fuel_t = dataframe[['Fuel_Type']]
Fuel_t = pd.get_dummies(Fuel_t,drop_first=True)
#print(Fuel_t.head())

Transmission = dataframe[['Transmission']]
Transmission = pd.get_dummies(Transmission,drop_first=True)

dataframe.replace({"First":1,"Second":2,"Third": 3,"Fourth & Above":4},inplace=True)

dataframe.drop(["Company"],axis=1,inplace=True)

final_train= pd.concat([dataframe,Location,Fuel_t,Transmission],axis=1)

final_train.drop(["Location","Fuel_Type","Transmission"],axis=1,inplace=True)
#print(final_train.shape)

test_data=pd.read_csv('test-data.csv')

test_data = test_data.iloc[:,1:]

#print("Shape of test data Before dropping any Row: ",dataframe.shape)
test_data = test_data[test_data['Mileage'].notna()]
#print("Shape of test data After dropping Rows with NULL values in Mileage: ",test_data.shape)
test_data = test_data[test_data['Engine'].notna()]
#print("Shape of test data After dropping Rows with NULL values in Engine : ",test_data.shape)
test_data = test_data[test_data['Power'].notna()]
#print("Shape of test data After dropping Rows with NULL values in Power  : ",test_data.shape)
test_data = test_data[test_data['Seats'].notna()]
#print("Shape of test data After dropping Rows with NULL values in Seats  : ",test_data.shape)
#print('Droping null done')

test_data = test_data.reset_index(drop=True)
#print('Index reset done')
for i in range(test_data.shape[0]):
    test_data.at[i, 'Mileage(km/kg)'] = test_data['Mileage'][i].split()[0]
    test_data.at[i, 'Engine(CC)'] = test_data['Engine'][i].split()[0]
    test_data.at[i, 'Power(bhp)'] = test_data['Power'][i].split()[0]
#print('Split Done')

test_data['Mileage(km/kg)'] = test_data['Mileage(km/kg)'].astype(float)
test_data['Engine(CC)'] = test_data['Engine(CC)'].astype(float)

#print('casting 1 Done')

position = []
for i in range(test_data.shape[0]):
    if test_data['Power(bhp)'][i]=='null':
        position.append(i)

test_data = test_data.drop(test_data.index[position])
test_data = test_data.reset_index(drop=True)
test_data['Power(bhp)'] = test_data['Power(bhp)'].astype(float)
#print('casting 2 Done')

for i in range(test_data.shape[0]):
    if pd.isnull(test_data.loc[i,'New_Price']) == False:
        test_data.at[i,'New_car_Price'] = test_data['New_Price'][i].split()[0]

test_data['New_car_Price'] = test_data['New_car_Price'].astype(float)

test_data.drop(["Name"],axis=1,inplace=True)
test_data.drop(["Mileage"],axis=1,inplace=True)
test_data.drop(["Engine"],axis=1,inplace=True)
test_data.drop(["Power"],axis=1,inplace=True)
test_data.drop(["New_Price"],axis=1,inplace=True)

var = 'Location'
Location = test_data[[var]]
Location = pd.get_dummies(Location,drop_first=True)
Location.head()
var = 'Fuel_Type'
Fuel_t = test_data[[var]]
Fuel_t = pd.get_dummies(Fuel_t,drop_first=True)
Fuel_t.head()

var = 'Transmission'
Transmission = test_data[[var]]
Transmission = pd.get_dummies(Transmission,drop_first=True)
Transmission.head()

test_data.replace({"First":1,"Second":2,"Third": 3,"Fourth & Above":4},inplace=True)
test_data.head()

final_test= pd.concat([test_data,Location,Fuel_t,Transmission],axis=1)
final_test.head()

final_test.drop(["Location","Fuel_Type","Transmission","New_car_Price"],axis=1,inplace=True)
final_test.head()

#print("Final Test Size: ",final_test.shape)
#print(final_test.head())
#x=dataframe.iloc[,[]]
X = final_train.loc[:,['Year', 'Kilometers_Driven', 'Owner_Type', 'Seats',
       'Mileage(km/kg)', 'Engine(CC)', 'Power(bhp)',
       'Location_Bangalore', 'Location_Chennai', 'Location_Coimbatore',
       'Location_Delhi', 'Location_Hyderabad', 'Location_Jaipur',
       'Location_Kochi', 'Location_Kolkata', 'Location_Mumbai',
       'Location_Pune', 'Fuel_Type_Diesel', 'Fuel_Type_LPG',
       'Fuel_Type_Petrol', 'Transmission_Manual','Price']]
'''
g=[]
for i in range(X.shape[0]):
    h=[]
    for j in range(X.shape[1]):
        h.append(X[X.columns[j]][i])
    g.append(h)

wcss = []
for i in range(1,20):
 kmeans = KMeans(n_clusters=i,init='k-means++',max_iter=300,n_init=10,random_state=0)
 kmeans.fit(g)
 wcss.append(kmeans.inertia_)
 print("Cluster", i, "Inertia", kmeans.inertia_)
plt.plot(range(1,20),wcss)
plt.title('The Elbow Curve')
plt.xlabel('Number of clusters')
plt.ylabel('WCSS') ##WCSS stands for total within-cluster sum of square
plt.savefig('./assets/elbow.png')
'''
kmeans = KMeans(n_clusters=5,init='k-means++',max_iter=300,n_init=10,random_state=0)
y_kmeans=kmeans.fit(X)
gg=kmeans.fit_predict(X)
print(y_kmeans.cluster_centers_)
#print(X.iloc[:,21],X.iloc[:,4])
plt.scatter(X.iloc[:,21],X.iloc[:,3],c=gg,cmap='rainbow')
#plt.scatter(y_kmeans.cluster_centers_[:,0],y_kmeans.cluster_centers_[:,1],marker='*',c='red')
#plt.show()
plt.savefig('./assets/plot.png', dpi=300, bbox_inches='tight')
#plt.scatter(X.iloc[:,21],X.iloc[:,3],c=gg,cmap='rainbow')
index_closest=-1
def recommendation(km):
    diff = np.array(y_kmeans.cluster_centers_) - np.array(km)[0]  # NumPy broadcasting
    #print(diff)
    dist = np.sqrt(np.sum(diff**2, axis=-1))  # Euclidean distance
    print(dist)
    closest_centroid = np.array(y_kmeans.cluster_centers_)[np.argmin(dist),]
    '''print("closest centroid:\n",closest_centroid)'''

    for i in range(5):
        if(np.array(y_kmeans.cluster_centers_)[i]==closest_centroid).all():
            global index_closest
            index_closest=i
            break
#recommendation()
    cluster_map=pd.DataFrame()
    cluster_map['cluster'] = y_kmeans.labels_
    print("recommended ",(np.array(cluster_map[cluster_map.cluster == index_closest].head().index)))
    ii=random.random()*(len(np.array(cluster_map[cluster_map.cluster == index_closest].index))-1)
    ii=int(ii)
    #for i in range(len(np.array(cluster_map[cluster_map.cluster == index_closest].index))):
    return dataframe['Name'][ii]
#print(np.array(y_kmeans.cluster_centers_))
#print(abs(closest_centroid[-1]-np.array(X)[1,:][-1]))
#print(y_kmeans.inertia_)
Distances=100000000
index=[]
def KNN(test_data):
    global final_train,index,Distances,index
    final_train = final_train.loc[:,['Year', 'Kilometers_Driven', 'Owner_Type', 'Seats',
        'Mileage(km/kg)', 'Engine(CC)', 'Power(bhp)',
        'Location_Bangalore', 'Location_Chennai', 'Location_Coimbatore',
        'Location_Delhi', 'Location_Hyderabad', 'Location_Jaipur',
        'Location_Kochi', 'Location_Kolkata', 'Location_Mumbai',
        'Location_Pune', 'Fuel_Type_Diesel', 'Fuel_Type_LPG',
        'Fuel_Type_Petrol', 'Transmission_Manual','Price']]
    f_t=np.array(final_train)
    print("Yayy",f_t,test_data)
    for i in range(5000):
        dd=0
        for j in range(21):
            dd=dd+sqrt(abs(f_t[i][j]**2-test_data[j]**2))
            #print(dd)
        if(dd<Distances):
            Distances=dd
            index.append(f_t[i][-1])
    print(index[0])
    return sum(index) / len(index)
'''print('closest_neighbour',index[-1],test_data[-1])'''
#fig = go.Figure(data=[go.Scatter(x=[1, 2, 3], y=[4, 1, 2])])
#encoded_image = base64.b64encode(open('/home/dell/ddd/minipro/plot.png', 'rb').read())
app.layout = html.Div(style={'position':'absolute','top':'7vh','left':'45vw'},children=[
    dcc.Input(
            id="text1",
            type="text",
            placeholder="Year",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text2",
            type="text",
            placeholder="Kilometers Driven",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text3",
            type="text",
            placeholder="Times Resold",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
                dcc.Input(
            id="text4",
            type="text",
            placeholder="Seats",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text5",
            type="text",
            placeholder="Mileage(km/kg)",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text6",
            type="text",
            placeholder="Engine(CC)",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text7",
            type="text",
            placeholder="Power(bhp)",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text8",
            type="text",
            placeholder="City",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text9",
            type="text",
            placeholder="Fuel type",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P(''),
        dcc.Input(
            id="text10",
            type="text",
            placeholder="Transmission",
            style={'borderStyle':'solid','height':'5vh','textAlign':'center'}
        ),
        html.P('',id='output'),
        html.P(''),
        html.P('',style={'backgroundImage':'url("/assets/elbow.png")','height':'500px','width':'500px','backgroundSize':'cover','position':'relative','left':'-20vw'}),
        #html.P('elbow method results'),
        html.P('',style={'backgroundImage':'url("/assets/plot.png")','height':'500px','width':'500px','backgroundSize':'cover','position':'relative','left':'-20vw'})
        ,html.P('mileage vs prices'),
        html.P('',style={'backgroundImage':'url("/assets/engine.png")','height':'500px','width':'500px','backgroundSize':'cover','position':'relative','left':'-20vw'}),
        html.P('engine vs prices'),
])
@app.callback(
    Output("output", "children"),
    Input("text1", "value"),
    Input("text2", "value"),
    Input("text3", "value"),
    Input("text4", "value"),
    Input("text5", "value"),
    Input("text6", "value"),
    Input("text7", "value"),
    Input("text8", "value"),
    Input("text9", "value"),
    Input("text10", "value"),

)
def update_output(text1, text2,text3,text4,text5,text6,text7,text8,text9,text10):
    if text1 is None:
        raise dash.exceptions.PreventUpdate
    if text2 is None:
        raise dash.exceptions.PreventUpdate
    if text3 is None:
        raise dash.exceptions.PreventUpdate
    if text4 is None:
        raise dash.exceptions.PreventUpdate
    if text5 is None:
        raise dash.exceptions.PreventUpdate
    if text6 is None:
        raise dash.exceptions.PreventUpdate
    if text7 is None:
        raise dash.exceptions.PreventUpdate
    if text8 is None:
        raise dash.exceptions.PreventUpdate
    if text9 is None:
        raise dash.exceptions.PreventUpdate
    if text10 is None:
        raise dash.exceptions.PreventUpdate
    else:
        t8={'Banglore':1,'Chennai': 2,'Coimbatore': 3,
            'Delhi': 4,'Hyderabad': 5,'Jaipur': 6,'Kochi': 7,
            'Kolkata': 8,'Mumbai': 9,'Pune': 10}
        t9={'Diesel': 0,'LPG': 1,'Petrol': 2}
        t10={'Automatic': 0,'Manual': 1}
        if text8 not in t8:
            return "invalid entry at city"
        if text9 not in t9:
            return "invalid entry at Fuel"
        if text10 not in t10:
            return "invalid entry at transmission"
        try:
            text1=float(text1)
            text2=float(text2)
            text3=float(text3)
            text4=float(text4)
            text5=float(text5)
            text6=float(text6)
            text7=float(text7)
        except:
            return "invalid entry,please check the values entered"
        text8=t8[text8]
        text9=t9[text9]
        text10=t10[text10]
        text3=text3+1
        a1=[text1,text2,text3,text4,text5,text7]
        for i in range(text8):
            a1.append(0)
        a1.append(1)

        for j in range(text8+1,11):
            a1.append(0)
        for k in range(text9):
            a1.append(0)
        a1.append(1)
        for k in range(text9+1,3):
            a1.append(0)
        a1.append(text10)
        gg=KNN(a1)
        km=recommendation(a1)
        return ("Predicted Price: ",gg," Recommended Car:",km)

if __name__ == '__main__':
    app.run_server(debug=True)


#!/usr/bin/env python3

from aoc import get_lines

def get_seat_id(s):
    b = ''
    for c in s:
        if c == 'B' \
        or c == 'R':
            b += '1'
        else:
            b += '0'
    return int(b, 2)

def run(input):
    seat_id_list = []
    for s in input:
        seat_id_list.append(get_seat_id(s))
    seat_id_list.sort()

    for i in range(len(seat_id_list)):
        curr = seat_id_list[i]
        next = seat_id_list[i + 1]
        if next != curr + 1:
            return curr + 1

def main():
    assert get_seat_id('BFFFBBFRRR') == 567
    assert get_seat_id('FFFBBBFRRR') == 119
    assert get_seat_id('BBFFBBFRLL') == 820
    return run(get_lines('d05'))

if __name__ == '__main__':
    print(main())


import math
import numpy as np
import matplotlib.pyplot as plt
from collections.abc import MutableMapping
from fractions import Fraction
from mpmath import quad
from mpl_toolkits.mplot3d import axes3d, Axes3D
from matplotlib import cm

class Stack(MutableMapping):
    '''
    Implements a stack holding variable bindings for execution of translated Maxima code.
    Provides access via Python dictionary like bindings.
    '''
    def __init__(self, data={}, sub={}):
        '''
        data parameter is a dictionary holding values for the current stack frame.
        sub is either an instance of Stack or dictionary, holding values at stack frames below the current one.
        '''
        self.mapping = data
        self.sub = sub

    def __getitem__(self, key):
        if key in self.mapping:
            return self.mapping[key]
        else:
            return self.sub[key]

    def __delitem__(self, key):
        if key in self.mapping:
            del self.mapping[key]
        else:
            del self.sub[key]

    def __setitem__(self, key, value):
        if key in self.mapping:
            self.mapping[key] = value
        else:
            self.sub[key] = value
        return(value)

    def __iter__(self):
        return iter({**self.sub, **self.mapping})

    def __len__(self):
        return len(self.mapping) + len(self.sub)

    def __repr__(self):
        return f"{self.mapping}, sub:{self.sub}"

    def ins(self, data):
        self.mapping={**self.mapping, **data}

# v contains the standard variable mapping from Maxima and is the base for all Stack instances for translated code
v = {
    'fpprec': 16,
    'pi': math.pi,
    'e': math.e,
    'ratepsilon': 2.0E-15
}

def plot2d(mapping, *constraints, v = v):
    '''
    provides functionality for plotting functions in 2D plane.
    mapping is either:
    1) A single function/lambda taking one input in the domain defined by constraints.
    2) List of functions/lambda as defined in 1 above.

    Example:
    plot2d(lambda x: x**2, ['x', 0, 1])
    where 0 and 1 are the lower and upper bounds respectively.
    '''
    plt.ion()
    if type(mapping) != list:
        mapping = [mapping]
    for expr in mapping:
        if len(constraints) == 1:
            X = np.arange(constraints[0][1],
                          constraints[0][2],
                          0.0001,
                          dtype = 'float')
            Y = np.array([expr(xi) for xi in X])
            plt.plot(X, Y)
    plt.draw()

def plot3d(mapping, *constraints, v = v):
    '''
    provides functionality for plotting functions in 3D plane.
    mapping is either:
    1) A single function/lambda taking two inputs in the domain defined by constraints.
    2) List of functions/lambda as defined in 1 above.

    Example:
    plot2d(lambda x, y: x**2 + y**2, ['x', 0, 1], ['y', 0, 2])
    where 0 and 1 are the lower and upper bounds respectively for first input and
    0 and 2 are the lower and upper bounds respectively for the second input.
    '''
    fig = plt.figure()
    ax = Axes3D(fig)
    plt.ion()
    if type(mapping) != list:
        mapping = [mapping]
    for expr in mapping:
        vexpr = np.vectorize(expr)
        if len(constraints) == 2:
            X = np.arange(constraints[0][1],
                          constraints[0][2],
                          0.01,
                          dtype = 'float')
            Y = np.arange(constraints[1][1],
                          constraints[1][2],
                          0.01,
                          dtype = 'float')
            X, Y = np.meshgrid(X, Y)
            Z = vexpr(X, Y)
            surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)
    plt.show()

# f contains the function mapping used by translated functions from Maxima
f = {
    'sin': math.sin,
    'pow': math.pow,
    'factorial': math.factorial,
    'floor': math.floor,
    'sqrt': math.sqrt,
    'num':lambda x:x,
    'denom': lambda x:1,
    'print': print,
    'listp': lambda x: type(x) == list,
    'numberp': lambda x: (type(x) == int or
                          type(x) == float or
                          type(x) == Fraction or
                          type(x) == np.float64 or
                          type(x) == np.float128 or
                          type(x) == np.float32 or
                          type(x) == np.float16 or
                          type(x) == np.float),
    'length': len,
     # As defined in "Concrete Mathematics", Section 3.4
    'mod': lambda x,y: (x if y == 0 else x - y * math.floor(x / y)),
    'emptyp': lambda x: (True if len(x) == 0 else False),
    'first': lambda x: x[0],
    'integerp': lambda x: type(x) == int,
    'append': lambda *x: [i for l in x for i in l],
    'plot2d': plot2d,
    'plot3d': plot3d,
    'map': lambda f, i: list(map(f, i)),
    'abs': abs,
    'every': lambda func, l: all(map(func, l)),
    'quad_qagi': quad,
    'cos': math.cos,
    'float': float,
    'signum': lambda x: 0 if x==0 else x/abs(x)
}

def assign(lhs, rhs, v = v):
    v[lhs] = rhs
    return rhs


from PIL import Image
import os
from django.conf import settings
from .models import *
from datetime import datetime as dt

def create_tumbnail(user, location, name):
    '''Creates thumbnails from originally upload files based on users cuirrent Plan'''
    size = UserDetail.objects.get(user=user).plan.size.all()
    new_path = dt.strftime(dt.now(), "images/%Y%m%d")
    for s in size:
        image = Image.open(os.path.join(settings.BASE_DIR)+"/"+location)
        image.thumbnail((s.thumbnail_width, s.thumbnail_height))
        save_dir = (os.path.join(settings.BASE_DIR, 'media/', new_path,
                                 'thumbnail'+str(s.thumbnail_width)+'x'+str(s.thumbnail_height)+'_'+str(user)+'_'+str(name))).replace("\\", "/")
        image.save(save_dir)
        file_uploaded = (os.path.join(new_path,
                                      'thumbnail'+str(s.thumbnail_width)+'x'+str(s.thumbnail_height)+'_'+str(user)+'_'+str(name))).replace("\\", "/")
        newTnail = UploadedFiles.create_new_thumbnail(
            data={
                "user": user,
                "file_uploaded": file_uploaded,
                "original": False
            })

        UploadedFiles.add_plan_to_file(
            UserDetail.objects.get(user=user).plan, newTnail)


graph_data = {
  'A': ['B', 'C'],
  'B': ['D', 'E'],
  'C': ['F'],
  'D': [],
  'E': ['F'],
  'F': []
}

visited_store = []  # List to keep track of visited nodes.
queue = []  # Initialize a queue

# traversal printing of tree
def bfs(visited, graph, node):
    visited.append(node)
    queue.append(node)

    while queue:
        s = queue.pop(0)
        print(s, end=" ")

        for neighbour in graph[s]:
            if neighbour not in visited:
                visited.append(neighbour)
                queue.append(neighbour)

visited_set = set()

def dfs(visited, graph, node):
    if node not in visited:
        print(node)
        visited.add(node)
        for neighbour in graph[node]:
            dfs(visited, graph, neighbour)

print(bfs(visited_store, graph_data, 'A'))
print(dfs(visited_set, graph_data, 'A'))


import numpy as np

def load_TIRadarHeatmap(heatmap_path):
    '''
    read TI radar heatmap
    :param heatmap_path: str - TI radar heatmap path
    :return: dict(np.array)
    '''
    data = np.fromfile(heatmap_path, dtype='float32')
    print(data.shape)
    data = data.reshape((4*257, 232), order='F')
    data = data.reshape((4, 257, 232))
    res = {
        "heatmap_static": data[0, :, :],
        "heatmap_dynamic": data[1, :, :],
        "x_bins": data[2, :, :],
        "y_bins": data[3, :, :],
    }
    return res

pth = './Dataset/20211027_1_group0021/group0021_frame0000/TIRadar/1635319097.410.heatmap.bin'
res = load_TIRadarHeatmap(pth)
x_bins = res['x_bins']
y_bins = res['y_bins']
static = res['heatmap_static']
dynamic = res['heatmap_dynamic']
print(x_bins.shape)
print(static.shape)
print(dynamic.shape)

import matplotlib.pyplot as plt

print(np.arctan2(y_bins[20, :], x_bins[20, :]) / (np.pi) * 180)
# print(y_bins[-1, :], y_bins[-1, :].shape)
plt.imshow(dynamic)
plt.show()
print(np.max(y_bins))
n = 1
map = np.zeros((151*n, 76*n))
print(map.shape)
for i in range(x_bins.shape[0]):
    for j in range(x_bins.shape[1]):
       map[(int(np.ceil(x_bins[i,j])) + 75)*n, int(np.ceil(y_bins[i,j]))*n] = dynamic[i,j]

data = map.T[::-1,:]
print(data.shape)
# data_split = data[55:, 50:100]
data_split = data
print(np.max(data_split), np.min(data_split))
d = data_split / np.max(data_split)
# plt.imshow(d, interpolation='gaussian')
import cv2
# d = cv2.GaussianBlur(d, (3, 3), 1)
plt.imshow(d)
plt.show()
dd = np.where(d, 1, 0)
plt.imshow(dd)
plt.show()
# import seaborn as sns
# sns.heatmap(d)
# plt.show()


def deco(func):
  import time
  import functools
  import math

  @functools.wraps(func)
  def inner(*args, **kwargs):
    start = time.time()
    res = func(*args, **kwargs)
    finish = time.time()

    delta = (finish - start)
    print(f'{func.__name__} works for {delta:2.3}')
    return res
  return  inner

@deco
def twoSumSlice( nums, target):
    result=[]
    for i in range(len(nums)):
        if target-nums[i] in nums[i+1::]:
            result.append(i)
            result.append(nums[i+1::].index((target-nums[i]))+i+1)
    return result

@deco
def twoSumDict(nums, target):
    collected = {}
    for index, val in enumerate(nums):
        diff = target - val
        if diff in collected:
            return [collected[diff], index]
        collected[val] = index
        #print(collected)

if __name__ == "__main__":
  print(twoSumSlice((80,5,2,7,9,11), 9))
  print(twoSumDict((80,5,2,7,9,11), 9))
  assert twoSumSlice([2, 7, 11, 15], 9) == [0, 1], 'проверка методом со слайсом'
  assert twoSumDict([2, 7, 11, 15], 9) == [0, 1], 'проверка методом со словарём'


from PyQt5.QtWidgets import (QMessageBox, QDialog, QPushButton, QTableView, QVBoxLayout,
                             QHeaderView, QHBoxLayout)
from PyQt5.QtSql import QSqlQuery, QSqlQueryModel
from PyQt5.QtCore import Qt, QVariant
from PyQt5.QtGui import QColor, QTextLength, QIcon
from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrintDialog
from ext.APM import DataError, QSqlAlignColorQueryModel
from ext import APM
from ext.Printing import ReportPrint
import poloresurce

class AvailableHorses(QDialog):

    def __init__(self, db, reportType, parent=None):
        super().__init__(parent=None)
        self.parent = parent
        self.reportType = reportType
        self.cdb = db
        self.setModal(True)
        self.setUI()
        self.table = None
        self.centerColumns = []
        self.printColumnWidths = []
        if self.reportType == APM.REPORT_TYPE_ALL_HORSES:
            self.setAllHorsesTable()
        elif self.reportType == APM.REPORT_TYPE_ALL_BREAKING_HORSES:
            self.setAllBreakingHorsesTable(APM.REPORT_TYPE_ALL_BREAKING_HORSES)
        elif self.reportType == APM.REPORT_TYPE_ALL_PLAYING_HORSES:
            self.setAllBreakingHorsesTable(APM.REPORT_TYPE_ALL_PLAYING_HORSES)

    def setUI(self):
        self.setWindowTitle("Unassigned Horses")
        self.horseTable = QTableView()
        self.horseTable.verticalHeader().setVisible(False)

        oKButton = QPushButton(QIcon(":/Icons8/exit/closeman.png"),"Exit", self)
        oKButton.clicked.connect(self.widgetClose)
        oKButton.setMaximumSize(100, 30)

        printButton = QPushButton(QIcon(":/Icons8/print/print.png"), "Print", self)
        printButton.setMaximumSize(100,30)
        printButton.clicked.connect(self.handlePrint)

        previewButton = QPushButton(QIcon(":/Icons8/print/printpreview.png"), "Preview", self)
        previewButton.setMaximumSize(100,30)
        previewButton.clicked.connect(self.handlePreview)

        pdfButton = QPushButton(QIcon(":/Icons8/print/savepdf.png"), "Save PDF", self)
        pdfButton.setMaximumSize(100, 30)
        pdfButton.clicked.connect(self.handlePdf)

        buttonsLayout = QHBoxLayout()
        buttonsLayout.addWidget(pdfButton)
        buttonsLayout.addWidget(printButton)
        buttonsLayout.addWidget(previewButton)
        buttonsLayout.addWidget(oKButton)

        layout = QVBoxLayout()
        layout.addWidget(self.horseTable)
        layout.addLayout(buttonsLayout)
        self.setLayout(layout)

    def setAllBreakingHorsesTable(self, mode):
        qry = QSqlQuery(self.cdb)
        qry.prepare("""SELECT
        ah.id ID,
        h.rp RP,
        h.name,
        CASE
            WHEN h.sexid = 1 THEN _ucs2 X'2642'
            WHEN h.sexid = 2 THEN _ucs2 X'2640'
            WHEN h.sexid = 3 THEN _ucs2 X'265E'
        END Sex,
        c.coat as Coat,
        TIMESTAMPDIFF(YEAR, h.dob, CURDATE()) Age,
        TIMESTAMPDIFF(MONTH, ah.dos, CURDATE()) Month,
        ah.agreementid AgrNo,
        ct.fullname as Contact
        FROM
        agreementhorses AS ah
        INNER JOIN horses AS h
        ON ah.horseid = h.id
        INNER JOIN coats as c
        ON h.coatid = c.id
        INNER JOIN agreements as a
        ON ah.agreementid = a.id
        INNER JOIN contacts as ct
        ON a.supplierid = ct.id
        WHERE ah.active
            AND a.agreementtype in (SELECT ?)
        ORDER BY a.agreementtype, a.id;""")
        qry.addBindValue(QVariant(0))if self.reportType == APM.REPORT_TYPE_ALL_BREAKING_HORSES \
            else qry.addBindValue(QVariant(1).value())
        qry.exec_()
        try:
            if qry.size() < 0:
                raise DataError('setAllHorsesTable', qry.lastError().text())
            elif qry.size() == 0:
                raise DataError('No Data', "There are not horses to show")
            self.centerColumns = [1, 3, 5, 6, 7]
            colorDict = {'column': (3),
                         u'\u2640': (QColor('pink'), QColor('black')),
                         u'\u2642': (QColor('lightskyblue'), QColor('black')),
                         u'\u265E': (QColor('lightgrey'), QColor('black'))}
            allHorsesModel = QSqlAlignColorQueryModel(self.centerColumns,[], colorDict)
            allHorsesModel.setQuery(qry)
            allHorsesModel.setHeaderData(0, Qt.Horizontal, "ID")
            allHorsesModel.setHeaderData(1, Qt.Horizontal, "RP")
            allHorsesModel.setHeaderData(2, Qt.Horizontal, "Name")
            allHorsesModel.setHeaderData(3, Qt.Horizontal, "Sex")
            allHorsesModel.setHeaderData(4, Qt.Horizontal, "Coat")
            allHorsesModel.setHeaderData(5, Qt.Horizontal, "Age")
            allHorsesModel.setHeaderData(6, Qt.Horizontal, "Month")
            allHorsesModel.setHeaderData(7, Qt.Horizontal, "AgrNo")
            allHorsesModel.setHeaderData(8, Qt.Horizontal, "With")
            self.horseTable.setModel(allHorsesModel)
            self.horseTable.hideColumn(0)
            header = self.horseTable.horizontalHeader()
            header.setStyleSheet("QHeaderView {font-size: 8pt;}")
            header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(2, QHeaderView.Stretch)
            header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(4, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(5, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(6, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(7, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(8, QHeaderView.ResizeToContents)
            self.horseTable.setRowHeight(0, 10)
            self.horseTable.verticalHeader().setDefaultSectionSize(
                self.horseTable.rowHeight(0))
            self.horseTable.setMinimumSize(700, 400)
            self.setWindowTitle("Horses On Breaking Agreements") if self.reportType == APM.REPORT_TYPE_ALL_BREAKING_HORSES \
                else self.setWindowTitle("Horses On Play & Sale Agreements")
            self.setMinimumWidth(800)
            # set the printing column widths

            col_1 = QTextLength(QTextLength.FixedLength, 45)
            col_2 = QTextLength()
            col_3 = QTextLength(QTextLength.FixedLength, 45)
            col_4 = QTextLength(QTextLength.FixedLength, 120)
            col_5 = QTextLength(QTextLength.FixedLength, 45)
            col_6 = QTextLength(QTextLength.FixedLength, 45)
            col_7 = QTextLength(QTextLength.FixedLength, 45)
            col_8 = QTextLength()

            self.printColumnWidths = [col_1, col_2, col_3, col_4, col_5,
                                      col_6, col_7, col_8]

        except DataError as err:
            QMessageBox.warning(self, "DataError", err.message)
            raise DataError(err.source, err.message)
            #self.widgetClose()

    def setAllPlayingHorsesTable(self):
        pass

    def setAllHorsesTable(self):
        """Gets the query for the model, uses a subclasesd SqlQueryModel including the posibility to
        pass information on centered columns and tex and background colors.
        Centered columns ares set in a list ([column#,....column#n]) where column referes to the column that
         will be centered.
         The color information is place in a dictionary with the following format:
            {'column' : value,
            key1:(QTextColor, QBackGroundCColor),
             key2: (QTextColor, QBackgroundColor),
             ....................................,
             keyn:(QTextColor, QBackgroundColor)}}
        where:
            column is the query field number,
            key is the comparison value to check agaisnt the query(column).value()
            and (QTextColor, QBackGroundColor) is a tuple wit099999999999999999999999999999o9h two QColor values, the first for
            QTextColorRole, and the second for QbackgroundColorrole"""
        colorDict = {'column':(5),
                        u'\u2640':(QColor('pink'), QColor('black')),
                        u'\u2642':(QColor('lightskyblue'), QColor('black')),
                        u'\u265E': (QColor('lightgrey'), QColor('black'))}
        try:
            qry = QSqlQuery(self.cdb)
            qry.exec_("""SELECT h.id,
                 h.rp,
                 h.name,
                 h.dob,
                 FLOOR(DATEDIFF(CURDATE(), h.dob)/365) Age,
                 CASE WHEN h.sexid = 1 THEN _ucs2 X'2642'
                 WHEN h.sexid = 2 THEN _ucs2 X'2640'
                 WHEN h.sexid = 3 THEN _ucs2 X'265E'
                 END Sex,
                 c.coat,
                 IF (h.isbroke = 1, _ucs2 X'2714', '') broke,
                 IF (h.isbreakable = 1, _ucs2 X'2714', '') breakable,
                 IF (h.active = 1, _ucs2 X'2714', '') active
                 FROM horses as h
                 INNER JOIN sexes as s
                 ON h.sexid = s.id
                 INNER JOIN coats as c
                 ON h.coatid = c.id
                 WHERE h.active
                 AND h.id NOT IN (SELECT ah.horseid FROM agreementhorses ah
                                  WHERE ah.active)
                 ORDER BY h.isbroke, h.sexid, h.name""")
            if qry.size() < 1:
                raise DataError('setAllHorsesTable', qry.lastError().text())
            self.centerColumns = [1, 4, 5, 7, 9]
            allHorsesModel = QSqlAlignColorQueryModel(self.centerColumns,[],colorDict)
            allHorsesModel.setQuery(qry)
            allHorsesModel.setHeaderData(1,Qt.Horizontal,"RP")
            allHorsesModel.setHeaderData(2, Qt.Horizontal, "Name")
            allHorsesModel.setHeaderData(3, Qt.Horizontal, "DOB")
            allHorsesModel.setHeaderData(4, Qt.Horizontal, "Age")
            allHorsesModel.setHeaderData(5, Qt.Horizontal, "Sex")
            allHorsesModel.setHeaderData(6, Qt.Horizontal, "Coat")
            allHorsesModel.setHeaderData(7, Qt.Horizontal, "Broke")
            allHorsesModel.setHeaderData(9,Qt.Horizontal, "Active")
            self.horseTable.setModel(allHorsesModel)
            self.horseTable.hideColumn(0)
            self.horseTable.hideColumn(8)
            header = self.horseTable.horizontalHeader()
            header.setStyleSheet("QHeaderView {font-size: 8pt;}")
            header.setSectionResizeMode(1, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(2, QHeaderView.Stretch)
            header.setSectionResizeMode(3, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(4, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(5, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(6, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(7, QHeaderView.ResizeToContents)
            header.setSectionResizeMode(9, QHeaderView.ResizeToContents)
            self.horseTable.setRowHeight(0, 10)
            self.horseTable.verticalHeader().setDefaultSectionSize(
                self.horseTable.rowHeight(0))
            self.horseTable.setMinimumSize(700, 400)

            #set the printing column widths

            col_1 = QTextLength(QTextLength.FixedLength, 45)
            col_2 = QTextLength()
            col_3 = QTextLength(QTextLength.FixedLength, 100)
            col_4 = QTextLength(QTextLength.FixedLength, 45)
            col_5 = QTextLength(QTextLength.FixedLength, 45)
            col_6 = QTextLength(QTextLength.FixedLength, 120)
            col_7 = QTextLength(QTextLength.FixedLength, 60)
            col_8 = QTextLength(QTextLength.FixedLength, 60)

            self.printColumnWidths = [col_1, col_2, col_3, col_4, col_5,
                                      col_6, col_7, col_8]

        except DataError as err:
            QMessageBox.warning(self,"DataError", err.message)

    def handlePrint(self):
        rep = ReportPrint(self.horseTable,self.window.title())
        rep.handlePrint()

    def handlePreview(self):
        rep = ReportPrint(self.horseTable, self.windowTitle())
        rep.handlePreview()

    def handlePdf(self):
        rep = ReportPrint(self.horseTable, self.windowTitle(),self.parent)
        rep.handlePdf()

    def widgetClose(self):
        self.done(QDialog.Rejected)