def test_database_update(self) -> None: """ Test that the Update Database functionality on the program works. """ # Assert that the database doesn't already have the tests table with Database() as DB: self.assertIsNone(DB.get_tests_table()) # Update the database using the database file sql_file_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'data', 'update_db.sql') self.update_window.file_path.set(sql_file_path) self.update_window.update_button.invoke() update_program_controller_loop(self.pc) # Assert that the database actually got updated with the sql file with Database() as DB: self.assertEqual(DB.get_tests_table()[0], "This is a test.")
def _update_database(self) -> None: """ Function that runs when the `Update` button is pressed. """ # TODO: Need to find a way to validate the database update. logger.debug( f'Retrieving the sql file path from the file path entry box.') sql_file = self.file_path.get() with Database() as DB: DB.update_database_with_sql_file(sql_file) logger.debug(f'Database updated.') self.update_label.configure(text='Done!')
def database_updater(response: RequestsResponseAlias) -> None: logger.debug(f'Starting database updater.') try: newest_db_version = response['files']['db_version.txt']['content'] except TypeError: # TODO: need to let user know that there is no network connected return if newest_db_version != DB_VERSION: for i in range(int(DB_VERSION) + 1, int(newest_db_version) + 1): sql_file_name = f'db_update_{str(i)}.sql' sql_file_url = response['files'][sql_file_name]['raw_url'] r = requests.get(sql_file_url) open(sql_file_name, 'wb').write(r.content) with Database() as DB: DB.update_database_with_sql_file(sql_file_name) os.remove(sql_file_name)
def __init__(self, parent: ttk.PanedWindow, controller: ProgramController) -> None: ttk.Frame.__init__(self, parent, width=100, height=300, relief=tk.RIDGE) self.controller = controller logger.debug(f'Initializing the MainWindow ttk frame.') self.ui_elements() # Set the status bar labels to always be on the bottom when opening a new pane window. self.rowconfigure(100, weight=1) logger.debug(f'Created MainWindow UI elements.') with Database() as DB: if DB.are_settings_saved(): self.enable_all_buttons()
def lock_unlock_aws_settings(self) -> None: """ This function executes when the 'Lock/Unlock' button is clicked. """ with Database() as DB: # If there is an AWS config already saved in the database, # then the program checks if the button says 'Lock' or 'Unlock' # If there is no data on the database, then the 'Lock/Unlock' button is disabled # and enables the save button if DB.are_settings_saved(): # If the button says 'Lock', then the input fields are disabled, the # text is grayed out, the save button is removed, and the button is # renamed to 'Unlock' if self.lock_unlock_button['text'] == 'Lock': logger.debug(f'Configuration window locked.') self._disable_all_widgets() self.lock_unlock_button['text'] = 'Unlock' self.save_button.grid_remove() # If the button says 'Unlock', then the input fields are editable, the # text is changed to black, the save button is added to the screen, and # the button is renamed to 'Lock' elif self.lock_unlock_button['text'] == 'Unlock': logger.debug(f'Configuration window unlocked.') self._enable_all_widgets() self.lock_unlock_button['text'] = 'Lock' self.save_button.grid(self.SAVE_BUTTON_GRID) else: self.lock_unlock_button.configure(text='Lock', state='disabled') self.save_button.grid(self.SAVE_BUTTON_GRID)
def populate_setup_fields(self) -> None: """ Populate the setup fields with the information from the database. This function checks if there are settings saved in the database. - If there are, then the function will add all those settings into the entry boxes the data corresponds to. - If there are no settings saved on the database, then the function doesn't do anything to the fields. """ with Database() as DB: if DB.are_settings_saved(): logger.debug( f'Populating setup fields with data from the database.') # AWS settings aws_config_data = DB.get_aws_config(label=True) self.access_key_id_string.set(aws_config_data[0]) self.secret_key_string.set(aws_config_data[1]) self.region_name_var.set(aws_config_data[2]) # FFMPEG settings ffmpeg_parameters, file_suffix, aws_different_output_extension, local_save_path, local_different_output_extension = DB.get_ffmpeg_config( ) # Set the ffmpeg_parameters data into the entry box, # if there is data for that in the database. # If there is no data (it's NULL), then an empty string is added to the entry box. self.ffmpeg_input_var.set( ffmpeg_parameters if ffmpeg_parameters is not None else "") # Set converted file suffix data into the entry box from the database # Since the table schema says that it cannot be NULL then there is no need # to check if the output is NULL before setting it. self.converted_file_suffix_input_var.set(file_suffix) # If the database's `aws_different_output_extension` field is NULL/None, # then the different_ffmpeg_output_extension_checkbutton is unchecked if aws_different_output_extension is None: self.use_different_extension.set(0) # If that field is not NULL/None, then the # different_ffmpeg_output_extension_checkbutton is checked, # the extension is set to the entry box, and the entry box is # added to the grid. else: self.use_different_extension.set(1) self.different_output_extension_var.set( aws_different_output_extension) self.different_ffmpeg_output_extension_input.grid( self.DIFFERENT_EXTENSION_INPUT_GRID) # If the database's `local_save_path` field is NULL/None, # then the local_save_checkbox is unchecked if local_save_path is None: self.local_save_var.set(0) # If that field is not NULL/None, then the # local_save_checkbox is checked, the path is set to the entry field, # and the entry box and button is added to the grid. else: self.local_save_var.set(1) self.local_save_path_var.set(local_save_path) self.local_save_path_input_field.grid( self.LOCAL_SAVE_PATH_INPUT_GRID) self.local_save_path_button.grid( self.LOCAL_SAVE_PATH_BUTTON_GRID) # If the database's `local_different_output_extension` field is NULL/None, # then the local_save_different_extension_checkbox is unchecked if local_different_output_extension is None: self.local_save_different_extension_checkbox_var.set(0) # If that field is not NULL/None, then the # local_save_different_extension_checkbox is checked, the extension is # added to the entry box, and the checkbox and entry box are added to the grid. else: self.local_save_different_extension_checkbox_var.set(1) self.local_save_different_output_extension_input_var.set( local_different_output_extension) self.local_save_different_extension_checkbox.grid( self.LOCAL_SAVE_OUTPUT_EXTENSION_CHECKBOX_GRID) self.local_save_different_output_extension_input.grid( self.LOCAL_SAVE_OUTPUT_EXTENSION_INPUT_GRID) # After adding all the data to the page then all the widgets are disabled # and the screen is displayed as if the Lock button was pressed. self._disable_all_widgets() self.lock_unlock_button['text'] = 'Unlock' self.lock_unlock_button.configure(state='normal') self.save_button.grid_remove()
def configure_aws(self, access_key_id: str, secret_key: str, region_name: str) -> None: """ Function to configure AWS settings. This function should be ran on another thread so that the UI elements are not stuck. Args: access_key_id (str): User inputted Access Key ID for AWS. secret_key (str): User inputted Secret Key for AWS. region_name (str): User selected region name from list. Raises: AWSAuthenticationException: Exception is raised when the HTTP Status Code from AWS is not 200. Catches: AWSKeyException: Exception is caught when either the Access Key ID or the Secret Key are wrong. AWSAuthenticationException: Exception is caught when the Access Key ID and Secret Key are correct but seem to be inactive. NoConnectionError: Exception is caught when there is no Internet connection. """ with Database() as DB: try: region_name_code = DB.get_region_name_code(region_name) response = AWS.test_connection(access_key_id, secret_key, region_name_code) if response['ResponseMetadata']['HTTPStatusCode'] == 200: DB.set_aws_config(access_key_id, secret_key, region_name_code) # Change setup_window_output_message_variable text to `Settings saved.` and set color to green self.setup_window_output_message_variable.set( 'Settings saved.') self.setup_window_output_message.configure( foreground='#3fe03f') # Enable the lock/unlock button. self.lock_unlock_button.configure(state='normal') # Enable all main window buttons self.controller.enable_main_window_buttons() # Run function to lock the settings self.lock_unlock_aws_settings() else: raise AWSAuthenticationException except AWSKeyException: self.setup_window_output_message_variable.set( 'ERROR: Access Key ID or Secret Access Key invalid.') self.setup_window_output_message.configure(foreground='red') logger.error( f'ERROR: Invalid AWS Access Key ID or Secret Access Key entered.' ) except AWSAuthenticationException: self.setup_window_output_message_variable.set( 'ERROR: Keys are correct but they may be inactive.') self.setup_window_output_message.configure(foreground='red') logger.error(f'ERROR: Innactive AWS keys entered.') except NoConnectionError: self.setup_window_output_message_variable.set( 'ERROR: No Internet connection detected.') self.setup_window_output_message.configure(foreground='red') logger.error( f'ERROR: No Internet connection, cannot authenticate AWS keys.' )
def save_configuration(self) -> None: """ Function that runs when the `Save Configuration` button is pressed. """ logger.debug(f'Starting save configuration process.') if not self._ff_in_path: self.setup_window_output_message_variable.set( 'Did not find ffmpeg or ffprobe on the system PATH.\nPlease install and add ffmpeg and ffprobe to PATH to use this program correctly.\nNo data saved.' ) self.setup_window_output_message.configure(foreground='red') return self.setup_window_output_message_variable.set('') # Get AWS input data access_key_id = self.access_key_id_input_field.get() secret_key = self.secret_key_input_field.get() region_name = self.region_name_var.get() # Get FFMPEG input data ffmpeg_parameters = self.ffmpeg_input_var.get() converted_file_suffix = str( self.converted_file_suffix_input.get()).strip() use_different_output_extension_for_aws = self.use_different_extension.get( ) output_extension_for_aws = self.different_output_extension_var.get() use_local_save = self.local_save_var.get() local_save_path = self.local_save_path_var.get() use_different_output_extension_for_local = self.local_save_different_extension_checkbox_var.get( ) output_extension_for_local = self.local_save_different_output_extension_input_var.get( ) # Check if the user left the converted file suffix input box empty. # If the input is empty then an error message is displayed that it cannot be empty. if converted_file_suffix == '': self.setup_window_output_message_variable.set( 'The converted file suffix cannot be empty.') self.setup_window_output_message.configure(foreground='red') logger.warning(f'No converted file suffix provided.') # Check if the user checked the "Different output extension for AWS" box but didn't # input the extension in the entry box. # If so, then an error message is displayed. if use_different_output_extension_for_aws and output_extension_for_aws == '': self.setup_window_output_message_variable.set( 'The output extension for AWS cannot be empty.') self.setup_window_output_message.configure(foreground='red') logger.warning( f'No extension entered when the `Different output extension for AWS`checkbox was selected.' ) return # Check if the user checked the "Save locally" box but didn't input # the path in the entry box. # If so, then an error message is displayed. if use_local_save and local_save_path == '': self.setup_window_output_message_variable.set( 'The local save path cannot be empty.') self.setup_window_output_message.configure(foreground='red') logger.warning( f'No local save path entered when the `Save locally` checkbox was selected.' ) return # Check if the user checked the "Different local output extension" box but didn't # input the extension in the entry box. # If so, then an error message is displayed. if use_different_output_extension_for_local and output_extension_for_local == '': self.setup_window_output_message_variable.set( 'The local output extension cannot be empty.') self.setup_window_output_message.configure(foreground='red') logger.warning( f'No extension entered when the `Different local output extension` checkbox was selected.' ) return with Database() as DB: # Since SQLite3 requires text values to be surrounded in single quotes # when executing an SQL statement, # I have to do this hack to get the single quotes around the string variable # or pass "NULL" if it's not needed DB.set_ffmpeg_config( ffmpeg_parameters, converted_file_suffix, "'{}'".format(output_extension_for_aws) if use_different_output_extension_for_aws else "NULL", "'{}'".format(local_save_path) if use_local_save else "NULL", "'{}'".format(output_extension_for_local) if use_different_output_extension_for_local else "NULL") # If any of the AWS inputs is empty, then a message is given # Else, a configuration message is given and a new thread is created # to test and save the AWS connection if access_key_id == '' or secret_key == '' or region_name == '': self.setup_window_output_message_variable.set( 'All AWS fields must to be filled in.') self.setup_window_output_message.configure(foreground='red') logger.warning(f'Not all AWS fields are filled in.') else: self.setup_window_output_message_variable.set( 'Configuring AWS settings. Please wait.') self.setup_window_output_message.configure(foreground='black') # Force update any pending tasks before having to configure AWS self.controller.update_idletasks() # Originally was using threading so that the the program wouldn't get stuck while checking # the credentials. # Had to make it non-multithreaded because of some weird tkinter errors. # The errors were not consistent and happened both when running the tests or running the # app by itself. # Original code: # threading.Thread( # target=self.configure_aws, # args=(access_key_id, secret_key, region_name) # ).start() self.configure_aws(access_key_id, secret_key, region_name)
def ui_elements(self) -> None: # Row 0 self.setup_window_top_label = ttk.Label( self, text='Please enter all the data.', style='setup_window_top_label.TLabel', justify=tk.CENTER) self.setup_window_top_label.grid(self.TOP_LABEL_GRID) # Row 1 self.setup_window_output_message_variable = tk.StringVar() self.setup_window_output_message = ttk.Label( self, text='', style='setup_window_output_message_label.TLabel', justify=tk.CENTER, textvariable=self.setup_window_output_message_variable) self.setup_window_output_message.grid(self.OUTPUT_MESSAGE_GRID) # Row 2 self.aws_heading_label = ttk.Label(self, text='AWS Settings', style='aws_heading_label.TLabel', justify=tk.CENTER) self.aws_heading_label.grid(self.AWS_HEADING_GRID) # Row 3 self.access_key_id_label = ttk.Label( self, text='AWS Access Key ID:', style='access_key_id_label.TLabel', justify=tk.LEFT) self.access_key_id_label.grid(self.ACCESS_KEY_ID_LABEL_GRID) self.access_key_id_string = tk.StringVar() self.access_key_id_input_field = ttk.Entry( self, width=44, textvariable=self.access_key_id_string) self.access_key_id_input_field.grid(self.ACCESS_KEY_ID_INPUT_GRID) # Row 4 self.secret_key_label = ttk.Label(self, text='AWS Secret Access Key:', style='aws_secret_key_label.TLabel', justify=tk.LEFT) self.secret_key_label.grid(self.SECRET_KEY_LABEL_GRID) self.secret_key_string = tk.StringVar() self.secret_key_input_field = ttk.Entry( self, width=44, textvariable=self.secret_key_string) self.secret_key_input_field.grid(self.SECRET_KEY_INPUT_GRID) # Row 5 self.region_name_label = ttk.Label(self, text='Default Region Name:', style='region_name_label.TLabel', justify=tk.LEFT) self.region_name_label.grid(self.REGION_NAME_LABEL_GRID) with Database() as DB: region_labels = DB.get_region_name_labels() self.region_name_var = tk.StringVar() self.region_name_input_field = ttk.Combobox( self, textvariable=self.region_name_var, values=region_labels, width=42, state='readonly') self.region_name_input_field.grid(self.REGION_NAME_INPUT_GRID) # Row 6 self.ffmpeg_heading_label = ttk.Label( self, text='FFMPEG Settings', style='ffmpeg_heading_label.TLabel', justify=tk.CENTER) self.ffmpeg_heading_label.grid(self.FFMPEG_HEADING_LABEL_GRID) # Row 7 self.ffmpeg_input_label = ttk.Label(self, text='FFMPEG parameters: ', style='ffmpeg_input_label.TLabel', justify=tk.LEFT) self.ffmpeg_input_label.grid(self.FFMPEG_INPUT_LABEL_GRID) self.ffmpeg_input_var = tk.StringVar() self.ffmpeg_input_var.set('-b:v 64k -bufsize 64k') self.ffmpeg_input = ttk.Entry(self, width=44, textvariable=self.ffmpeg_input_var) self.ffmpeg_input.grid(self.FFMPEG_INPUT_GRID) # Row 8 self.converted_file_suffix_label = ttk.Label( self, text='Converted file suffix:', style='converted_file_suffix_label.TLabel', justify=tk.LEFT) self.converted_file_suffix_label.grid( self.CONVERTED_FILE_SUFFIX_LABEL_GRID) self.converted_file_suffix_input_var = tk.StringVar() self.converted_file_suffix_input_var.set('_converted') self.converted_file_suffix_input = ttk.Entry( self, width=20, textvariable=self.converted_file_suffix_input_var) self.converted_file_suffix_input.grid( self.CONVERTED_FILE_SUFFIX_INPUT_GRID) # Row 9 self.use_different_extension = tk.IntVar() self.different_ffmpeg_output_extension_checkbutton = ttk.Checkbutton( self, text='Different output extension for AWS', variable=self.use_different_extension, style='regular.TCheckbutton', command=self._ffmpeg_extension_checkbutton_press) self.different_ffmpeg_output_extension_checkbutton.grid( self.DIFFERENT_EXTENSION_CHECKBOX_GRID) self.different_output_extension_var = tk.StringVar() self.different_output_extension_var.set("avi") self.different_ffmpeg_output_extension_input = ttk.Entry( self, width=10, textvariable=self.different_output_extension_var) # Row 10 self.local_save_var = tk.IntVar() self.local_save_checkbox = ttk.Checkbutton( self, text='Save locally', variable=self.local_save_var, style='regular.TCheckbutton', command=self._local_save_press) self.local_save_checkbox.grid(self.LOCAL_SAVE_CHECKBOX_GRID) self.local_save_path_var = tk.StringVar() self.local_save_path_input_field = ttk.Entry( self, width=36, textvariable=self.local_save_path_var) self.local_save_path_button = ttk.Button( self, text="Open Folder", command=self._open_folder_path) # Row 11 self.local_save_different_extension_checkbox_var = tk.IntVar() self.local_save_different_extension_checkbox = ttk.Checkbutton( self, text='Different local output extension', variable=self.local_save_different_extension_checkbox_var, style='regular.TCheckbutton', command=self._local_save_different_extension_press) self.local_save_different_output_extension_input_var = tk.StringVar() self.local_save_different_output_extension_input_var.set("mkv") self.local_save_different_output_extension_input = ttk.Entry( self, width=10, textvariable=self.local_save_different_output_extension_input_var) # Row 12 self.ffmpeg_example_label = ttk.Label( self, text='', style='ffmpeg_example_label.TLabel', justify=tk.CENTER) self.ffmpeg_example_label.grid(self.FFMPEG_EXAMPLE_LABEL) # Row 13 self.save_button = ttk.Button(self, text="Save Configuration", style='regular.TButton', command=self.save_configuration) self.save_button.grid(self.SAVE_BUTTON_GRID) self.lock_unlock_button = ttk.Button( self, text='Lock', style='regular.TButton', command=self.lock_unlock_aws_settings, # Disabled since there should be nothing saved on the database at first boot state='disabled') self.lock_unlock_button.grid(self.LOCK_UNLOCK_BUTTON_GRID)