예제 #1
0
def query_altimeter(sql_ctrl: SQLController) -> float:
    # Logic taken from:
    # https://github.com/adafruit/Adafruit_CircuitPython_MPL3115A2/blob/master/examples/mpl3115a2_simpletest.py
    altitude = round(altimeter.altitude, 2)

    # Safety check for altitude
    # If altitude is above or below these extremes, there is a value error
    # These are rounded ~values for Mt. Everest & Dead Sea
    if altitude > 10000 or altitude < -1000:
        logging.info('Altitude ERROR: {a}'.format(a=altitude))

        # Change the value of altitude prior to saving it
        altitude = sql_ctrl.get_last_altitude()

        # Save the rowid, so we can go back and change to a future altitude,
        # ensuring the replacement altitude is from this hike (i.e. altimeter fails on 1st picture)
        last_rowid = sql_ctrl.get_last_rowid()
        # This entry in the database will be the next index, hence the +1
        altitude_error_list.append(last_rowid + 1)
    else:
        # We have received a valid altitude
        logging.info('Altitude is: {a}'.format(a=altitude))

        # First we need to check to see if there are altitude values to go back and fix
        while len(altitude_error_list) > 0:
            # get last ROWID in the list and change the altitude
            rowid = altitude_error_list.pop()
            sql_ctrl.set_altitude_for_rowid(rowid, altitude)

    # We now have an error checked altitude to return
    return altitude
예제 #2
0
파일: collector.py 프로젝트: mbaytas/capra
def camcapture(pi_cam: picamera, cam_num: int, hike_num: int, photo_index: int,
               sql_controller: SQLController):
    print('select cam{n}'.format(n=cam_num))
    if cam_num < 1 or cam_num > 3:
        raise Exception(
            '{n} is an invalid camera number. It must be 1, 2, or 3.'.format(
                n=cam_num))
    else:
        if cam_num == 1:
            gpio.output(SEL_1, True)
            gpio.output(SEL_2, False)
            print("cam 1 selected")
        if cam_num == 2:
            gpio.output(SEL_1, False)
            gpio.output(SEL_2, False)
            print("cam 2 selected")
        if cam_num == 3:
            gpio.output(SEL_1, True)
            gpio.output(SEL_2, True)
            print("cam 3 selected")
        time.sleep(0.2)  # it takes some time for the pin selection

        # Build image file path
        image_path = '{d}hike{h}/{p}_cam{c}.jpg'.format(d=DIRECTORY,
                                                        h=hike_num,
                                                        p=photo_index,
                                                        c=cam_num)
        print(image_path)

        # Take the picture
        pi_cam.capture(image_path)
        sql_controller.set_image_path(cam_num, image_path, hike_num,
                                      photo_index)
        print('cam {c} -- picture taken!'.format(c=cam_num))
예제 #3
0
def main():
    initialize_logger(CAMERA_PATH)
    logging.info('Starting transfer script...')

    # 1. Copy all files from camera storage location to projector storage location
    # if a file being copied has the same name as a file already on the projector,
    # the old file will be overwritten
    logging.info('Begin SSH copy')
    try:
        os.system('scp -r {c}* {p}'.format(c=CAMERA_PATH, p=PROJECTOR_PATH))
    except Exception as error:
        logging.exception('===== Error while trying to copy data to projector ===== ')
        logging.exception(error)

    # 2. Remove all pictures on camera
    delete_picture_directories(CAMERA_PATH)

    # 3. Delete camera DB data
    # This removes all the data but does not drop tables (next indexes will be preseerved)
    sql_controller = SQLController(database=CAMERA_DB)
    sql_controller.delete_picture_and_hikes_tables()

    # 4. Start the remote script
    # subprocess.call('ssh [email protected] python3 /home/pi/capra/test-projector-insert.py', shell=True)
    
    # subprocess.call('ssh [email protected] export DISPLAY=:0', shell=True)
    subprocess.call('ssh [email protected] python3 /home/pi/Developer/capra-slideshow/slideshow-fade-image.py', shell=True)
    
    # subprocess.call('ssh [email protected] export DISPLAY=:0 && python3 /home/pi/Developer/capra-slideshow/slideshow-fade-image.py', shell=True)

    logging.info('...finished transfer script \n')
예제 #4
0
def getDBControllers():
    global dbSRCController, dbDESTController

    dbSRCController = SQLController(database=SRCDBPATH)

    # TODO: if dest does not exist, create a new DB by copying the skeleton file
    dbDESTController = SQLController(database=DESTDBPATH)
예제 #5
0
    def setUpClass(self):
        # NOTE - if the database or id is changed, all the tests will break
        # They are dependent upon that
        self.sql_controller = SQLController(database=self.DB,
                                            directory=self.directory)
        self.picture = self.sql_controller.get_picture_with_id(11994)

        # For testing directly on the sql statements
        self.sql_statements = SQLStatements()
예제 #6
0
def getDBControllers():
    global dbSRCController, dbSRCController_remote, dbDESTController

    # this is a locally saved db, copied from camera
    dbSRCController = SQLController(database=CAMERA_DB)

    # the remote database in the camera
    dbSRCController_remote = SQLController(database=CAMERA_DB_REMOTE)

    # if dest does not exist, create a new DB by copying and renaming the skeleton file
    if (not os.path.exists(PROJECTOR_DB)):
        src_file = DATAPATH + g.DBNAME_INIT
        shutil.copy(src_file, DATAPATH +
                    g.DBNAME_MASTER)  # copy the file to destination dir

    dbDESTController = SQLController(database=PROJECTOR_DB)
예제 #7
0
class TestDatabase(unittest.TestCase):
    DB = '/home/pi/capra-storage/capra_camera.db'
    DB_EMPTY = '/home/pi/capra/tests/test_db_camera_empty.db'

    sql_controller = SQLController(database=DB)

    def test_get_last_altitude(self):
        sql_controller = SQLController(database=self.DB_EMPTY)
        alt = sql_controller.get_last_altitude()
        self.assertEqual(alt, 0)
예제 #8
0
    def setUpClass(self):
        # NOTE - if the database or id is changed, all the tests will break
        # They are dependent upon that
        self.sql_controller = SQLController(database=self.DB, directory=self.directory)
        self.picture = self.sql_controller.get_picture_with_id(11994)

        # For testing directly on the sql statements
        self.sql_statements = SQLStatements()

        # Grab an array of hikes from the database
        sql = 'SELECT hike_id FROM hikes ORDER BY hike_id ASC;'
        rows = self.sql_controller._execute_query_for_anything(sql)
        i = 0
        for r in rows:
            self.hikes.append(r[0])

            # Grab the first picture_id from each hike from the database
            sql = 'SELECT picture_id FROM pictures WHERE hike={h} ORDER BY index_in_hike ASC LIMIT 1;'.format(h=r[0])
            pid = self.sql_controller._execute_query_for_int(sql)
            self.picture_ids.append(pid)
            i += 1
예제 #9
0
    def setupSQLController(self):
        '''Initializes the database connection.\n
        If on Mac/Windows give dialog box to select database,
        otherwise it will use the global defined location for the database'''

        # Mac/Windows: select the location
        if platform.system() == 'Darwin' or platform.system() == 'Windows':
            # filename = QFileDialog.getOpenFileName(self, 'Open file', '', 'Database (*.db)')
            # self.database = filename[0]
            # self.directory = os.path.dirname(self.database)

            self.database = '/Users/Jordan/Dropbox/Everyday Design Studio/A Projects/100 Ongoing/Capra/capra-storage/capra-storage-jordan-projector/capra_projector_jun2021_min_test_0708.db'
            self.directory = '/Users/Jordan/Dropbox/Everyday Design Studio/A Projects/100 Ongoing/Capra/capra-storage/capra-storage-jordan-projector'
        else:  # Raspberry Pi: preset location
            self.database = g.DATAPATH_PROJECTOR + g.DBNAME_MASTER
            self.directory = g.DATAPATH_PROJECTOR

        print(self.database)
        print(self.directory)
        self.sql_controller = SQLController(database=self.database, directory=self.directory)

        # self.picture = self.sql_controller.get_picture_with_id(20000) # 28300
        self.picture = self.sql_controller.get_random_picture()
예제 #10
0
class DatabaseMergedMayTests(unittest.TestCase):
    DB = 'tests/capra_projector_apr2021_min_test_full_merged.db'
    directory = 'capra-storage'
    sql_controller = None
    sql_statements = None
    picture = None

    @classmethod
    def setUpClass(self):
        # NOTE - if the database or id is changed, all the tests will break
        # They are dependent upon that
        self.sql_controller = SQLController(database=self.DB,
                                            directory=self.directory)
        self.picture = self.sql_controller.get_picture_with_id(11994)

        # For testing directly on the sql statements
        self.sql_statements = SQLStatements()
예제 #11
0
파일: collector.py 프로젝트: mbaytas/capra
def main():
    # Initialize and setup hardware
    #initialize_GPIOs()                              # Define the GPIO pin modes
    i2c_bus = smbus.SMBus(1)  # Setup I2C bus
    i2c = busio.I2C(3, 2)  # Setup I2C for DS3231 - TODO merge with smbus
    get_RTC_time(i2c)  # Update system time from RTC
    turn_off_leds()  # TODO - why do we need to
    hello_blinks()  # Say hello through LEDs
    pzo = gpio.PWM(PIEZO, 100)
    beep(pzo, C, 0.25, 0.1, 3)
    #pi_cam = initialize_picamera(RESOLUTION)        # Setup the camera
    initialize_background_play_pause()  # Setup play/pause button
    prev_pause = True

    print("initialized piezo pin OK")

    print('Initializing camera object')
    gpio.output(SEL_1, False)
    gpio.output(SEL_2, False)
    time.sleep(0.2)
    print('Select pins OK')
    pi_cam = picamera.PiCamera()
    time.sleep(0.2)
    print('Cam init OK')
    pi_cam.resolution = RESOLUTION
    print('Resolution OK')
    pi_cam.rotation = 180
    print('Rotation OK')

    # As long as initially paused, do not create new hike yet
    print("Waiting for initial unpause...")
    while (shared.pause):
        if (not prev_pause):
            logging.info('Paused')
            prev_pause = True
        print(">>PAUSED!<<")
        blink(LED_RED, 1, 0.3)
        time.sleep(1)
    print("Initial unpause!")

    # Create SQL controller and update hike information
    sql_controller = SQLController(database=DB)

    created = sql_controller.will_create_new_hike(NEW_HIKE_TIME, DIRECTORY)
    if created:  # new hike created; blink four times
        blink(LED_RED, 4, 0.2)
        os.chmod(
            DIRECTORY,
            766)  # set permissions to be read and written to when run manually
        os.chmod(DB, 766)
    else:  # continuing last hike; blink two times
        blink(LED_RED, 2, 0.2)
    time.sleep(1)
    hike_num = sql_controller.get_last_hike_id()
    photo_index = sql_controller.get_last_photo_index_of_hike(hike_num)

    # Initialize logger
    initialize_logger(hike_num)

    # Start the time lapse
    # --------------------------------------------------------------------------
    while (True):
        # Pause the program if applicable
        while (shared.pause):
            if (not prev_pause):
                logging.info('Paused')
                beep(a, 0.1, 0.1, 2, 2)
                prev_pause = True
            print(">>PAUSED!<<")
            blink(LED_RED, 1, 0.3)
            time.sleep(1)
        # If applicable, log 'unpaused'
        if (prev_pause):
            logging.info('Unpaused')
            prev_pause = False

        # Read the time as UNIX timestamp
        current_time = get_RTC_time(i2c)

        # New picture: increment photo index & add row to database
        photo_index += 1
        sql_controller.create_new_picture(hike_num, photo_index, current_time)

        query_altimeter(i2c_bus)  # Query Altimeter first (takes a while)

        # Take pictures
        camcapture(pi_cam, 1, hike_num, photo_index, sql_controller)
        camcapture(pi_cam, 2, hike_num, photo_index, sql_controller)
        camcapture(pi_cam, 3, hike_num, photo_index, sql_controller)

        # Update the database with metadata for picture & hike
        altitude = read_altimeter(i2c_bus)
        sql_controller.set_picture_time_altitude(altitude, hike_num,
                                                 photo_index)
        sql_controller.set_hike_endtime_picture_count(photo_index, hike_num)

        # timestamp = time.time() # OLD: this takes the time from the RPi, not the DS3221
        timestamp = get_RTC_time(i2c)
        # Blink on every fourth picture
        if (photo_index % 4 == 0):
            blink(LED_BLUE, 1, 0.1)
            logging.info('cameras still alive')

        # Wait until 2.5 seconds have passed since last picture
        while (get_RTC_time(i2c) < timestamp + 2.5):
            pass
