def __init__(self):
        """
        Initialize database and vitess
        """

        self.db = ImageDB()
        self.vitess = VitessConn()
        self.minio = MinioConn()
def query_test():
   # query video
   db = ImageDB()
   vq_time_list = []
   # TODO: arguments type

   arguments = {'latitude': None, 'longitude': None, 'city': None, 'state': None, 'country': None, 'Camera_ID': '0',
                'date': None, 'start_time': None, 'end_time': None, 'download': "True", 'feature': None, 'size': 5000}

   query_time = time.time()
   ####
   db.get_video(arguments)
   #db.cam_images()
   ####
   print("Time taken to query: {}".format(time.time() - query_time))
def test():
    db = ImageDB()
    db.init_tables()
    ''' TEST CAMERA FILE '''
    ''' 1. test correct case '''
    db.batch_insert_camera("./vitess_test/camera_test_standard.csv")
    ''' 2. test wrong hearder name '''
    #db.batch_insert_camera("./vitess_test/camera_test_checkHeader.csv")
    ''' 3. test exceed column '''
    #db.batch_insert_camera("./vitess_test/camera_test_checkExceedColumn.csv")
    ''' 4. test missing column '''
    #db.batch_insert_camera("./vitess_test/camera_test_checkMissingColumn.csv")
    ''' 5. test missing values '''
    #db.batch_insert_camera("./vitess_test/camera_test_checkMissingValues.csv")
    ''' 6. test empty cell '''
    #db.batch_insert_camera("./vitess_test/camera_test_checkEmptyCell.csv")
    ''' 7. test add same things twice '''
    #db.batch_insert_camera("./vitess_test/camera_test_checkInsertExistValue.csv")
    ''' TEST ImageVideo FILE without feature csv'''
    ''' 1. test correct case'''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv")
    ''' 8. test wrong hearder name '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkHeader.csv")
    ''' 9. test exceed column '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkExceedColumn.csv")
    ''' 10. test missing column '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkMissingColumn.csv")
    ''' 11. test missing values '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkMissingValues.csv")
    ''' 12. test empty cell '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkEmptyCell.csv")
    ''' 13. test add same things twice '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkInsertExistValue.csv")
    ''' TEST ImageVideo FILE with feature csv, with feature csv correct all the time'''
    ''' 1. test correct case'''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv", "./vitess_test/imageFeature_test_standard.csv")
    ''' test wrong hearder name '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkHeader.csv", "./vitess_test/imageFeature_test_standard.csv")
    ''' test exceed column '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkExceedColumn.csv", "./vitess_test/imageFeature_test_standard.csv")
    ''' test missing column '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkMissingColumn.csv", "./vitess_test/imageFeature_test_standard.csv")
    ''' test missing values '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkMissingValues.csv", "./vitess_test/imageFeature_test_standard.csv")
    ''' test empty cell '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkEmptyCell.csv", "./vitess_test/imageFeature_test_standard.csv")
    ''' test add same things twice '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkInsertExistValue.csv", "./vitess_test/imageFeature_test_standard.csv")
    ''' TEST ImageVideo FILE with feature csv, with ImageVideo csv correct all the time'''
    ''' 14. test wrong hearder name '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv", "./vitess_test/imageFeature_test_checkHeader.csv")

    # TODO: This should not fail
    ''' 15. test exceed column '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv", "./vitess_test/imageFeature_test_checkExceedColumn.csv")
    ''' 16. test missing column '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv", "./vitess_test/imageFeature_test_checkMissingColumn.csv")
    ''' 17. test missing values '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv", "./vitess_test/imageFeature_test_checkMissingValues.csv")
    ''' 18. test empty cell '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv", "./vitess_test/imageFeature_test_checkEmptyCell.csv")
    ''' 19. test add same things twice '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_standard.csv", "./vitess_test/imageFeature_test_checkInsertExistValue.csv")
    ''' TEST ImageVideo FILE with feature csv, both invalid'''
    ''' This section is obsoleted, any violation of csv format will exit the program '''
    ''' test wrong hearder name '''
    #db.insert_image('any', './test_images/', "./vitess_test/imageVideo_test_checkHeader.csv", "./vitess_test/imageFeature_test_checkHeader.csv")
    ''' test exceed column '''
    #db.insert_image("./vitess_test/imageVideo_test_checkExceedColumn.csv", "./vitess_test/imageFeature_test_checkExceedColumn.csv")
    ''' test missing column '''
    #db.insert_image("./vitess_test/imageVideo_test_checkMissingColumn.csv", "./vitess_test/imageFeature_test_checkMissingColumn.csv")
    ''' test missing values '''
    #db.insert_image("./vitess_test/imageVideo_test_checkMissingValues.csv", "./vitess_test/imageFeature_test_checkMissingValues.csv")
    ''' test empty cell -- FAIL !!!!!!!!!! '''
    #db.insert_image("./vitess_test/imageVideo_test_checkEmptyCell.csv", "./vitess_test/imageFeature_test_checkEmptyCell.csv")
    ''' test add same things twice '''
    #db.insert_image("./vitess_test/imageVideo_test_checkInsertExistValue.csv", "./vitess_test/imageFeature_test_checkInsertExistValue.csv")

    #test image retrieval

    #db.batch_insert_camera("./vitess_test/camera_test_standard.csv")
    '''test time range query'''
    #arg1 = {'latitude': None, 'longitude': None, 'city': None, 'state': None, 'country': None, 'Camera_ID': None, 'date': None, 'start_time': '13:00:00', 'end_time': '14:00:00', 'download': None}
    #db.get_image(arg1)
    ''' test time range and date query '''
    #arg2 = {'latitude': 72.06, 'longitude': None, 'city': None, 'state': 'NY', 'country': None, 'Camera_ID': None, 'date': '02/01/2019', 'start_time': '20:00:00', 'end_time': '18:00:00', 'download': None,'feature':None}
    #db.get_image(arg2)
    '''test camera_id and city query'''
    #arg3 = {'latitude': None, 'longitude': None, 'city': 'Boston', 'state': None, 'country': None, 'Camera_ID': 1, 'date': None, 'start_time': None, 'end_time': None, 'download': None,'feature':None}
    #db.get_image(arg3)
    '''test state and camera_id query'''
    #arg4 = {'latitude': None, 'longitude': None, 'city': None, 'state': 'MA', 'country': None, 'Camera_ID': 1, 'date': None, 'start_time': None, 'end_time': None, 'download': None,'feature':None}
    #db.get_image(arg4)
    '''test country only query'''
    #arg5 = {'latitude': None, 'longitude': None, 'city': None, 'state': None, 'country': 'USA', 'Camera_ID': None, 'date': None, 'start_time': None, 'end_time': None, 'download': None,'feature':None}
    #db.get_image(arg5)
    ''' test no matching results '''
    #arg6 = {'latitude': None, 'longitude': None, 'city': None, 'state': None, 'country': 'UK', 'Camera_ID': 1, 'date': None, 'start_time': None, 'end_time': None, 'download': None,'feature':None}
    #db.get_image(arg6)
    ''' test download data '''
    #arg7 = {'latitude': None, 'longitude': None, 'city': None, 'state': None, 'country': None, 'Camera_ID': 1, 'date': None, 'start_time': None, 'end_time': None, 'download': 'True','feature':None}
    #db.get_image(arg7)
    '''test city only query'''
    #arg8 = {'latitude': None, 'longitude': None, 'city': 'Boston', 'state': None, 'country': None, 'Camera_ID': None, 'date': None, 'start_time': None, 'end_time': None, 'download': None,'feature':None}
    #db.get_image(arg8)
    ''' test feature '''