예제 #12
0
    def __init__(self, win):
        # Setup the window
        self.window = win
        self.window.title("Capra Slideshow")
        self.window.geometry("1280x720")
        # self.window.geometry("720x1280")
        self.window.configure(background='purple')
        self.canvas = Canvas(root,
                             width=1280,
                             height=720,
                             background="#888",
                             highlightthickness=0)
        # self.canvas.configure(bg='#444')
        self.canvas.pack(expand='yes', fill='both')

        # Hardware control events
        self.window.bind(
            GPIO.add_event_detect(clk,
                                  GPIO.BOTH,
                                  callback=self.detected_rotary_change))
        self.clkLastState = GPIO.input(clk)

        # Using GPIO.BOTH and GPIO input state, however I have noticed glitches. This will need to be ironed out
        # May need to use an after loop
        self.rotary_button_state = GPIO.input(rotary_button)
        self.rotary_button_state = not self.rotary_button_state  # default is True, so set it to False
        self.window.bind(
            GPIO.add_event_detect(rotary_button,
                                  GPIO.BOTH,
                                  callback=self.rotary_button_pressed))

        self.window.bind(
            GPIO.add_event_detect(BUTTON_PLAY_PAUSE,
                                  GPIO.FALLING,
                                  callback=self.button_pressed_play_pause))
        self.window.bind(
            GPIO.add_event_detect(BUTTON_NEXT,
                                  GPIO.FALLING,
                                  callback=self.button_pressed_next))
        self.window.bind(
            GPIO.add_event_detect(BUTTON_PREVIOUS,
                                  GPIO.FALLING,
                                  callback=self.button_pressed_previous))

        # self.window.bind(GPIO.add_event_detect(SLIDER_SWITCH_MODE_0, GPIO.FALLING, callback=self.switch_mode_0))
        # self.window.bind(GPIO.add_event_detect(SLIDER_SWITCH_MODE_1, GPIO.BOTH, callback=self.switch_mode_1))
        # self.window.bind(GPIO.add_event_detect(SLIDER_SWITCH_MODE_2, GPIO.FALLING, callback=self.switch_mode_2))

        self.determine_switch_mode()
        self.window.bind(
            GPIO.add_event_detect(BUTTON_MODE,
                                  GPIO.FALLING,
                                  callback=self.button_mode))

        # Initialization for database implementation
        self.sql_controller = SQLController(database=DB)
        self.picture_starter = self.sql_controller.get_first_time_picture_in_hike(
            10)
        self.picture = self.sql_controller.next_time_picture_in_hike(
            self.picture_starter)

        # Initialization for images and associated properties
        self.alpha = 0

        # Initialize current and next images
        self.current_raw_top = Image.open(
            self._build_filename(self.picture_starter.camera1), 'r')
        self.next_raw_top = Image.open(
            self._build_filename(self.picture.camera1), 'r')
        self.current_raw_mid = Image.open(
            self._build_filename(self.picture_starter.camera2), 'r')
        self.next_raw_mid = Image.open(
            self._build_filename(self.picture.camera2), 'r')
        self.current_raw_bot = Image.open(blank_path, 'r')
        self.next_raw_bot = Image.open(blank_path, 'r')

        # Display the first 3 images to the screen
        self.display_photo_image_top = ImageTk.PhotoImage(self.current_raw_top)
        self.image_label_top = Label(master=self.canvas,
                                     image=self.display_photo_image_top,
                                     borderwidth=0)
        self.image_label_top.pack(side='right', fill='both', expand='yes')
        # self.image_label_top.place(x=20, rely=0.0, anchor='nw')

        self.display_photo_image_mid = ImageTk.PhotoImage(self.current_raw_mid)
        self.image_label_mid = Label(master=self.canvas,
                                     image=self.display_photo_image_mid,
                                     borderwidth=0)
        self.image_label_mid.pack(side='right', fill='both', expand='yes')
        # self.image_label_mid.place(x=20, y=405, anchor='nw')

        self.display_photo_image_bot = ImageTk.PhotoImage(self.current_raw_bot)
        self.image_label_bot = Label(master=self.canvas,
                                     image=self.display_photo_image_bot,
                                     borderwidth=0)
        self.image_label_bot.pack(side='right', fill='both', expand='yes')
        # self.image_label_bot.place(x=20, y=810, anchor='nw')

        # Hike labels
        self.label_mode = Label(self.canvas, text='Modes: ')
        self.label_hike = Label(self.canvas, text='Hike: ')
        self.label_index = Label(self.canvas, text='Index: ')
        self.label_alt = Label(self.canvas, text='Altitude: ')
        self.label_date = Label(self.canvas, text='Date: ')

        self.label_mode.place(relx=1.0, y=0, anchor='ne')
        self.label_hike.place(relx=1.0, y=22, anchor='ne')
        self.label_index.place(relx=1.0, y=44, anchor='ne')
        self.label_alt.place(relx=1.0, y=66, anchor='ne')
        self.label_date.place(relx=1.0, y=88, anchor='ne')

        # Start background threads which will continue for life of the class
        root.after(0, func=self.check_accelerometer)
        root.after(10, func=self.update_text)
        root.after(15, func=self.fade_image)
        root.after(self.TRANSITION_DELAY, func=self.auto_play_slideshow)
예제 #13
0
class Slideshow:
    # GLOBAL CLASS STATE VARIABLES (COUNTERS, BOOLS, ETC)
    MODE = 0  # 0 = Time | 1 = Altitude | 2 = Color # TODO - make into struct
    TRANSITION_DELAY = 4000  # Autoplay time between pictures (in milliseconds)
    IS_TRANSITION_FORWARD = True  # Autoplay direction (forward or backward)
    PLAY = True  # Autoplay (Play/Pause) bool
    IS_ACROSS_HIKES = False  # Is rotary encoder pressed down
    ROTARY_COUNT = 0  # Used exclusively for testing rotary encoder steps

    def __init__(self, win):
        # Setup the window
        self.window = win
        self.window.title("Capra Slideshow")
        self.window.geometry("1280x720")
        # self.window.geometry("720x1280")
        self.window.configure(background='purple')
        self.canvas = Canvas(root,
                             width=1280,
                             height=720,
                             background="#888",
                             highlightthickness=0)
        # self.canvas.configure(bg='#444')
        self.canvas.pack(expand='yes', fill='both')

        # Hardware control events
        self.window.bind(
            GPIO.add_event_detect(clk,
                                  GPIO.BOTH,
                                  callback=self.detected_rotary_change))
        self.clkLastState = GPIO.input(clk)

        # Using GPIO.BOTH and GPIO input state, however I have noticed glitches. This will need to be ironed out
        # May need to use an after loop
        self.rotary_button_state = GPIO.input(rotary_button)
        self.rotary_button_state = not self.rotary_button_state  # default is True, so set it to False
        self.window.bind(
            GPIO.add_event_detect(rotary_button,
                                  GPIO.BOTH,
                                  callback=self.rotary_button_pressed))

        self.window.bind(
            GPIO.add_event_detect(BUTTON_PLAY_PAUSE,
                                  GPIO.FALLING,
                                  callback=self.button_pressed_play_pause))
        self.window.bind(
            GPIO.add_event_detect(BUTTON_NEXT,
                                  GPIO.FALLING,
                                  callback=self.button_pressed_next))
        self.window.bind(
            GPIO.add_event_detect(BUTTON_PREVIOUS,
                                  GPIO.FALLING,
                                  callback=self.button_pressed_previous))

        # self.window.bind(GPIO.add_event_detect(SLIDER_SWITCH_MODE_0, GPIO.FALLING, callback=self.switch_mode_0))
        # self.window.bind(GPIO.add_event_detect(SLIDER_SWITCH_MODE_1, GPIO.BOTH, callback=self.switch_mode_1))
        # self.window.bind(GPIO.add_event_detect(SLIDER_SWITCH_MODE_2, GPIO.FALLING, callback=self.switch_mode_2))

        self.determine_switch_mode()
        self.window.bind(
            GPIO.add_event_detect(BUTTON_MODE,
                                  GPIO.FALLING,
                                  callback=self.button_mode))

        # Initialization for database implementation
        self.sql_controller = SQLController(database=DB)
        self.picture_starter = self.sql_controller.get_first_time_picture_in_hike(
            10)
        self.picture = self.sql_controller.next_time_picture_in_hike(
            self.picture_starter)

        # Initialization for images and associated properties
        self.alpha = 0

        # Initialize current and next images
        self.current_raw_top = Image.open(
            self._build_filename(self.picture_starter.camera1), 'r')
        self.next_raw_top = Image.open(
            self._build_filename(self.picture.camera1), 'r')
        self.current_raw_mid = Image.open(
            self._build_filename(self.picture_starter.camera2), 'r')
        self.next_raw_mid = Image.open(
            self._build_filename(self.picture.camera2), 'r')
        self.current_raw_bot = Image.open(blank_path, 'r')
        self.next_raw_bot = Image.open(blank_path, 'r')

        # Display the first 3 images to the screen
        self.display_photo_image_top = ImageTk.PhotoImage(self.current_raw_top)
        self.image_label_top = Label(master=self.canvas,
                                     image=self.display_photo_image_top,
                                     borderwidth=0)
        self.image_label_top.pack(side='right', fill='both', expand='yes')
        # self.image_label_top.place(x=20, rely=0.0, anchor='nw')

        self.display_photo_image_mid = ImageTk.PhotoImage(self.current_raw_mid)
        self.image_label_mid = Label(master=self.canvas,
                                     image=self.display_photo_image_mid,
                                     borderwidth=0)
        self.image_label_mid.pack(side='right', fill='both', expand='yes')
        # self.image_label_mid.place(x=20, y=405, anchor='nw')

        self.display_photo_image_bot = ImageTk.PhotoImage(self.current_raw_bot)
        self.image_label_bot = Label(master=self.canvas,
                                     image=self.display_photo_image_bot,
                                     borderwidth=0)
        self.image_label_bot.pack(side='right', fill='both', expand='yes')
        # self.image_label_bot.place(x=20, y=810, anchor='nw')

        # Hike labels
        self.label_mode = Label(self.canvas, text='Modes: ')
        self.label_hike = Label(self.canvas, text='Hike: ')
        self.label_index = Label(self.canvas, text='Index: ')
        self.label_alt = Label(self.canvas, text='Altitude: ')
        self.label_date = Label(self.canvas, text='Date: ')

        self.label_mode.place(relx=1.0, y=0, anchor='ne')
        self.label_hike.place(relx=1.0, y=22, anchor='ne')
        self.label_index.place(relx=1.0, y=44, anchor='ne')
        self.label_alt.place(relx=1.0, y=66, anchor='ne')
        self.label_date.place(relx=1.0, y=88, anchor='ne')

        # Start background threads which will continue for life of the class
        root.after(0, func=self.check_accelerometer)
        root.after(10, func=self.update_text)
        root.after(15, func=self.fade_image)
        root.after(self.TRANSITION_DELAY, func=self.auto_play_slideshow)
        # root.after(0, func=self.check_mode)

    def _build_next_raw_images(self, next_picture: Picture):
        # print('build images')
        self.next_raw_top = Image.open(
            self._build_filename(next_picture.camera1), 'r')
        self.next_raw_mid = Image.open(
            self._build_filename(next_picture.camera2), 'r')
        # self.next_raw_bot = Image.open(blank_path, 'r')

    def _build_filename(self, end_of_path: str) -> str:
        # return '{p}{e}'.format(p=PATH, e=end_of_path)
        return '{e}'.format(p=PATH, e=end_of_path)

    # Loops for the life of the program, fading between the current image and the NEXT image
    def fade_image(self):
        # print('Fading the image at alpha of: ', self.alpha)
        # print(time.time())
        if self.alpha < 1.0:
            # Top image
            self.current_raw_top = Image.blend(self.current_raw_top,
                                               self.next_raw_top, self.alpha)
            # self.current_raw_top = self.next_raw_top
            self.display_photo_image_top = ImageTk.PhotoImage(
                self.current_raw_top)
            self.image_label_top.configure(image=self.display_photo_image_top)

            # Middle image
            self.current_raw_mid = Image.blend(self.current_raw_mid,
                                               self.next_raw_mid, self.alpha)
            # self.current_raw_mid = self.next_raw_mid
            self.display_photo_image_mid = ImageTk.PhotoImage(
                self.current_raw_mid)
            self.image_label_mid.configure(image=self.display_photo_image_mid)

            # Bottom image
            self.current_raw_bot = Image.blend(self.current_raw_bot,
                                               self.next_raw_bot, self.alpha)
            # self.current_raw_bot = self.next_raw_bot
            self.display_photo_image_bot = ImageTk.PhotoImage(
                self.current_raw_bot)
            self.image_label_bot.configure(image=self.display_photo_image_bot)

            self.alpha = self.alpha + 0.0417
            # self.alpha = self.alpha + 0.0209
        root.after(83, self.fade_image)

    def update_text(self):
        self.determine_switch_mode()
        if self.MODE == 0:
            mode = 'Mode: Time'
        elif self.MODE == 1:
            mode = 'Mode: Altitude'
        elif self.MODE == 2:
            mode = 'Mode: Color'
        else:
            mode = 'Mode: ERROR'

        hike = 'Hike {n}'.format(n=self.picture.hike_id)

        hike_sz = self.sql_controller.get_size_of_hike(self.picture)
        index = '{x} / {n}'.format(x=self.picture.index_in_hike, n=hike_sz)

        altitude = '{a}m'.format(a=self.picture.altitude)

        value = datetime.datetime.fromtimestamp(self.picture.time)
        date_time = value.strftime('%-I:%M:%S%p on %d %b, %Y')
        date = '{d}'.format(d=date_time)

        self.label_mode.configure(text=mode)
        self.label_hike.configure(text=hike)
        self.label_index.configure(text=index)
        self.label_alt.configure(text=altitude)
        self.label_date.configure(text=date)

        root.after(500, self.update_text)

    # TODO - track down where the random number being printed to the terminal is coming from
    def auto_play_slideshow(self):
        # print('Auto incremented slideshow')
        if (self.PLAY):
            if self.IS_TRANSITION_FORWARD:  # Advance forward on autoplay
                self.picture = self.sql_controller.get_next_picture(
                    current_picture=self.picture,
                    mode=self.MODE,
                    is_across_hikes=self.IS_ACROSS_HIKES)
                self._build_next_raw_images(self.picture)
                self.alpha = .2
                self.picture.print_obj()  # This is a print()
            else:  # Advance backward on autoplay
                self.picture = self.sql_controller.get_previous_picture(
                    current_picture=self.picture,
                    mode=self.MODE,
                    is_across_hikes=self.IS_ACROSS_HIKES)
                self._build_next_raw_images(self.picture)
                self.alpha = .2
                self.picture.print_obj()  # This is a print()

        root.after(self.TRANSITION_DELAY, self.auto_play_slideshow)

    # BCM HARDWARE CONTROLS
    def detected_rotary_change(self, event):
        clkState = GPIO.input(clk)
        cntState = GPIO.input(cnt)

        # The encoder has moved
        if clkState != self.clkLastState:
            self.determine_switch_mode()
            # Increment
            if cntState != clkState:
                # Next picture
                self.IS_TRANSITION_FORWARD = True  # For auto slideshow
                self.ROTARY_COUNT += 1
                print("Rotary +: ", self.ROTARY_COUNT)

                self.picture = self.sql_controller.get_next_picture(
                    current_picture=self.picture,
                    mode=self.MODE,
                    is_across_hikes=self.IS_ACROSS_HIKES)
                self._build_next_raw_images(self.picture)
                self.alpha = .2  # Resets amount of fade between pictures
                self.picture.print_obj()  # This is a print()
            # Decrement
            else:
                # Previous picture
                self.IS_TRANSITION_FORWARD = False  # For auto slideshow
                self.ROTARY_COUNT -= 1
                print("Rotary -: ", self.ROTARY_COUNT)

                self.picture = self.sql_controller.get_previous_picture(
                    current_picture=self.picture,
                    mode=self.MODE,
                    is_across_hikes=self.IS_ACROSS_HIKES)
                self._build_next_raw_images(self.picture)
                self.alpha = .2  # Resets amount of fade between pictures
                self.picture.print_obj()  # This is a print()
        self.clkLastState = clkState
        # TODO - try around with this in or out depending on the rotary encoder
        # sleep(0.1)

    def rotary_button_pressed(self, event):
        print('rotary pressed')
        self.IS_ACROSS_HIKES = not self.IS_ACROSS_HIKES
        print('Is across hikes: {i}'.format(i=self.IS_ACROSS_HIKES))

        # Another way to detect rotary encoder button press
        # self.rotary_button_state = not self.rotary_button_state
        # print('Rotary button state: {i}'.format(i=self.rotary_button_state))

        # sleep(0.1)

    def button_pressed_play_pause(self, event):
        self.PLAY = not self.PLAY
        if self.PLAY:
            print('Pressed Play')
        else:
            print('Pressed Pause')

    def button_pressed_next(self, event):
        print('Next pressed')

    def button_pressed_previous(self, event):
        print('Previous pressed')

    def button_mode(self, event):
        # self.MODE += 1
        # self.MODE = self.MODE % 3  # to loop count back to 0 from 3
        print('Mode button not incrementing | Current mode = {n}'.format(
            n=self.MODE))

    def determine_switch_mode(self):
        switch_mode_0_state = GPIO.input(SLIDER_SWITCH_MODE_0)
        switch_mode_1_state = GPIO.input(SLIDER_SWITCH_MODE_1)
        switch_mode_2_state = GPIO.input(SLIDER_SWITCH_MODE_2)

        # print(switch_mode_0_state)
        # print(switch_mode_1_state)
        # print(switch_mode_2_state)

        if switch_mode_0_state == 0:
            self.MODE = 0
        elif switch_mode_1_state == 0:
            self.MODE = 1
        elif switch_mode_2_state == 0:
            self.MODE = 2
        else:
            print("ERROR")
        # print('MODE: {m}'.format(m=self.MODE))

    # ADC HARDWARE CONTROLS
    def check_accelerometer(self):
        ax = AnalogIn(mcp, ACCELEROMETER_X).value
        ay = AnalogIn(mcp, ACCELEROMETER_Y).value
        az = AnalogIn(mcp, ACCELEROMETER_Z).value

        pitch = 180 * math.atan(ax / math.sqrt(ay * ay + az * az)) / math.pi
        roll = 180 * math.atan(ay / math.sqrt(ax * ax + az * az)) / math.pi

        if pitch > 35 and roll < 34:
            orientation = 'correct vertical'
        elif pitch < 31 and roll > 34:
            orientation = 'upside down vertical'
        else:
            orientation = 'horizontal'
        # print(orientation)

        # TODO - calculate pitch and roll correctly
        # if roll > -45 and roll < 45:
        #    orientation = 'landscape'
        # elif roll >= 45 or roll <= -45:
        #    orientation = 'vertical'

        root.after(500, self.check_accelerometer)