def build_up_test(num_of_cams, is_real_camera, num_procs, diff_thresh,
                  store_interval):
    # Handle exceptions and edge cases
    if num_procs > 3:
        print('Please limit number of processes to 3\n'
              )  #Limit processes to 3 to prevent memory error
        return
    if num_procs > num_of_cams:
        print('Cannot start more processes than number of cameras')
        print('Reducing number of processes to {}...\n'.format(num_procs))
        num_procs = num_of_cams

    # Initialize start time variable and routine object
    start_time = time.perf_counter()
    print("\nInitializing Connection...")

    print("Finished connection intialization... ELAPSED: ",
          time.perf_counter() - start_time)

    print("querying camera data..." + "------> ", end="")
    timestamp = time.perf_counter()

    print("{} seconds\n".format(time.perf_counter() - timestamp))

    #Read predefined classes from class names file
    classes = utils.read_class_names("my_classes.names")
    class_dict = {}

    vitess = VitessConn()
    for i in range(len(classes)):
        class_dict.update({classes[i]: vitess.getFeature(classes[i])})

    # Assign Cameras to media list
    if is_real_camera == 'ip':
        imageDB = ImageDB()
        media_list, header = imageDB.read_data('camera_list.csv',
                                               config.CAM_HEADER, 'tuple',
                                               None)
        media_list = divide_cams(media_list, num_of_cams, num_procs)

    elif is_real_camera == 'our_vid':
        media_list = ['video_moving.mp4']
        #media_list = [['video_moving.mp4'], ['video_static.mp4'], ['video_moving.mp4'], ['video_static.mp4'], ['video_moving.mp4'], ['video_static.mp4'], ['video_moving.mp4'], ['video_static.mp4'], ['video_moving.mp4'], ['video_static.mp4']]  # 'video_static.mp4', 'video_inbetween.mp4']

        if num_procs < num_of_cams:
            chunked_media = [media_list[0], media_list[1], media_list[2]]
            for media in range(3, len(media_list[:num_of_cams])):
                chunked_media[media % 3].append(media_list[media][0])
            media_list = chunked_media
            print(len(media_list[0]))
            print(len(media_list[1]))
            print(len(media_list[2]))

    else:
        print('is_real_camera flag set to invalid value')
        print('Valid values for is_real_camera: ip, our_vid')
        return
    # Start multiprocessing
    '''
    args = []
    for PID in range(num_procs):
        args.append((media_list[PID], is_real_camera, diff_thresh, store_interval, PID, num_procs, class_dict, classes))

    with multiprocessing.Pool(num_procs) as pool:
        pool.map(_retrieveImage_alias, args)
    '''

    myroutine = Routine()
    myroutine.retrieveImage(media_list, is_real_camera, diff_thresh,
                            store_interval, 0, num_procs, class_dict, classes)

    print('Full video: ', time.perf_counter() - start_time)
    print('Finished')