예제 #14
0
 def test_get_last_altitude(self):
     sql_controller = SQLController(database=self.DB_EMPTY)
     alt = sql_controller.get_last_altitude()
     self.assertEqual(alt, 0)
예제 #15
0
def main():
    # Initialize logger that is used while device starts up
    # Once a hike is started the logger switches to a hike specific log
    initialize_startup_logger()

    # Initialize and setup hardware
    rgb_led.turn_off()
    rgb_led.turn_pink()
    piezo.play_power_on_jingle()

    pi_cam = initialize_picamera(g.CAM_RESOLUTION)  # Setup the camera
    initialize_background_turn_off()  # Setup the Off button
    initialize_background_play_pause()  # Setup Play/Pause button
    prev_pause = True

    logging.info('--------------------- POWERED ON ---------------------')
    # As long as initially paused, do not create new hike yet
    while shared.pause:
        # Check for turn off button, LOW battery, or LOW storage
        check_button_turn_off()
        check_low_battery_turn_off()
        check_low_storage_turn_off()

        if round(time.time(), 0) % 60 == 0:
            logging.info('>>>>>Another minute initially PAUSED')
        time.sleep(1)
    logging.info('>>>>>Pause button pressed --> FIRST UNPAUSE')
    rgb_led.turn_off()

    # Create SQL controller and update hike information
    sql_controller = SQLController(database=g.DB)
    timestamp = round(time.time(), 0)
    created = sql_controller.will_create_new_hike(g.NEW_HIKE_TIME, g.DIRECTORY,
                                                  timestamp)
    if created:  # new hike created: blink teal
        rgb_led.blink_teal_new_hike()
    else:  # continue last hike: blink green
        rgb_led.blink_green_continue_hike()
    time.sleep(1)
    hike_num = sql_controller.get_last_hike_id()
    photo_index = sql_controller.get_last_photo_index_of_hike(hike_num)

    # Switch to the hike specific logfile
    switch_to_hike_logger(hike_num)
    logging.info(
        '--------------------- NEW RECORDING SESSION STARTED ---------------------'
    )

    # Start the time lapse
    # --------------------------------------------------------------------------
    while (True):
        # Check for turn off button, LOW battery, or LOW storage
        check_button_turn_off()
        check_low_battery_turn_off()
        check_low_storage_turn_off()

        # Pause the program if applicable
        while shared.pause:
            if not prev_pause:
                logging.info('>PAUSED<')
                prev_pause = True
                rgb_led.turn_pink()
                piezo.play_paused_jingle()

            # Check for turn off button, LOW battery, or LOW storage
            check_button_turn_off()
            check_low_battery_turn_off()
            check_low_storage_turn_off()

            if round(time.time(), 0) % 60 == 0:
                logging.info('>>>>> + 1 minute PAUSED')
            time.sleep(1)
        # Unpause program
        if prev_pause:
            logging.info('>UNPAUSED<')
            prev_pause = False
            rgb_led.turn_off()
            piezo.play_start_recording_jingle()

        # Read the time as UNIX timestamp
        timestamp = round(time.time(), 0)
        logging.info('Unix Timestamp: {t}'.format(t=timestamp))

        # New picture: increment photo index & add row to database
        photo_index += 1

        # Get altitude before a new entry is added to the database
        altitude = query_altimeter(sql_controller)

        sql_controller.create_new_picture(hike_num, photo_index, timestamp)

        # Take pictures
        camcapture(pi_cam, 1, hike_num, photo_index, sql_controller)
        camcapture(pi_cam, 2, hike_num, photo_index, sql_controller)
        camcapture(pi_cam, 3, hike_num, photo_index, sql_controller)

        # Update the database with metadata for picture & hike
        sql_controller.set_picture_altitude(altitude, hike_num, photo_index)
        sql_controller.set_hike_endtime_picture_count(timestamp, photo_index,
                                                      hike_num)

        # Blink to notify that the timelapse is still going
        rgb_led.blink_green_new_picture()

        # Log on every 5th picture
        if photo_index % 5 == 0:
            logging.info('Cameras still alive (5 pictures taken)')

        # Wait until 5 seconds have passed since starting to take the pictures
        while time.time() < timestamp + g.CAM_INTERVAL:
            pass
예제 #16
0
class DatabaseMay2021Tests(unittest.TestCase):
    DB = 'tests/capra_projector_may2021.db'
    directory = 'capra-storage'
    sql_controller = None
    sql_statements = None
    picture = None

    @classmethod
    def setUpClass(self):
        # NOTE - if the database or id is changed, all the tests will break
        # They are dependent upon that
        self.sql_controller = SQLController(database=self.DB,
                                            directory=self.directory)
        self.picture = self.sql_controller.get_picture_with_id(11994)

        # For testing directly on the sql statements
        self.sql_statements = SQLStatements()

    def test_get_picture_with_id(self):
        self.assertEqual(self.picture.picture_id, 11994)
        self.assertEqual(self.picture.hike_id, 10)
        self.assertEqual(self.picture.altitude, 2123.5)

    # 20,284 total pictures in database
    # x  .05
    #  1,014.2  ->  rounds to 1,015 skip in the global ranks

    def test_get_next_color_skip_in_global(self):
        self.assertEqual(self.picture.picture_id, 11994)
        self.assertEqual(self.picture.colorrank_global, 10549)
        # id        color_rank_global
        # 11994     10549 + 1015
        pic = self.sql_controller.get_next_color_skip_in_global(self.picture)
        self.assertEqual(pic.colorrank_global, 11564)
        self.assertEqual(pic.picture_id, 1398)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 12579)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 13594)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 19684)
        self.assertEqual(pic.picture_id, 30037)
        # wrap around
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 415)
        self.assertEqual(pic.picture_id, 8493)

        # 4146      20284
        pic = self.sql_controller.get_picture_with_id(4146)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 1015)
        self.assertEqual(pic.picture_id, 10321)

        # 4887     19270
        pic = self.sql_controller.get_picture_with_id(4887)
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 1)
        self.assertEqual(pic.picture_id, 1102)

    def test_get_previous_color_skip_in_global(self):
        self.assertEqual(self.picture.picture_id, 11994)
        self.assertEqual(self.picture.colorrank_global, 10549)
        # id        color_rank_global
        # 11994     10549 - 1015
        pic = self.sql_controller.get_previous_color_skip_in_global(
            self.picture)
        self.assertEqual(pic.colorrank_global, 9534)
        self.assertEqual(pic.picture_id, 29239)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 5474)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 2429)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 399)
        # wrap back around
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 19668)
        self.assertEqual(pic.picture_id, 25289)

        # 8388      310
        pic = self.sql_controller.get_picture_with_id(8388)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 19579)
        self.assertEqual(pic.picture_id, 6668)

        # 1102      1
        pic = self.sql_controller.get_picture_with_id(1102)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 19270)
        self.assertEqual(pic.picture_id, 4887)

        # 10321     1015
        pic = self.sql_controller.get_picture_with_id(10321)
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 20284)
        self.assertEqual(pic.picture_id, 4146)