class DBbuild:
    """
    This class is used to set up and update different parts of the database.
    """
    def __init__(self):
        """
        Initialize database and vitess
        """

        self.db = ImageDB()
        self.vitess = VitessConn()
        self.minio = MinioConn()

    def start(self, filename):
        """
        Initialize all the tables and insert camera data.

        :param filename: .csv file containing saved camera information
        """

        self.db.init_tables()
        self.db.single_insert_camera(filename)
        self.vitess.mydb.commit()

    def append(self, filename):
        """
        Insert camera data to the current tables.

        :param filename: .csv file containing new camera information
        """

        self.db.single_insert_camera(filename)
        self.db.batch_insert_camera(filename)

    def fill(self):
        """
        Insert camera data to the current tables from the API.

        """

        self.db.batch_insert_camera_from_api()

    def feature(self, filename):
        """
        ?

        :param filename: .csv file containing ...
        """
        try:
            classes = utils.read_class_names(filename)

            for i in range(len(classes)):
                self.vitess.insertFeature(
                    tuple([str(uuid.uuid1()), classes[i]]))
                if self.minio.mc.bucket_exists(classes[i]) is False:
                    self.minio.create_bucket(classes[i])
            if self.minio.mc.bucket_exists("none") is False:
                self.minio.create_bucket("none")

            self.vitess.mydb.commit()

            print("Features Inserted")
        except:
            print("Error inserting feature or creating bucket")

    def update(self, filename):
        """
        Update the resolution of all cameras within the .csv file given
        as an argument.

        :param filename: .csv file containing camera information
        """

        cam_info, cam_header = self.db.read_data(filename, config.CAM_HEADER,
                                                 'tuple', None)

        with open('updated_camera_list.csv', mode='w') as csv_file:
            cam_writer = csv.writer(csv_file, delimiter=',')
            cam_writer.writerow(cam_header)

            for info in cam_info:
                cam = Camera(camera_id=info[0],
                             ip_address=info[8],
                             image_path=info[10],
                             video_path=info[11])
                cam.capture = cv2.VideoCapture(cam.video_path)
                cam.get_image()

                if cam.newImage is not None:
                    img_w = cam.newImage.shape[0]
                    img_h = cam.newImage.shape[1]

                cam_writer.writerow([
                    info[0], info[1], info[2], info[3], info[4], info[5],
                    img_w, img_h, info[8], info[9], info[10], info[11]
                ])
                cam.capture.release()
                print('updated camera: {}'.format(info[0]))

        csv_file.close()

    def queryTest(self):
        """
        ??
        :return:
        """
        # Query 1
        label = 'person'
        start_query = time.time()
        self.vitess.allFramesWithFeature(label)
        end_query1 = time.time() - start_query

        # Query 2
        Timestamp1 = '14:43:00'
        Timestamp2 = '14:43:10'
        CountryName = 'MA'
        StateName = 'Null'
        start_query = time.time()
        frames_between_timestamps = self.vitess.framesBetweenTimestamps(
            Timestamp1, Timestamp2, CountryName, StateName)
        end_query2 = time.time() - start_query

        # Query 3
        start_query = time.time()
        self.vitess.findOneImage()
        end_query2 = time.time() - start_query
 def __init__(self):
     self.db = ImageDB()