예제 #17
0
class DatabaseOriginal4Tests(unittest.TestCase):
    DB = 'tests/capra_projector_jan2021_min_test.db'  # no / infront makes the path relative
    directory = 'capra-storage'
    sql_controller = None
    sql_statements = None
    picture = None

    @classmethod
    def setUpClass(self):
        # NOTE - if the database or id is changed, all the tests will break
        # They are dependent upon that
        self.sql_controller = SQLController(database=self.DB,
                                            directory=self.directory)
        self.picture = self.sql_controller.get_picture_with_id(1994)

        # For testing directly on the sql statements
        self.sql_statements = SQLStatements()

    def test_get_picture_with_id(self):
        self.assertEqual(self.picture.picture_id, 1994)
        self.assertEqual(self.picture.altitude, 936.62)

    def test_picture_object(self):
        picture = self.sql_controller.get_picture_with_id(1994)

        self.assertEqual(picture.picture_id, 1994)
        self.assertEqual(picture.time, 1556391512.0)
        self.assertEqual(picture.year, 2019)
        self.assertEqual(picture.month, 4)
        self.assertEqual(picture.day, 27)
        self.assertEqual(picture.minute, 718)
        self.assertEqual(picture.dayofweek, 5)

        self.assertEqual(picture.hike_id, 3)
        self.assertEqual(picture.index_in_hike, 878)

        self.assertEqual(picture.altitude, 936.62)
        self.assertEqual(picture.altrank_hike, 855)
        self.assertEqual(picture.altrank_global, 2355)
        self.assertEqual(picture.altrank_global_h, 1970)

        hsvColor = QColor()
        hsvColor.setHsv(91, 18, 220)
        self.assertEqual(picture.color_hsv, hsvColor)
        rgbColor = QColor(204, 219, 220)
        self.assertEqual(picture.color_rgb, rgbColor)
        self.assertEqual(picture.colorrank_hike, 1776)
        self.assertEqual(picture.colorrank_global, 2785)
        self.assertEqual(picture.colorrank_global_h, 2891)
        self.assertEqual(picture.colors_count, 4)
        colorList = [
            QColor(204, 219, 220),
            QColor(52, 46, 17),
            QColor(122, 117, 75),
            QColor(17, 17, 15)
        ]
        self.assertEqual(picture.colors_rgb, colorList)
        self.assertEqual(picture.colors_conf, [0.22, 0.18, 0.16, 0.11])

        self.assertEqual(picture.camera1,
                         '/media/pi/capra-hd/hike3/878_cam1.jpg')
        self.assertEqual(picture.camera2,
                         '/media/pi/capra-hd/hike3/878_cam2.jpg')
        self.assertEqual(picture.camera3,
                         '/media/pi/capra-hd/hike3/878_cam3.jpg')
        self.assertEqual(picture.cameraf,
                         '/media/pi/capra-hd/hike3/878_cam2f.jpg')
        self.assertEqual(picture.created, '2021-02-16 09:39:15')
        self.assertEqual(picture.updated, '2021-02-16 09:39:15')

    # Time
    # --------------------------------------------------------------------------

    def test_get_next_time_in_hikes(self):
        # next picture by 1
        pic = self.sql_controller.get_next_time_in_hikes(self.picture, 1)
        self.assertEqual(pic.time, 1556391516.0)
        self.assertEqual(pic.picture_id, 1995)

        # next picture by 94
        pic = self.sql_controller.get_next_time_in_hikes(self.picture, 94)
        self.assertEqual(pic.time, 1556391888.0)
        self.assertEqual(pic.picture_id, 2088)

        # next picture by enough to wrap it around
        pic = self.sql_controller.get_next_time_in_hikes(self.picture, 1674)
        self.assertEqual(pic.time, 1556323240.0)
        self.assertEqual(pic.picture_id, 10)

        # offset is more than total list size, but is below the row
        pic = self.sql_controller.get_next_time_in_hikes(
            self.picture, 27 + 3658)
        self.assertEqual(pic.time, 1556391620.0)
        self.assertEqual(pic.picture_id, 2021)

        # offset is more than total list size, but is above the row
        pic = self.sql_controller.get_next_time_in_hikes(
            self.picture, 1675 + 3658)
        self.assertEqual(pic.time, 1556323244.0)
        self.assertEqual(pic.picture_id, 11)

    def test_get_previous_time_in_hikes(self):
        # previous picture by 1
        pic = self.sql_controller.get_previous_time_in_hikes(self.picture, 1)
        self.assertEqual(pic.time, 1556391508.0)
        self.assertEqual(pic.picture_id, 1993)

        # previous picture by 94
        pic = self.sql_controller.get_previous_time_in_hikes(self.picture, 94)
        self.assertEqual(pic.time, 1556391136.0)
        self.assertEqual(pic.picture_id, 1900)

        # previous picture by enough to wrap it around
        pic = self.sql_controller.get_previous_time_in_hikes(
            self.picture, 1994)
        self.assertEqual(pic.time, 1556400336.0)
        self.assertEqual(pic.picture_id, 3658)

        pic = self.sql_controller.get_previous_time_in_hikes(
            self.picture, 2021)
        self.assertEqual(pic.time, 1556400228.0)
        self.assertEqual(pic.picture_id, 3631)

        # offset is more than total list size, but is above the row
        pic = self.sql_controller.get_previous_time_in_hikes(
            self.picture, 11 + 3658)
        self.assertEqual(pic.time, 1556391468.0)
        self.assertEqual(pic.picture_id, 1983)

        # offset is more than total list size, but is below the row
        pic = self.sql_controller.get_previous_time_in_hikes(
            self.picture, 2000 + 3658)
        self.assertEqual(pic.time, 1556400312.0)
        self.assertEqual(pic.picture_id, 3652)

    def test_get_next_time_in_global(self):
        # in minute
        pic = self.sql_controller.get_next_time_in_global(self.picture, 1)
        self.assertEqual(pic.picture_id, 1995)

        pic = self.sql_controller.get_next_time_in_global(self.picture, 6)
        self.assertEqual(pic.picture_id, 2000)

        # below
        pic = self.sql_controller.get_next_time_in_global(self.picture, 7)
        self.assertEqual(pic.picture_id, 2001)

        pic = self.sql_controller.get_next_time_in_global(self.picture, 2556)
        self.assertEqual(pic.picture_id, 892)

        # wrap around
        pic = self.sql_controller.get_next_time_in_global(self.picture, 2557)
        self.assertEqual(pic.picture_id, 893)

        pic = self.sql_controller.get_next_time_in_global(self.picture, 3657)
        self.assertEqual(pic.picture_id, 1993)

        # moding below
        pic = self.sql_controller.get_next_time_in_global(self.picture, 3659)
        self.assertEqual(pic.picture_id, 1995)

        pic = self.sql_controller.get_next_time_in_global(self.picture, 3668)
        self.assertEqual(pic.picture_id, 2004)

        # moding above
        pic = self.sql_controller.get_next_time_in_global(self.picture, 6216)
        self.assertEqual(pic.picture_id, 894)

        pic = self.sql_controller.get_next_time_in_global(self.picture, 6714)
        self.assertEqual(pic.picture_id, 1392)

    def test_get_previous_time_in_global(self):
        # in minute
        pic = self.sql_controller.get_previous_time_in_global(self.picture, 1)
        self.assertEqual(pic.picture_id, 1993)

        pic = self.sql_controller.get_previous_time_in_global(self.picture, 8)
        self.assertEqual(pic.picture_id, 1986)

        # above
        pic = self.sql_controller.get_previous_time_in_global(self.picture, 9)
        self.assertEqual(pic.picture_id, 1985)

        pic = self.sql_controller.get_previous_time_in_global(
            self.picture, 834)
        self.assertEqual(pic.picture_id, 1160)

        pic = self.sql_controller.get_previous_time_in_global(
            self.picture, 1101)
        self.assertEqual(pic.picture_id, 893)

        # wrap around above
        pic = self.sql_controller.get_previous_time_in_global(
            self.picture, 1102)
        self.assertEqual(pic.picture_id, 892)

        # wrap around below
        pic = self.sql_controller.get_previous_time_in_global(
            self.picture, 1800)
        self.assertEqual(pic.picture_id, 194)

        # moding above
        pic = self.sql_controller.get_previous_time_in_global(
            self.picture, 3659)
        self.assertEqual(pic.picture_id, 1993)

        pic = self.sql_controller.get_previous_time_in_global(
            self.picture, 3668)
        self.assertEqual(pic.picture_id, 1984)

        # moding below
        pic = self.sql_controller.get_previous_time_in_global(
            self.picture, 7116)
        self.assertEqual(pic.picture_id, 2194)

    def test_get_next_time_skip_in_hikes(self):
        # id	time		hike	index_in_hike	(hike | index) = id
        # 1566	1556389800	3		450			--> (4 | 80) = 3354
        # (451/2158) * 385 = 80.46 (ceiling then subtract 1)
        pic = self.sql_controller.get_picture_with_id(1566)
        pic = self.sql_controller.get_next_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3354)

        # wrap around
        # 3354	1556399120	4		80			--> (1 | 187) = 188
        # (81/385) * 892 = 187.66
        pic = self.sql_controller.get_next_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 188)

        # 188   1556323952  1       187         --> (2 | 46) = 939
        # (188/892) * 223 = 47.0
        pic = self.sql_controller.get_next_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 939)

        # 939   1556377384  2       46          --> (3 | 454) = 1570
        # (47/223) * 2158 = 454.8
        pic = self.sql_controller.get_next_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1570)

        # 3000  1556395536  3       1884        --> (4 | 336) = 3610
        # (1885/2158) * 385 = 336.29
        pic = self.sql_controller.get_picture_with_id(3000)
        pic = self.sql_controller.get_next_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3610)

    def test_get_previous_time_skip_in_hikes(self):
        # id	time		hike	index_in_hike   (hike | index) = id
        # 1566	1556389800	3		450	        --> (2 | 46) = 939
        # (451/2158) * 223 = 46.6
        pic = self.sql_controller.get_picture_with_id(1566)
        pic = self.sql_controller.get_previous_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 939)

        # 939	1556377384	2		46		    --> (1 | 187) = 188
        # (47/223) * 892 = 188.0
        pic = self.sql_controller.get_previous_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 188)

        # 188	1556323952	1		187		    --> (4 | 81) = 3355
        # (188/892) * 385	= 81.14
        pic = self.sql_controller.get_previous_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3355)

        # 3654  1556400320  4       380        --> (3 | 2135) = 3251
        # (381/385) * 2158 = 2135.58
        pic = self.sql_controller.get_picture_with_id(3654)
        pic = self.sql_controller.get_previous_time_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3251)

    def test_get_next_time_skip_in_global(self):
        # id    time        minute      id
        # 1994  1556391512  718 -> 733  2219
        # 2000  1556391536  718	-> 733  2225
        pic = self.sql_controller.get_next_time_skip_in_global(self.picture)
        self.assertEqual(pic.picture_id, 2219)

        pic = self.sql_controller.get_picture_with_id(2000)
        pic = self.sql_controller.get_next_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 2225)

        # id    time        minute          id
        # 650   1556325800  1063 -> 1078    875
        # 688   1556325952  1065 -> 1080    906
        # 872   1556326688  1078 -> 493    1090
        # 885   1556326740  1079 -> 494    1103
        pic = self.sql_controller.get_picture_with_id(650)
        pic = self.sql_controller.get_next_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 875)

        pic = self.sql_controller.get_picture_with_id(688)
        pic = self.sql_controller.get_next_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 906)

        pic = self.sql_controller.get_picture_with_id(872)
        pic = self.sql_controller.get_next_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 1090)

        pic = self.sql_controller.get_picture_with_id(885)
        pic = self.sql_controller.get_next_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 1103)

    def test_get_previous_time_skip_in_global(self):
        # id    time        minute          id
        # 1994  1556391512  718 -> 703      1769
        # 1769  1556390612  703 -> 688      1544
        pic = self.sql_controller.get_previous_time_skip_in_global(
            self.picture)
        self.assertEqual(pic.picture_id, 1769)

        pic = self.sql_controller.get_previous_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 1544)

        # id    time        minute          id
        # 1135  1556388076  661 -> 481      912
        # 912   1556377276  481 -> 1066     694
        pic = self.sql_controller.get_picture_with_id(1135)
        pic = self.sql_controller.get_previous_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 912)

        # wrap arounds
        pic = self.sql_controller.get_previous_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 694)

        # 1050  1556377828  490 -> 1075     832
        pic = self.sql_controller.get_picture_with_id(1056)
        pic = self.sql_controller.get_previous_time_skip_in_global(pic)
        self.assertEqual(pic.picture_id, 838)

    # Altitude
    # --------------------------------------------------------------------------
    def test_get_next_altitude_in_hikes(self):
        # forward by 1
        pic = self.sql_controller.get_next_altitude_in_hikes(self.picture, 1)
        self.assertEqual(pic.altrank_global_h, 1971)

        # forward by 94
        pic = self.sql_controller.get_next_altitude_in_hikes(pic, 94)
        self.assertEqual(pic.altrank_global_h, 2065)

        # wrap to top of list
        pic = self.sql_controller.get_picture_with_id(3274)
        pic = self.sql_controller.get_next_altitude_in_hikes(pic, 2)
        self.assertEqual(pic.altrank_global_h, 1)

        # mod to row below
        pic = self.sql_controller.get_next_altitude_in_hikes(
            self.picture, 6 + 3658)
        self.assertEqual(pic.picture_id, 1987)

        # mod to row above
        pic = self.sql_controller.get_next_altitude_in_hikes(
            self.picture, 3600 + 3658)
        self.assertEqual(pic.picture_id, 1912)

    def test_get_previous_altitude_in_hikes(self):
        # backward by 1
        pic = self.sql_controller.get_previous_altitude_in_hikes(
            self.picture, 1)
        self.assertEqual(pic.altrank_global_h, 1969)

        # backward by 94
        pic = self.sql_controller.get_previous_altitude_in_hikes(
            self.picture, 94)
        self.assertEqual(pic.altrank_global_h, 1876)

        # wrap to bottom of list
        pic = self.sql_controller.get_picture_with_id(592)
        pic = self.sql_controller.get_previous_altitude_in_hikes(pic, 6)
        self.assertEqual(pic.picture_id, 3276)

        # mod to row above
        pic = self.sql_controller.get_previous_altitude_in_hikes(
            self.picture, 10 + 3658)
        self.assertEqual(pic.altrank_global_h, 1960)

        # mod to row below
        pic = self.sql_controller.get_previous_altitude_in_hikes(
            self.picture, 2000 + 3658)
        self.assertEqual(pic.altrank_global_h, 3628)

    def test_get_next_altitude_in_global(self):
        # forward by 1
        pic = self.sql_controller.get_next_altitude_in_global(self.picture, 1)
        self.assertEqual(pic.altrank_global, 2356)

        # forward by 3
        pic = self.sql_controller.get_next_altitude_in_global(self.picture, 3)
        self.assertEqual(pic.altrank_global, 2358)

        # wrap to top of list
        pic = self.sql_controller.get_picture_with_id(2976)
        pic = self.sql_controller.get_next_altitude_in_global(pic, 1)
        self.assertEqual(pic.picture_id, 524)

        # mod to row below
        pic = self.sql_controller.get_next_altitude_in_global(
            self.picture, 100 + 3658)
        self.assertEqual(pic.altrank_global, 2455)

        # mod to row above
        pic = self.sql_controller.get_next_altitude_in_global(
            self.picture, 2000 + 3658)
        self.assertEqual(pic.altrank_global, 697)

    def test_get_previous_altitude_in_global(self):
        # previous by 1
        pic = self.sql_controller.get_previous_altitude_in_global(
            self.picture, 1)
        self.assertEqual(pic.altrank_global, 2354)

        # previous by 3
        pic = self.sql_controller.get_previous_altitude_in_global(
            self.picture, 3)
        self.assertEqual(pic.altrank_global, 2352)

        # wrap to bottom of list
        pic = self.sql_controller.get_picture_with_id(544)
        pic = self.sql_controller.get_previous_altitude_in_global(pic, 3)
        self.assertEqual(pic.picture_id, 3032)

        # mod to row above
        pic = self.sql_controller.get_previous_altitude_in_global(
            self.picture, 100 + 3658)
        self.assertEqual(pic.altrank_global, 2255)

        # mod to row below
        pic = self.sql_controller.get_previous_altitude_in_global(
            self.picture, 2360 + 3658)
        self.assertEqual(pic.altrank_global, 3653)

    def test_get_next_altitude_skip_in_hikes(self):
        # id	time		hike	alt_rank_hike	id (hike | alt_rank_hike)
        # wrap around
        # 1994	1556391512	3		855				--> 707 (1 | 354)
        # (855/2158) * 892 = 353.41
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(self.picture)
        self.assertEqual(pic.picture_id, 707)

        # 1094	1556378004	2		47				--> 3577 (4 | 82)
        # (47/223) * 385 = 81.14
        pic = self.sql_controller.get_picture_with_id(1094)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3577)

        # 34	1556323336	1		800				--> 938 (2 | 200)
        # (800/892) * 223 = 200.0
        pic = self.sql_controller.get_picture_with_id(34)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 938)

        # Testing the lower edge
        # ---------------------------------
        # 524   1556325296  1       1               --> 1048 (2 | 1)
        # (1/892) * 223 = 0.25
        pic = self.sql_controller.get_picture_with_id(524)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1048)

        # 563   1556325452  1       3               --> 1048 (2 | 1)
        # (3/892) * 223 = 0.75
        pic = self.sql_controller.get_picture_with_id(563)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1048)

        # 592   1556325568  1       4               --> 1048 (2 | 1)
        # (4/892) * 223 = 1.0
        pic = self.sql_controller.get_picture_with_id(592)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1048)

        # 594   1556325576  1       5               --> 1078 (2 | 2)
        # (5/892) * 223 = 1.25
        pic = self.sql_controller.get_picture_with_id(594)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1078)

        # 595   1556325580  1       6               --> 1078 (2 | 2)
        # (6/892) * 223 = 1.5
        pic = self.sql_controller.get_picture_with_id(595)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1078)

        # 2    1556323208  1       891             --> 912 (2 | 223)
        # (891/892) * 223 = 222.75
        pic = self.sql_controller.get_picture_with_id(2)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 912)

        # 3658  1556400336  4       1               --> 1124 (3 | 6)
        # (1/384) * 2158 = 5.62
        pic = self.sql_controller.get_picture_with_id(3658)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1125)

        # 3274	1556398800	4		384				--> 3033 (3 | 2153)
        # (384/385) * 2158 = 2152.39
        pic = self.sql_controller.get_picture_with_id(3274)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3033)

        # 3275	1556398804	4		385				--> 2976 (3 | 2158)
        # (385/385) * 2158 = 2158.0
        pic = self.sql_controller.get_picture_with_id(3275)
        pic = self.sql_controller.get_next_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 2976)

    def test_get_previous_altitude_skip_in_hikes(self):
        # id	time		hike	alt_rank_hike	id (hike | alt_rank_hike)

        # wrap back to hike 3
        # -------------------
        # 619	1556325676	1		26				--> 1228 (3 | 63)
        # (26/892) * 2158 = 62.9
        pic = self.sql_controller.get_picture_with_id(619)
        pic = self.sql_controller.get_previous_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1228)

        # 2	    1556323208	1		891				--> 3036 (3 | 2156)
        # (891/892) * 2158 = 2155.58
        pic = self.sql_controller.get_picture_with_id(2)
        pic = self.sql_controller.get_previous_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3036)

        # -------------------
        # 1017	1556377696	2		210				--> 49 (1 | 840)
        # (210/223) * 892 = 840.0
        pic = self.sql_controller.get_picture_with_id(1017)
        pic = self.sql_controller.get_previous_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 49)

        # 1994	1556391512	3		855				--> 3507 (4 | 153)
        # (855/2158) * 385 = 152.537
        pic = self.sql_controller.get_previous_altitude_skip_in_hikes(
            self.picture)
        self.assertEqual(pic.picture_id, 3507)

        # 1127	1556388044	3		10				--> 3657 (4 | 2)
        # (10/2158) * 223 = 1.03
        pic = self.sql_controller.get_picture_with_id(1127)
        pic = self.sql_controller.get_previous_altitude_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3657)

    def test_get_next_altitude_skip_in_global(self):
        # 1994  |   2355 + (3658 * .05) = 2537.9
        pic = self.sql_controller.get_next_altitude_skip_in_global(
            self.picture)
        self.assertEqual(pic.altrank_global, 2538)
        self.assertEqual(pic.picture_id, 2207)

        # 524   |   1 + (3658 * .05) = 183.9
        pic = self.sql_controller.get_picture_with_id(524)
        pic = self.sql_controller.get_next_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 184)
        self.assertEqual(pic.picture_id, 514)  # 183

        # 3006  | 3475 + (3658 * .05) = 3657.9
        pic = self.sql_controller.get_picture_with_id(3006)
        pic = self.sql_controller.get_next_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 3658)

        # wrap around bottom to the top
        # ------------------------------------
        # 3059  | 3476 + (3658 * .05) = 3658.9
        pic = self.sql_controller.get_picture_with_id(3059)
        pic = self.sql_controller.get_next_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 1)

        # 2973  | 3600 + (3658 * .05) = 3782.9
        pic = self.sql_controller.get_picture_with_id(2973)
        pic = self.sql_controller.get_next_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 125)
        self.assertEqual(pic.picture_id, 455)

    def test_get_previous_altitude_skip_in_global(self):
        # 1994  |   2355 - (3658 * .05)
        # 2355 - 183 = 2172
        pic = self.sql_controller.get_previous_altitude_skip_in_global(
            self.picture)
        self.assertEqual(pic.altrank_global, 2172)

        # 3489  | 1800 - (3658 * .05)
        # 1800 - 183 = 1617
        pic = self.sql_controller.get_picture_with_id(3489)
        pic = self.sql_controller.get_previous_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 1617)

        # 2976   |   3658 - (3658 * .05)
        # 3658 - 183 = 3475
        pic = self.sql_controller.get_picture_with_id(2976)
        pic = self.sql_controller.get_previous_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 3475)
        self.assertEqual(pic.picture_id, 3006)

        # wrap around from top to the bottom
        # ------------------------------------
        # 613   |   20 - (3658 * .05) + 3658
        # 20 - 183 + 3658 = 3495
        pic = self.sql_controller.get_picture_with_id(613)  # 20 rank
        pic = self.sql_controller.get_previous_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 3495)
        self.assertEqual(pic.picture_id, 3056)

        # 513   |   183 - (3658 * .05) + 3658
        # 183 - 183 + 3658 = 3658
        pic = self.sql_controller.get_picture_with_id(513)
        pic = self.sql_controller.get_previous_altitude_skip_in_global(pic)
        self.assertEqual(pic.altrank_global, 3658)
        self.assertEqual(pic.picture_id, 2976)

    # Color
    # --------------------------------------------------------------------------
    def test_get_next_color_in_hikes(self):
        # forward by 1
        pic = self.sql_controller.get_next_color_in_hikes(self.picture, 1)
        self.assertEqual(pic.colorrank_global_h, 2892)

        # forward by 2
        pic = self.sql_controller.get_next_color_in_hikes(self.picture, 2)
        self.assertEqual(pic.colorrank_global_h, 2893)

        # wrap to top of list
        pic = self.sql_controller.get_picture_with_id(3369)
        pic = self.sql_controller.get_next_color_in_hikes(pic, 8)
        self.assertEqual(pic.picture_id, 883)

        # mod to row below
        pic = self.sql_controller.get_next_color_in_hikes(
            self.picture, 9 + 3658)
        self.assertEqual(pic.colorrank_global_h, 2900)

        # mod to row above
        pic = self.sql_controller.get_next_color_in_hikes(
            self.picture, 800 + 3658)
        self.assertEqual(pic.colorrank_global_h, 33)

    def test_get_previous_color_in_hikes(self):
        # backward by 1
        pic = self.sql_controller.get_previous_color_in_hikes(self.picture, 1)
        self.assertEqual(pic.colorrank_global_h, 2890)

        # backward by 5
        pic = self.sql_controller.get_previous_color_in_hikes(self.picture, 5)
        self.assertEqual(pic.colorrank_global_h, 2886)

        # wrap to bottom of list
        pic = self.sql_controller.get_picture_with_id(341)
        pic = self.sql_controller.get_previous_color_in_hikes(pic, 25)
        self.assertEqual(pic.picture_id, 3482)
        self.assertEqual(pic.colorrank_global_h, 3658)

        # mod to row above
        pic = self.sql_controller.get_previous_color_in_hikes(
            self.picture, 891 + 3658)
        self.assertEqual(pic.colorrank_global_h, 2000)

        # mod to row below
        pic = self.sql_controller.get_previous_color_in_hikes(
            self.picture, 2900 + 3658)
        self.assertEqual(pic.colorrank_global_h, 3649)

    def test_get_next_color_in_global(self):
        # forward by 1
        pic = self.sql_controller.get_next_color_in_global(self.picture, 1)
        self.assertEqual(pic.colorrank_global, 2786)

        # forward by 10
        pic = self.sql_controller.get_next_color_in_global(self.picture, 10)
        self.assertEqual(pic.colorrank_global, 2795)

        # wrap to top of list
        pic = self.sql_controller.get_picture_with_id(3482)
        pic = self.sql_controller.get_next_color_in_global(pic, 4)
        self.assertEqual(pic.picture_id, 518)
        self.assertEqual(pic.colorrank_global, 1)

        # mod to row below
        pic = self.sql_controller.get_next_color_in_global(
            self.picture, 500 + 3658)
        self.assertEqual(pic.colorrank_global, 3285)

        # mod to row above
        pic = self.sql_controller.get_next_color_in_global(
            self.picture, 900 + 3658)
        self.assertEqual(pic.colorrank_global, 27)

    def test_get_previous_color_in_global(self):
        # backward by 1
        pic = self.sql_controller.get_previous_color_in_global(self.picture, 1)
        self.assertEqual(pic.colorrank_global, 2784)

        # backward by 10
        pic = self.sql_controller.get_previous_color_in_global(
            self.picture, 10)
        self.assertEqual(pic.colorrank_global, 2775)

        # wrap to bottom of list
        pic = self.sql_controller.get_picture_with_id(882)
        pic = self.sql_controller.get_previous_color_in_global(pic, 2)
        self.assertEqual(pic.picture_id, 873)
        self.assertEqual(pic.colorrank_global, 3658)

        # mod to row above
        pic = self.sql_controller.get_previous_color_in_global(
            self.picture, 500 + 3658)
        self.assertEqual(pic.colorrank_global, 2285)

        # mod to row below
        pic = self.sql_controller.get_previous_color_in_global(
            self.picture, 2800 + 3658)
        self.assertEqual(pic.colorrank_global, 3643)

    def test_get_next_color_skip_in_hikes(self):
        # id	hike	color_rank_hike 	id (hike | color_rank_hike)
        # wrap around hike 1 to hike 4 (color_rank 4 --> 1)
        # 94    1       772             --> 3508 (4 | 334)
        # (772/892) * 385 = 333.2
        pic = self.sql_controller.get_picture_with_id(94)
        pic = self.sql_controller.get_next_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 3304)

        # hike 3 to hike 2 (colors_rank 2 --> 3)
        # 1994  3		1776			--> 1084 (2 | 183)
        # (1776/2158) * 223 = 183.53
        pic = self.sql_controller.get_next_color_skip_in_hikes(self.picture)
        self.assertEqual(pic.picture_id, 1087)

        # hike 2 to hike 1 (colors_rank 3 --> 4)
        # 1109  2       26              --> 479 (1 | 104)
        # (26/223) * 892 = 104.0
        pic = self.sql_controller.get_picture_with_id(1109)
        pic = self.sql_controller.get_next_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 479)

    def test_get_previous_color_skip_in_hikes(self):
        # id	hike	color_rank_hike 	id (hike | color_rank_hike)
        # wrap to hike 4 to hike 1 (color_rank 1 --> 4)
        # 3388  4       2               <-- 883 (1 | 5)
        # (2/385) * 892 = 4.63
        pic = self.sql_controller.get_picture_with_id(3388)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 883)

        # hike 3 to hike 4 (colors_rank 2 --> 1)
        # 1994  3		1776			<-- 3522 (4 | 317)
        # (1776/2158) * 385 = 316.85
        pic = self.sql_controller.get_previous_color_skip_in_hikes(
            self.picture)
        self.assertEqual(pic.picture_id, 3522)

        # hike 1 to hike 2 (color_rank 4 --> 3)
        # (1/892) * 223 = 0.25
        pic = self.sql_controller.get_picture_with_id(518)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1106)

        # (2/892) * 223 = 0.5
        pic = self.sql_controller.get_picture_with_id(882)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1106)

        # (3/892) * 223 = 0.75
        pic = self.sql_controller.get_picture_with_id(878)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1106)

        # (4/892) * 223 = 1.0
        pic = self.sql_controller.get_picture_with_id(879)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1106)

        # (5/892) * 223 = 1.25
        pic = self.sql_controller.get_picture_with_id(883)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1115)

        # (7/892) * 223 = 1.75
        pic = self.sql_controller.get_picture_with_id(308)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1115)

        # (8/892) * 223 = 2.0
        pic = self.sql_controller.get_picture_with_id(315)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1115)

        # (9/892) * 223 = 2.25
        pic = self.sql_controller.get_picture_with_id(310)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1067)

        # (10/892) * 223 = 2.5
        pic = self.sql_controller.get_picture_with_id(312)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1067)

        # (11/892) * 223 = 2.75
        pic = self.sql_controller.get_picture_with_id(325)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1067)

        # (12/892) * 223 = 3.0
        pic = self.sql_controller.get_picture_with_id(332)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1067)

        # End of hike 1
        # (887/892) * 223 = 221.75
        pic = self.sql_controller.get_picture_with_id(373)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1024)

        # (888/892) * 223 = 222.0
        pic = self.sql_controller.get_picture_with_id(9)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1024)

        # (889/892) * 223 = 222.25
        pic = self.sql_controller.get_picture_with_id(386)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1044)

        # (890/892) * 223 = 222.5
        pic = self.sql_controller.get_picture_with_id(1)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1044)

        # (891/892) * 223 = 222.75
        pic = self.sql_controller.get_picture_with_id(143)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1044)

        # (892/892) * 223 = 223.0
        pic = self.sql_controller.get_picture_with_id(873)
        pic = self.sql_controller.get_previous_color_skip_in_hikes(pic)
        self.assertEqual(pic.picture_id, 1044)

    def test_get_next_color_skip_in_global(self):
        pic = self.sql_controller.get_next_color_skip_in_global(self.picture)
        self.assertEqual(pic.colorrank_global, 2968)

        # bottom
        pic = self.sql_controller.get_picture_with_id(1220)  # 3640
        pic = self.sql_controller.get_next_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 165)
        self.assertEqual(pic.picture_id, 2594)

    def test_get_previous_color_skip_in_global(self):
        pic = self.sql_controller.get_previous_color_skip_in_global(
            self.picture)
        self.assertEqual(pic.colorrank_global, 2602)

        # top
        pic = self.sql_controller.get_picture_with_id(310)  # 10
        pic = self.sql_controller.get_previous_color_skip_in_global(pic)
        self.assertEqual(pic.colorrank_global, 3485)
        self.assertEqual(pic.picture_id, 1798)
예제 #18
0
#!/usr/bin/env python3

import os
import sys
import time
sys.path.append('..')
from classes.sql_controller import SQLController

DB = '/Volumes/Capra/capra-storage/capra_explorer.db'
DB_EMPTY = '/Volumes/Capra/capra-storage/capra_explorer_empty.db'

controller = SQLController(database=DB_EMPTY)


def test_get_hike_count():
    count = controller.get_hike_count()
    print(count)
    # assert count == 5, 'Should be 5'


def test_get_last_hike_id():
    hike_id = controller.get_last_hike_id()
    print(hike_id)
    # assert hike_id == 5, 'Should be 5'


def test_get_last_hike_time():
    val = controller.get_time_since_last_hike()
    print('Last time: {t}'.format(t=val))

예제 #19
0
class DatabaseValidityTest(unittest.TestCase):
    '''This is to test the validity of a given database, which you select on launch
    The main things tested: \n
    • Does `hikes` table have same count as `SELECT count(*) FROM pictures WHERE hike=n` \n
    • Do rank's last values == count \n
    • Verify hike ranks go 1,2,3...n-2,n-1,n \n
    • Verify archive ranks go 1,2,3...n-2,n-1,n
    '''

    # hard-coded locations
    DB = 'tests/capra_projector_jun2021_min_test_0708.db'  # no / infront makes the path relative
    directory = 'capra-storage'
    sql_controller = None
    sql_statements = None
    picture = None

    # hikes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    #          15, 16, 19, 29, 30, 31, 33, 41, 43, 44,
    #          50, 51, 52, 53, 54, 58, 59, 60]

    # first picture_id of each hike
    # picids_hikes = [2262, 3152, 3373, 5529, 5912, 7300, 8202, 9616, 11062, 11241,
    #                 14801, 17135, 17159, 18885, 21166, 21209, 21768, 1, 82, 144,
    #                 23493, 24612, 25095, 25570, 26996, 27262, 27631, 29765]

    @classmethod
    def setUpClass(self):
        # NOTE - if the database or id is changed, all the tests will break
        # They are dependent upon that
        self.sql_controller = SQLController(database=self.DB,
                                            directory=self.directory)
        self.picture = self.sql_controller.get_picture_with_id(1)

        # For testing directly on the sql statements
        self.sql_statements = SQLStatements()

    @classmethod
    def tearDownClass(self):
        self.sql_controller = None

    def test_get_picture_with_id(self):
        self.assertEqual(self.picture.picture_id, 1)
        self.assertEqual(self.picture.altitude, 14.25)

    # Size Calls
    # --------------------------------------------------------------------------
    def test_get_archive_size(self):
        size = self.sql_controller.get_archive_size()
        # self.assertEqual(size, 30378)
        self.assertEqual(size, 30404)

    def test_hike_sizes_with_ranks(self):
        print(
            '\n   Hike ID\tcount(*)\thikes table\tindex_in_hike\taltrank_hike\tcolor_rank_hike'
        )

        sql = 'SELECT hike_id FROM hikes'
        rows = self.sql_controller._execute_query_for_anything(sql)

        # for h in self.hikes:
        for r in rows:
            h = r[0]
            sz_count = self.sql_controller.get_hike_size_with_id(h)
            sql = 'SELECT pictures FROM hikes WHERE hike_id={h};'.format(h=h)
            sz_hikes_table = self.sql_controller._execute_query_for_int(sql)

            last_index_qry = 'SELECT index_in_hike FROM pictures WHERE hike={h} ORDER BY index_in_hike DESC LIMIT 1;'.format(
                h=h)
            last_index = self.sql_controller._execute_query_for_int(
                last_index_qry)
            last_altrank_qry = 'SELECT altrank_hike FROM pictures WHERE hike={h} ORDER BY altrank_hike DESC LIMIT 1;'.format(
                h=h)
            last_altrank = self.sql_controller._execute_query_for_int(
                last_altrank_qry)
            last_colorrank_qry = 'SELECT color_rank_hike FROM pictures WHERE hike={h} ORDER BY color_rank_hike DESC LIMIT 1;'.format(
                h=h)
            last_colorrank = self.sql_controller._execute_query_for_int(
                last_colorrank_qry)

            if sz_count == sz_hikes_table == last_index == last_altrank == last_colorrank:
                match = '✅'
            else:
                match = '❌'

            print('{bool} {h}\t\t{cnt}\t\t{tbl}\t\t{ind}\t\t{alt}\t\t{clr}'.
                  format(bool=match,
                         h=h,
                         cnt=sz_count,
                         tbl=sz_hikes_table,
                         ind=last_index,
                         alt=last_altrank,
                         clr=last_colorrank))

    def test_incremental_validity_ranks_hikes(self):
        print('\n\n')
        sql = 'SELECT hike_id FROM hikes'
        rows = self.sql_controller._execute_query_for_anything(sql)
        for r in rows:  # same as r = rows[0]
            h = r[0]

            queries = [
                'SELECT index_in_hike FROM pictures WHERE hike={h} ORDER BY index_in_hike ASC;'
                .format(h=h),
                'SELECT altrank_hike FROM pictures WHERE hike={h} ORDER BY altrank_hike ASC;'
                .format(h=h),
                'SELECT color_rank_hike FROM pictures WHERE hike={h} ORDER BY color_rank_hike ASC;'
                .format(h=h)
            ]
            for q in queries:
                print('Testing:\t{q}'.format(q=q))
                indexes = self.sql_controller._execute_query_for_anything(q)

                counter = 1
                for i in indexes:
                    self.assertEqual(counter, i[0])
                    counter += 1

    def test_incremental_validity_ranks_archive(self):
        print('\n\n')
        queries = [
            'SELECT time_rank_global FROM pictures ORDER BY time_rank_global ASC;',
            'SELECT altrank_global FROM pictures ORDER BY altrank_global ASC;',
            'SELECT altrank_global_h FROM pictures ORDER BY altrank_global_h ASC;',
            'SELECT color_rank_global FROM pictures ORDER BY color_rank_global ASC;',
            'SELECT color_rank_global_h FROM pictures ORDER BY color_rank_global_h ASC;'
        ]
        for q in queries:
            print('Testing:\t{q}'.format(q=q))
            indexes = self.sql_controller._execute_query_for_anything(q)

            counter = 1
            for i in indexes:
                self.assertEqual(counter, i[0])
                counter += 1