class Routine:
    def __init__(self):
        self.db = ImageDB()


    def retrieveImage(self, media_list, is_real_camera, threshold, store_interval, PID, num_proc, class_dict, classes):

        """
        This function performs the following:
        1. Opens cameras and retrieves images
        2. Compares the retrieved image with the last stored image
        3. Makes decision to store retrieved image based on difference results
        4. Passes image to be stored through object detection
        5. Passes the image and image feature information to be inserted into Vitess and Minio

        :param media_list:          list,  camera information to be initialized
        :param is_real_camera:      bool,  True if a real camera
                                           False if a test video
        :param threshold:           float, value representing threshold for difference calculations
        :param store_interval:      float, how long before the old images from a camera needs to be updated (seconds)
        :param PID:                 int,   process id
        :param num_proc:            int,   number of processes
        :param class_dict:          dict,  dictionary of objects that can be detected and their id's
        """
        from SSIM_PIL import compare_ssim

        print ('Process {}: {}'.format(PID, media_list))
        folder_path = "output_images/"
        information = ""
        runtime = 0
        information += "querying camera data..." + "------>"
        timestamp = time.perf_counter()

        cam_list = []
        k = 0
        for media in media_list:
            if is_real_camera == 'ip':
                cam = setup_cams(media)
                cam.number = k
            else:
                cam = setup_vids(media, PID, is_real_camera, k)
                cam.number = k
            k += 1
            cam_list.append(cam)

        information += str(time.perf_counter() - timestamp) + " seconds\n"
        yolo = YOLO(PID, classes)
        # TODO: Check if output_images folder exists
        start_time = time.time()

        size = 0
        i = 0
        j = 0

        count_proc1 = 0
        count_proc2 = 0
        count_proc3 = 0

        start_time = time.time()
        flag = True
        store_size = 0
        #while flag == True:
        while(time.time() - start_time < 180):
            # Track cycles for each process
            if PID == 0:
                count_proc1 += 1
            elif PID == 1:
                count_proc2 += 1
            else:
                count_proc3 += 1

            # Run each camera in camera list
            for cam in cam_list:
                try:
                    # Start time of one single round
                    ui_time = time.perf_counter()

                    # Date string for image name
                    ts = datetime.datetime.now()

                    # Information will be a long string of time performance written to a text file
                    information += "retrieving image ------>"

                    timestamp = time.perf_counter()

                    # Download image from current camera
                    cam.get_image()

                    image_date = ts.strftime("%Y-%m-%d")
                    image_time = ts.strftime("%H:%M:%S.%f")[:-3]
                    j += 1

                    information += str(time.perf_counter() - timestamp) + "seconds\n"

                    # Score is the old and new image difference value
                    score = 0
                    image_name = cam.camera_id + "_" + image_date + "_" + image_time + ".jpg"

                    # Crop image to increase accuracy and speed of difference calculations
                    # TODO: Cropping for ip and public_vid flags
                    # TODO: Perform Automatic cropping
                    '''
                    if is_real_camera != 'our_vid':
                        cropImage = cam.newImage[cam.crop_y_max:cam.crop_y_min, cam.crop_x_max:cam.crop_x_min]
                    else:
                        cropImage = cam.newImage
                    '''
                    cropImage = cam.newImage[cam.crop_y_max:cam.crop_y_min, cam.crop_x_max:cam.crop_x_min]

                    # Comparison of old and new image
                    npImage = Image.fromarray(cropImage)
                    image_size = getsizeof(cam.newImage)
                    size += image_size
                    # If oldImage exits compare with the new image
                    if cam.oldImage:
                        timestamp = time.perf_counter()  # Time stamp to see how long difference calculation takes
                        score = compare_ssim(cam.oldImage, npImage)
                        information += "Difference calculations: " + str(time.perf_counter() - timestamp) + "seconds\n"
                    # If old and new image difference score is less than the minimum threshold difference percentage...
                    if score < threshold:
                        # Record current time for last difference check
                        cam.last_dif_check = time.time()

                        # Overwrite new image as the old image
                        cam.oldImage = npImage

                        # Reset store interval
                        cam.store_interval = store_interval

                        cv2.imwrite(folder_path + image_name, cam.newImage)
                        store_size += os.stat('./' + folder_path + image_name).st_size

                        # object detection
                        timestamp = time.perf_counter()
                        bounding_box = yolo.predict(cam.newImage)
                        isprocessed = 1

                        information += "Object Detection: " + str(time.perf_counter() - timestamp) + "seconds\n"

                        # store into database
                        path = folder_path + image_name

                        timestamp = time.perf_counter()
                        self.db.insert_image(image_name, cam.camera_id, isprocessed, bounding_box, classes, image_date,
                                             image_time, image_size, class_dict, path)

                        information += "Image Insertion: " + str(time.perf_counter() - timestamp) + "seconds\n"

                    # If the interval between current time and last difference check is greater than store interval
                    elif time.time() - cam.last_dif_check >= cam.store_interval:
                        cam.last_dif_check = time.time()
                        cam.oldImage = npImage
                        cv2.imwrite(folder_path + image_name, cam.newImage)
                        store_size += os.stat('./' + folder_path + image_name).st_size

                        # object detection
                        timestamp = time.perf_counter()
                        bounding_box = yolo.predict(cam.newImage)
                        isprocessed = 1

                        information += "Object Detection: " + str(time.perf_counter() - timestamp) + "seconds\n"

                        # Store into database
                        path = folder_path + image_name

                        # Inserting images and features into database
                        timestamp = time.perf_counter()
                        self.db.insert_image(image_name, cam.camera_id, isprocessed, bounding_box, classes, image_date,
                                             image_time, image_size, class_dict, path)

                        information += "Image Insertion: " + str(time.perf_counter() - timestamp) + "seconds\n"

                        # If the difference score is the meeting the minimum threshold, double the store interval
                        if (1 - score) < (1 - threshold) / 2:
                            cam.store_interval *= 2


                    try:
                        os.remove(path)
                    except:
                        pass


                    # Tracking the run time for cameras
                    if cam.oldImage:
                        runtime += time.perf_counter() - ui_time

                    # Adding time information to text file
                    information += "Single Round for cam " + cam.camera_id + ":" + str(time.perf_counter() - ui_time) \
                                   + "seconds\n"
                    information += "\n"

                except Exception as e:
                    print(e)
                    cam_list.remove(cam)
                    if len(cam_list) == 0:
                        flag = False

                    pass
            cam.frames = cam.frames + 1
            i += 1


        print("Number of frames processed for {} : {}".format(PID, j))
        print("Runtime for process {}: {}".format(PID, runtime), " seconds")
        print("Processed size: {}".format(size))
        print("store size : {}".format(store_size))

        with open ('output.txt', 'a') as f:
            f.write("Number of frames processed for {} : {}\n".format(PID, j))
            f.write("Runtime for process {}: {} seconds\n".format(PID, runtime))
            f.write("Processed size: {}\n".format(size))            #Size written to file is in bytes
            f.write("Stored size: {}\n".format(store_size))         #Size written to file is in bytes
            f.write("\n")
        cam.capture.release()

        for cam in cam_list:
            print("ID: {}".format(cam.camera_id), ", Number of frames processed = {}".format(cam.frames))

        file = open('image_upload_times.txt', 'w')
        file.write(information)
        file.close()