예제 #20
0
g.init()

start_time = time.time()

oldDBController = None
newDBController = None

# PATH = '/media/pi/capra-hd/'
PATH = '../capra-sample-data/dbTransform/'
OLD_DB = PATH + 'capra_projector_test.db'
NEW_DB = PATH + 'capra_camera_test.db'

print("Old db: {}".format(OLD_DB))
print("New db: {}".format(NEW_DB))

oldDBController = SQLController(database=OLD_DB)
newDBController = SQLController(database=NEW_DB)

oldCursor = oldDBController.connection.cursor()
newCursor = newDBController.connection.cursor()


# *** hikes ***
#         "hike_id"             INTEGER UNIQUE,
#         "avg_altitude"        REAL,
#         "avg_brightness"      REAL,
#         "avg_hue"             REAL,
#         "avg_hue_lumosity"    REAL,
#         "start_time"          REAL UNIQUE,
#         "end_time"            REAL UNIQUE,
#         "pictures"            INTEGER,
예제 #21
0
class DatabaseTest(unittest.TestCase):
    # no / infront makes the path relative
    # DB = 'tests/capra_projector_may2021.db'
    # DB = 'tests/capra_projector_jun2021_min_full_clean_0618.db'
    # DB = 'tests/capra_projector_apr2021_min_test_full.db'
    # DB = 'tests/capra_projector_jun2021_min_test_0623.db'
    DB = 'tests/capra_projector_jun2021_min_test_0708.db'
    directory = 'capra-storage'
    sql_controller = None
    sql_statements = None
    picture = None

    hikes = []
    picture_ids = []  # only the 1st picture_id in each hike

    @classmethod
    def setUpClass(self):
        # NOTE - if the database or id is changed, all the tests will break
        # They are dependent upon that
        self.sql_controller = SQLController(database=self.DB, directory=self.directory)
        self.picture = self.sql_controller.get_picture_with_id(11994)

        # For testing directly on the sql statements
        self.sql_statements = SQLStatements()

        # Grab an array of hikes from the database
        sql = 'SELECT hike_id FROM hikes ORDER BY hike_id ASC;'
        rows = self.sql_controller._execute_query_for_anything(sql)
        i = 0
        for r in rows:
            self.hikes.append(r[0])

            # Grab the first picture_id from each hike from the database
            sql = 'SELECT picture_id FROM pictures WHERE hike={h} ORDER BY index_in_hike ASC LIMIT 1;'.format(h=r[0])
            pid = self.sql_controller._execute_query_for_int(sql)
            self.picture_ids.append(pid)
            i += 1

    @classmethod
    def tearDownClass(self):
        self.sql_controller = None

    def test_get_picture_with_id(self):
        self.assertEqual(self.picture.picture_id, 11994)
        self.assertEqual(self.picture.altitude, 2128.38)

    # Size Calls
    # --------------------------------------------------------------------------
    def test_get_hike_size(self):
        size = self.sql_controller.get_hike_size(self.picture)
        self.assertEqual(size, 3561)

    def test_get_archive_size(self):
        size = self.sql_controller.get_archive_size()
        self.assertEqual(size, 30404)

    # Data Types
    # --------------------------------------------------------------------------
    def test_picture_object(self):
        picture = self.sql_controller.get_picture_with_id(1994)

        self.assertEqual(picture.picture_id, 1994)
        self.assertEqual(picture.time, 1556391512.0)
        self.assertEqual(picture.year, 2019)
        self.assertEqual(picture.month, 4)
        self.assertEqual(picture.day, 27)
        self.assertEqual(picture.minute, 718)
        self.assertEqual(picture.dayofweek, 5)

        self.assertEqual(picture.hike_id, 3)
        self.assertEqual(picture.index_in_hike, 879)

        self.assertEqual(picture.altitude, 936.62)
        self.assertEqual(picture.altrank_hike, 855)
        self.assertEqual(picture.altrank_global, 17954)
        self.assertEqual(picture.altrank_global_h, 19549)

        hsvColor = QColor()
        hsvColor.setHsv(25, 173, 49)
        self.assertEqual(picture.color_hsv, hsvColor)
        rgbColor = QColor(49, 43, 16)
        self.assertEqual(picture.color_rgb, rgbColor)
        self.assertEqual(picture.colorrank_hike, 167)
        self.assertEqual(picture.colorrank_global, 3310)
        self.assertEqual(picture.colorrank_global_h, 15042)
        self.assertEqual(picture.colors_count, 4)
        colorList = [QColor(49, 43, 16), QColor(117, 111, 69),
                     QColor(200, 201, 214), QColor(18, 18, 16)]

        self.assertEqual(picture.colors_rgb, colorList)
        self.assertEqual(picture.colors_conf, [0.17, 0.16, 0.15, 0.11])

        self.assertEqual(picture.camera1, 'capra-storage/hike3/878_cam1.jpg')
        self.assertEqual(picture.camera2, 'capra-storage/hike3/878_cam2.jpg')
        self.assertEqual(picture.camera3, 'capra-storage/hike3/878_cam3.jpg')
        self.assertEqual(picture.cameraf, 'capra-storage/hike3/878_cam2f.jpg')
        self.assertEqual(picture.created, '2019-08-22 17:44:51')
        self.assertEqual(picture.updated, '2021-07-08 19:44:47')

    def test_hike_object(self):
        hike = self.sql_controller.get_hike_with_id(59)

        self.assertEqual(hike.hike_id, 59)
        self.assertEqual(hike.avg_altitude, 916.65)
        self.assertEqual(hike.avg_altrank, 22)

        self.assertEqual(hike.start_time, 1598658160.0)
        self.assertEqual(hike.start_year, 2020)
        self.assertEqual(hike.start_month, 8)
        self.assertEqual(hike.start_day, 28)
        self.assertEqual(hike.start_minute, 1002)
        self.assertEqual(hike.start_dayofweek, 4)

        self.assertEqual(hike.end_time, 1598675747.0)
        self.assertEqual(hike.end_year, 2020)
        self.assertEqual(hike.end_month, 8)
        self.assertEqual(hike.end_day, 28)
        self.assertEqual(hike.end_minute, 1295)
        self.assertEqual(hike.end_dayofweek, 4)

        self.assertEqual(hike.color_rgb, QColor(78, 74, 61))
        self.assertEqual(hike.color_rank, 11)
        self.assertEqual(hike.num_pictures, 2134)
        self.assertEqual(hike.path, 'capra-storage/hike59/')
        self.assertEqual(hike.created, '2020-08-28 23:42:39')
        self.assertEqual(hike.updated, '2021-07-08 21:23:04')

    # UI Databse Calls
    # --------------------------------------------------------------------------
    def test_count_colors_for_hikes(self):
        size = 128.0  # variable to test different return size of elements

        # 16 - 24, 14891
        # 30 - 44, 18922
        # 41 - 81, 21252
        # 43 - 62, 21333

        # These all have 128 or more pictures
        picture_ids = [1, 893, 1116, 3274, 3659, 5049, 5953, 7369, 8817, 8996, 12557, 14915, 16641,
                       18966, 19526, 21395, 23513, 24632, 25116, 25592, 27019, 27286, 27656, 29790]

        for p in picture_ids:
            pic = self.sql_controller.get_picture_with_id(p)

            colors = self.sql_controller.ui_get_colors_for_hike_sortby('alt', pic)
            self.assertEqual(len(colors), size)
            colors = self.sql_controller.ui_get_colors_for_hike_sortby('color', pic)
            self.assertEqual(len(colors), size)
            colors = self.sql_controller.ui_get_colors_for_hike_sortby('time', pic)
            self.assertEqual(len(colors), size)

    # Altitude Graph
    # TODO - Tests for altitude graph list

    # Color Bar
    def test_ui_get_colors_for_hike(self):
        self.assertEqual(self.picture.picture_id, 11994)
        self.assertEqual(self.picture.hike_id, 10)
        rgbColor = QColor(79, 57, 48)
        self.assertEqual(self.picture.color_rgb, rgbColor)

        # Sorted by altrank_hike ASC
        colors = self.sql_controller.ui_get_colors_for_hike_sortby('alt', self.picture)
        self.assertEqual(len(colors), 128)
        color0 = QColor(48, 52, 44)
        color94 = QColor(165, 179, 197)
        color126 = QColor(200, 199, 210)

        self.assertEqual(colors[0], color0)
        self.assertEqual(colors[94], color94)
        self.assertEqual(colors[126], color126)

        # Sorted by color_rank_hike ASC
        colors = self.sql_controller.ui_get_colors_for_hike_sortby('color', self.picture)
        self.assertEqual(len(colors), 128)
        color0 = QColor(0, 0, 0)
        color94 = QColor(1, 1, 1)
        color126 = QColor(91, 87, 90)

        self.assertEqual(colors[0], color0)
        self.assertEqual(colors[94], color94)
        self.assertEqual(colors[126], color126)

        # Sorted by time ASC
        colors = self.sql_controller.ui_get_colors_for_hike_sortby('time', self.picture)
        self.assertEqual(len(colors), 128)
        color0 = QColor(48, 52, 44)
        color94 = QColor(205, 199, 202)
        color126 = QColor(0, 0, 0)

        self.assertEqual(colors[0], color0)
        self.assertEqual(colors[94], color94)
        self.assertEqual(colors[126], color126)

    def test_ui_get_colors_for_hike(self):
        colors = self.sql_controller.ui_get_colors_for_archive_sortby('alt')
        self.assertEqual(len(colors), 1280)
        self.assertEqual(colors[94], QColor(100, 135, 219))

        colors = self.sql_controller.ui_get_colors_for_archive_sortby('color')
        self.assertEqual(len(colors), 1280)
        self.assertEqual(colors[94], QColor(37, 56, 45))

        colors = self.sql_controller.ui_get_colors_for_archive_sortby('time')
        self.assertEqual(len(colors), 1280)
        self.assertEqual(colors[94], QColor(141, 143, 149))

    # Time Bar
    def test_ui_get_percent_for_hike(self):
        self.assertEqual(self.picture.picture_id, 11994)
        self.assertEqual(self.picture.hike_id, 10)

        percent = self.sql_controller.ui_get_percentage_in_hike_with_mode('alt', self.picture)
        self.assertEqual(percent, 0.7939)
        percent = self.sql_controller.ui_get_percentage_in_hike_with_mode('color', self.picture)
        self.assertEqual(percent, 0.1022)
        percent = self.sql_controller.ui_get_percentage_in_hike_with_mode('time', self.picture)
        self.assertEqual(percent, 0.8422)

    def test_ui_get_percent_for_archive(self):
        self.assertEqual(self.picture.picture_id, 11994)
        self.assertEqual(self.picture.hike_id, 10)

        percent = self.sql_controller.ui_get_percentage_in_archive_with_mode('alt', self.picture)
        self.assertEqual(percent, 0.9759)
        percent = self.sql_controller.ui_get_percentage_in_archive_with_mode('color', self.picture)
        self.assertEqual(percent, 0.1585)
        percent = self.sql_controller.ui_get_percentage_in_archive_with_mode('time', self.picture)
        self.assertEqual(percent, 0.3840)
예제 #22
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Capra Transfer Animation")
        self.setGeometry(0, 0, 1280, 720)

        # Setups up database connection depending on the system
        self.setupSQLController()

        # Setup Bg Thread for getting picture from Database
        self.threadpool = QThreadPool()
        self.threadpool.setMaxThreadCount(1)
        self.newPictureThread = NewPictureThread(self.sql_controller)
        self.threadpool.start(self.newPictureThread)

        # Setup BG color
        # c1 = QColor(9, 24, 94, 255)
        self.bgcolor = BackgroundColor(self, QVBoxLayout(), Qt.AlignBottom, self.picture.color_rgb)

        self.setupColorWidgets(self.picture)
        self.setupAltitudeLabel(self.picture)
        self.setupTimeLabelAndBar(self.picture)

        self.superCenterImg = TransferCenterImage(self, self.picture)

        self.setupAltitudeGraphAndLine(self.picture)
        self.altitudegraph.hide()
        self.centerLabel.opacityHide()
        self.line.hide()

        # Setup QTimer for checking for new Picture
        self.handlerCounter = 1
        self.timerAnimationHandler = QTimer()
        self.timerAnimationHandler.timeout.connect(self.animationHandler)
        self.timerAnimationHandler.start(7000)

    def animationHandler(self):
        self.fadeOutTimer = QTimer()
        self.fadeOutTimer.setSingleShot(True)

        if self.handlerCounter % 4 == 1:  # Color
            self.fadeMoveInColorWidgets()
            self.fadeOutTimer.timeout.connect(self.fadeMoveOutColorWidgets)
        elif self.handlerCounter % 4 == 2:  # Altitude
            self.fadeMoveInAltitudeEverything()
            self.fadeOutTimer.timeout.connect(self.fadeMoveOutAltitudeEverything)
        elif self.handlerCounter % 4 == 3:  # Time
            self.fadeMoveInTime()
            self.fadeOutTimer.timeout.connect(self.fadeMoveOutTime)
        elif self.handlerCounter % 4 == 0:  # New Picture: image, bg color, ui elements
            global globalPicture
            self.picture = globalPicture

            # Update UI Elements
            self.updateColorWidgets(self.picture)
            self.updateTimeLabelAndBar(self.picture)
            self.updateAltitudeLabelAndGraph(self.picture)
            # self.setupTimeLabelAndBar(self.picture)
            # self.setupAltitudeLabel(self.picture)

            # Change Image and Background
            self.superCenterImg.fadeNewImage(self.picture)
            self.bgcolor.changeColor(self.picture.color_rgb)

        self.fadeOutTimer.start(3500)
        self.handlerCounter += 1

    # Database connection - previous db with UI data
    def setupSQLController(self):
        '''Initializes the database connection.\n
        If on Mac/Windows give dialog box to select database,
        otherwise it will use the global defined location for the database'''

        # Mac/Windows: select the location
        if platform.system() == 'Darwin' or platform.system() == 'Windows':
            # filename = QFileDialog.getOpenFileName(self, 'Open file', '', 'Database (*.db)')
            # self.database = filename[0]
            # self.directory = os.path.dirname(self.database)

            self.database = '/Users/Jordan/Dropbox/Everyday Design Studio/A Projects/100 Ongoing/Capra/capra-storage/capra-storage-jordan-projector/capra_projector_jun2021_min_test_0708.db'
            self.directory = '/Users/Jordan/Dropbox/Everyday Design Studio/A Projects/100 Ongoing/Capra/capra-storage/capra-storage-jordan-projector'
        else:  # Raspberry Pi: preset location
            self.database = g.DATAPATH_PROJECTOR + g.DBNAME_MASTER
            self.directory = g.DATAPATH_PROJECTOR

        print(self.database)
        print(self.directory)
        self.sql_controller = SQLController(database=self.database, directory=self.directory)

        # self.picture = self.sql_controller.get_picture_with_id(20000) # 28300
        self.picture = self.sql_controller.get_random_picture()

        # self.picture2 = self.sql_controller.get_picture_with_id(27500)
        # self.picture3 = self.sql_controller.get_picture_with_id(12000)
        # self.picture4 = self.sql_controller.get_picture_with_id(10700)
        # 10700, 11990, 12000, 15000, 20000, 27100, 27200, 27300, 27500, 28300

        # TODO - should we preload all the UI Data?
        # self.uiData = self.sql_controller.preload_ui_data()
        # self.preload = True

    # Time Mode
    def setupTimeLabelAndBar(self, picture: Picture):
        self.timeLabel = UILabelTopCenter(self, '', '')
        self.timeLabel.setPrimaryText(picture.uitime_hrmm)
        self.timeLabel.setSecondaryText(picture.uitime_ampm)

        percent_rank = self.sql_controller.ui_get_percentage_in_archive_with_mode('time', self.picture)
        self.timebar = TimeBarTransfer(self, percent_rank)

        self.timeLabel.opacityHide()
        self.timebar.hide()

    def updateTimeLabelAndBar(self, picture: Picture):
        self.timeLabel.setPrimaryText(picture.uitime_hrmm)
        self.timeLabel.setSecondaryText(picture.uitime_ampm)

        percent_rank = self.sql_controller.ui_get_percentage_in_archive_with_mode('time', self.picture)
        self.timebar.trigger_refresh(percent_rank)

    def fadeMoveInTime(self):
        self.timeLabel.show()
        self.timebar.show()

        # Move in top label
        self.animMoveInTimeLabel = QPropertyAnimation(self.timeLabel, b"geometry")
        self.animMoveInTimeLabel.setDuration(2000)
        self.animMoveInTimeLabel.setStartValue(QRect(0, 360, 1280, 110))
        self.animMoveInTimeLabel.setEndValue(QRect(0, 15, 1280, 110))
        self.animMoveInTimeLabel.start()

        # Fade in the timebar
        fadeTimebar = QGraphicsOpacityEffect()
        self.timebar.setGraphicsEffect(fadeTimebar)
        self.animFadeInAltitudeGraph = QPropertyAnimation(fadeTimebar, b"opacity")
        self.animFadeInAltitudeGraph.setStartValue(0)
        self.animFadeInAltitudeGraph.setEndValue(1)
        self.animFadeInAltitudeGraph.setDuration(2000)
        self.animFadeInAltitudeGraph.start()

        self.update()

    def fadeMoveOutTime(self):
        # Move out time label
        self.animMoveOutTimeLabel = QPropertyAnimation(self.timeLabel, b"geometry")
        self.animMoveOutTimeLabel.setDuration(2000)
        self.animMoveOutTimeLabel.setStartValue(QRect(0, 15, 1280, 110))
        self.animMoveOutTimeLabel.setEndValue(QRect(0, 360, 1280, 110))
        self.animMoveOutTimeLabel.start()

        # Fade out time label - issue with interferring with other effects
        # fadeOutTimeLabel = QGraphicsOpacityEffect()
        # self.timeLabel.setGraphicsEffect(fadeOutTimeLabel)
        # self.animFadeOutTimeLabel = QPropertyAnimation(fadeOutTimeLabel, b"opacity")
        # self.animFadeOutTimeLabel.setStartValue(1)
        # self.animFadeOutTimeLabel.setEndValue(0)
        # self.animFadeOutTimeLabel.setDuration(2000)
        # self.animFadeOutTimeLabel.start()

        # Fade out the timebar
        fadeTimebar = QGraphicsOpacityEffect()
        self.timebar.setGraphicsEffect(fadeTimebar)
        self.animFadeOutAltitudeGraph = QPropertyAnimation(fadeTimebar, b"opacity")
        self.animFadeOutAltitudeGraph.setStartValue(1)
        self.animFadeOutAltitudeGraph.setEndValue(0)
        self.animFadeOutAltitudeGraph.setDuration(2000)
        self.animFadeOutAltitudeGraph.start()

        self.update()

    # Altitude Mode
    def setupAltitudeLabel(self, picture: Picture):
        self.centerLabel = UILabelTopCenter(self, '', 'M')
        self.centerLabel.setPrimaryText(picture.uialtitude)

    def setupAltitudeGraphAndLine(self, picture: Picture):
        archiveSize = self.sql_controller.get_archive_size()
        idxList = self.sql_controller.chooseIndexes(int(archiveSize), 1280.0)
        altitudelist = self.sql_controller.ui_get_altitudes_for_archive_sortby('alt', idxList)

        currentAlt = picture.altitude

        altitudelist.insert(0, currentAlt)  # Append the new altitude at the start of the list
        altitudelist = sorted(altitudelist)  # Sorts so the altitude will fit within the correct spot on the graph

        self.altitudegraph = AltitudeGraphTransferQWidget(self, True, altitudelist, currentAlt)
        self.altitudegraph.setGraphicsEffect(UIEffectDropShadow())

        MINV = min(altitudelist)
        MAXV = max(altitudelist)
        H = 500
        self.linePosY = 108 + H - ( ((currentAlt - MINV)/(MAXV-MINV)) * (H-3) )
        # print(self.linePosY)

        self.line = QLabel("line", self)
        bottomImg = QPixmap("assets/line.png")
        self.line.setPixmap(bottomImg)
        self.line.setAlignment(Qt.AlignCenter)

        self.line.setGeometry(0, int(self.linePosY), 1280, 3)

    def updateAltitudeLabelAndGraph(self, picture: Picture):
        self.centerLabel.setPrimaryText(picture.uialtitude)
        self.setupAltitudeGraphAndLine(picture)

    def fadeMoveInAltitudeEverything(self):
        self.altitudegraph.show()
        self.centerLabel.show()
        self.line.show()

        # Alt Line
        self.animMoveInLine = QPropertyAnimation(self.line, b"geometry")
        self.animMoveInLine.setDuration(3000)
        self.animMoveInLine.setStartValue(QRect(0, 360, 1280, 3))
        self.animMoveInLine.setEndValue(QRect(0, self.linePosY, 1280, 3))
        self.animMoveInLine.start()

        fadeEffect = QGraphicsOpacityEffect()
        self.line.setGraphicsEffect(fadeEffect)
        self.animFadeInLine = QPropertyAnimation(fadeEffect, b"opacity")
        # self.anim2 = QPropertyAnimation(self.label, b"windowOpacity")
        self.animFadeInLine.setStartValue(0)
        self.animFadeInLine.setEndValue(1)
        self.animFadeInLine.setDuration(1500)
        self.animFadeInLine.start()

        # Move in top label
        self.animMoveInAltitudeValue = QPropertyAnimation(self.centerLabel, b"geometry")
        self.animMoveInAltitudeValue.setDuration(2000)
        self.animMoveInAltitudeValue.setStartValue(QRect(0, 360, 1280, 110))
        self.animMoveInAltitudeValue.setEndValue(QRect(0, 15, 1280, 110))
        self.animMoveInAltitudeValue.start()

        # Fade in AltitudeGraphTransferQWidget
        fadeEffect2 = QGraphicsOpacityEffect()
        self.altitudegraph.setGraphicsEffect(fadeEffect2)
        self.animFadeInAltitudeGraph = QPropertyAnimation(fadeEffect2, b"opacity")
        self.animFadeInAltitudeGraph.setStartValue(0)
        self.animFadeInAltitudeGraph.setEndValue(1)
        self.animFadeInAltitudeGraph.setDuration(2000)
        self.animFadeInAltitudeGraph.start()

        self.update()

    def fadeMoveOutAltitudeEverything(self):
        # Label
        self.animMoveOutAltitudeValue = QPropertyAnimation(self.centerLabel, b"geometry")
        self.animMoveOutAltitudeValue.setDuration(2000)
        self.animMoveOutAltitudeValue.setStartValue(QRect(0, 30, 1280, 110))
        self.animMoveOutAltitudeValue.setEndValue(QRect(0, 360, 1280, 110))
        self.animMoveOutAltitudeValue.start()

        # Fade Out - Line
        fadeOutLine = QGraphicsOpacityEffect()
        self.line.setGraphicsEffect(fadeOutLine)
        self.animFadeOutLine = QPropertyAnimation(fadeOutLine, b"opacity")
        self.animFadeOutLine.setStartValue(1)
        self.animFadeOutLine.setEndValue(0)
        self.animFadeOutLine.setDuration(2000)
        self.animFadeOutLine.start()

        # Fade Out - Graph
        fadeOutGraph = QGraphicsOpacityEffect()
        self.altitudegraph.setGraphicsEffect(fadeOutGraph)
        self.animFadeOutGraph = QPropertyAnimation(fadeOutGraph, b"opacity")
        self.animFadeOutGraph.setStartValue(1)
        self.animFadeOutGraph.setEndValue(0)
        self.animFadeOutGraph.setDuration(2000)
        self.animFadeOutGraph.start()

        self.update()

    # Color Mode
    def setupColorWidgets(self, picture: Picture):
        self.colorpalette = ColorPaletteNew(self, True, picture.colors_rgb, picture.colors_conf)
        self.colorpalette.setGraphicsEffect(UIEffectDropShadow())

        archiveSize = self.sql_controller.get_archive_size()
        idxList = self.sql_controller.chooseIndexes(int(archiveSize), 1280.0)

        colorlist = self.sql_controller.ui_get_colors_for_archive_sortby('color', idxList)
        self.colorbar = ColorBarTransfer(self, True, colorlist)

        self.hideColorWidgets()

    def updateColorWidgets(self, picture: Picture):
        self.colorpalette.trigger_refresh(True, picture.colors_rgb, picture.colors_conf)

    def showColorWidgets(self):
        self.colorpalette.show()
        self.colorbar.show()

    def hideColorWidgets(self):
        self.colorpalette.hide()
        self.colorbar.hide()

    def fadeMoveInColorWidgets(self):
        self.colorpalette.show()
        self.colorbar.show()

        # Move in ColorPalette
        self.animMoveInColor = QPropertyAnimation(self.colorpalette, b"geometry")
        self.animMoveInColor.setDuration(2000)
        self.animMoveInColor.setStartValue(QRect(0, 360, 0, 0))
        self.animMoveInColor.setEndValue(QRect(0, 15, 0, 0))
        self.animMoveInColor.start()

        # Fade in ColorBar
        fadeEffect = QGraphicsOpacityEffect()
        self.colorbar.setGraphicsEffect(fadeEffect)
        self.animFadeInColor = QPropertyAnimation(fadeEffect, b"opacity")
        self.animFadeInColor.setStartValue(0)
        self.animFadeInColor.setEndValue(1)
        self.animFadeInColor.setDuration(2000)
        self.animFadeInColor.start()

        # This will overwrite the dropshadow effect
        # fadeEffect = QGraphicsOpacityEffect()
        # self.colorpalette.setGraphicsEffect(fadeEffect)
        # self.anim2 = QPropertyAnimation(fadeEffect, b"opacity")
        # self.anim2.setStartValue(0)
        # self.anim2.setEndValue(1)
        # self.anim2.setDuration(2000)
        # self.anim2.start()

        self.update()

    def fadeMoveOutColorWidgets(self):
        # Move out ColorPalette
        self.animMoveOutColor = QPropertyAnimation(self.colorpalette, b"geometry")
        self.animMoveOutColor.setDuration(2000)
        self.animMoveOutColor.setStartValue(QRect(0, 30, 0, 0))
        self.animMoveOutColor.setEndValue(QRect(0, 360, 0, 0))
        self.animMoveOutColor.start()

        # Fade out ColorBar
        fadeEffect = QGraphicsOpacityEffect()
        self.colorbar.setGraphicsEffect(fadeEffect)
        self.animFadeOutColor = QPropertyAnimation(fadeEffect, b"opacity")
        self.animFadeOutColor.setStartValue(1)
        self.animFadeOutColor.setEndValue(0)
        self.animFadeOutColor.setDuration(2000)
        self.animFadeOutColor.start()

        self.update()

    # Keyboard Input
    def keyPressEvent(self, event):
        # Closing App
        if event.key() == Qt.Key_Escape:
            self.close()
        elif event.key() == Qt.Key_1:
            print('1')
            # self.bgcolor.changeColor(self.picture.color_rgb)
        elif event.key() == Qt.Key_2:
            print('2')
            # self.fadeMoveInColorWidgets()
        elif event.key() == Qt.Key_3:
            print('3')
            # self.fadeMoveOutColorWidgets()
        elif event.key() == Qt.Key_4:
            print('4')
            # self.fadeMoveInAltitudeEverything()
        elif event.key() == Qt.Key_5:
            print('5')
            # self.fadeMoveOutAltitudeEverything()
        elif event.key() == Qt.Key_6:
            print('6')
            # self.fadeMoveInTime()
        elif event.key() == Qt.Key_7:
            print('7')
            # self.fadeMoveOutTime()
        elif event.key() == Qt.Key_8:
            print('8')
        elif event.key() == Qt.Key_9:
            print('9')
        elif event.key() == Qt.Key_0:
            print('0')

    def closeEvent(self, event):
        print('User has clicked the red X')
        self.garbageCollection()

    def garbageCollection(self):
        self.newPictureThread.signals.terminate = True