Main Application Module

import os
import tkinter
import tkinter.messagebox

import raconverter
import raconverter.data_file
import raconverter.data_file_form
import raconverter.entry_fields
import raconverter.message_text
import raconverter.metadata_file
import raconverter.syntax_file
from raconverter.constants import *


class MainApplication:
    """
    The main application (controller).
    The class adds content and functionality to the top level window (root).

    :param root: The top level window
    """

    def __init__(self, root):
        self.root = root
        self._button_step1 = None
        self._button_step2 = None
        self._button_step3 = None
        self._button_step4 = None
        self._directory_path = ''
        self._file_name = ''
        self._file_extension = ''

        # Instantiate classes
        self.message_text = raconverter.message_text.MessageText()
        self.data_file = raconverter.data_file.DataFile()
        self.syntax_file = raconverter.syntax_file.SyntaxFile()
        self.entry_fields = raconverter.entry_fields.EntryFields()
        self.metadata_file = raconverter.metadata_file.MetadataFile()
        self.misc = raconverter.Misc()

        # Add content
        self.root.title(MAIN_TITLE)
        self.add_menu(self.root)
        self._text_entry = self.message_text.create_text_entry(self.root)
        self.message_text.insert_message(self._text_entry, INTRO_TEXT)
        self.add_buttons(self.root, self._text_entry)
        self.set_widget_state(self.button_step1)

        # Fix the windows size and center on screen
        self.root.resizable(RESIZE_X, RESIZE_Y)
        self.misc.center_window_on_screen(self.root)

    @property
    def button_step1(self):
        """
        Get button.

        :return: Button "Step 1"
        """
        return self._button_step1

    @button_step1.setter
    def button_step1(self, button_object):
        """
        Store button object in an instance attribute.
        """
        self._button_step1 = button_object

    @property
    def button_step2(self):
        """
        Get button.

        :return: Button "Step 2"
        """
        return self._button_step2

    @button_step2.setter
    def button_step2(self, button_object):
        """
        Store button object in an instance attribute.
        """
        self._button_step2 = button_object

    @property
    def button_step3(self):
        """
        Get button.

        :return: Button "Step 3"
        """
        return self._button_step3

    @button_step3.setter
    def button_step3(self, button_object):
        """
        Store button object in an instance attribute.
        """
        self._button_step3 = button_object

    @property
    def button_step4(self):
        """
        Get button.

        :return: Button "Step 4"
        """
        return self._button_step4

    @button_step4.setter
    def button_step4(self, button_object):
        """
        Store button object in an instance attribute.
        """
        self._button_step4 = button_object

    @property
    def directory_path(self):
        """
        Get the directory path.

        :return: The path to the file directory
        """
        return self._directory_path

    @directory_path.setter
    def directory_path(self, full_file_name):
        """
        Set the directory path.

        :param full_file_name: The full file name including path, file name, and file extension
        :return: The path to the file directory
        """
        self._directory_path = self.data_file.get_directory_path(full_file_name)

    @property
    def file_name(self):
        """
        Get the file name.

        :return: The physical name of the (data) file without extension
        """
        return self._file_name

    @file_name.setter
    def file_name(self, full_file_name):
        """
        Set the file name.

        :param full_file_name: The full file name including path, file name, and file extension
        """
        self._file_name = self.data_file.get_file_name(full_file_name)

    @property
    def file_extension(self):
        """
        Get the file extension.

        :return: The file extension
        """
        return self._file_extension

    @file_extension.setter
    def file_extension(self, full_file_name):
        """
        Set the file extension.

        :param full_file_name: The full file name including path, file name, and file extension
        """
        self._file_extension = self.data_file.get_file_extension(full_file_name)

    @staticmethod
    def set_widget_state(widget, state=0):
        """
        Change the state of a widget.

        :param widget: The widget
        :param state: State enable (0) | State disable (1)
        """
        if state == 0:
            widget.config(state=STATE_ENABLE)
            widget.focus_force()
        elif state == 1:
            widget.config(state=STATE_DISABLE)

    def add_menu(self, root):
        """
        Add menu.

        :param root: The top level window
        """
        menu = tkinter.Menu(root, font=FONT, relief=RELIEF_FLAT)
        sub_menu = tkinter.Menu(menu, font=FONT, tearoff=TEAROFF)
        sub_menu.add_command(label=SUB_MENU_ABOUT, underline=UNDERLINE, command=lambda: self.about())
        sub_menu.add_separator()
        sub_menu.add_command(label=SUB_MENU_EXIT, underline=UNDERLINE, command=lambda: root.destroy())
        menu.add_cascade(menu=sub_menu, label=MENU_RACONVERTER, underline=UNDERLINE)
        root.config(menu=menu)

    @staticmethod
    def about():
        """
        Add application release information.
        """
        tkinter.messagebox.showinfo(ABOUT_RACONVERTER, ABOUT_MESSAGE)

    def add_buttons(self, root, text_entry):
        """
        Add buttons.

        :param root: The top level window
        :param text_entry: A text entry object
        """
        button_frame = tkinter.Frame(root)
        button_frame.pack(side=SIDE_TOP, padx=BUTTON_FRAME_PADX10, pady=BUTTON_FRAME_PADY7, fill=FILL_X,
                          expand=EXPAND_YES)
        # Button step 1
        button_step1 = tkinter.Button(button_frame, height=BUTTON_HEIGHT2, state=STATE_ENABLE, text=BUTTON_STEP1)
        button_step1.config(font=FONT, command=lambda: self.select_data_file(text_entry))
        button_step1.pack(side=SIDE_TOP, fill=FILL_X, pady=BUTTON_PADY3, expand=EXPAND_YES)
        self.button_step1 = button_step1
        # Button step 2
        button_step2 = tkinter.Button(button_frame, height=BUTTON_HEIGHT2, state=STATE_DISABLE, text=BUTTON_STEP2)
        button_step2.config(font=FONT, command=lambda: self.create_syntax(text_entry))
        button_step2.pack(side=SIDE_TOP, fill=FILL_X, pady=BUTTON_PADY3, expand=EXPAND_YES)
        self.button_step2 = button_step2
        # Button step 3
        button_step3 = tkinter.Button(button_frame, height=BUTTON_HEIGHT2, state=STATE_DISABLE, text=BUTTON_STEP3)
        button_step3.config(font=FONT, command=lambda: self.create_data_file_form(root, text_entry))
        button_step3.pack(side=SIDE_TOP, fill=FILL_X, pady=BUTTON_PADY3, expand=EXPAND_YES)
        self.button_step3 = button_step3
        # Button step 4
        button_step4 = tkinter.Button(button_frame, height=BUTTON_HEIGHT2, state=STATE_DISABLE, text=BUTTON_STEP4)
        button_step4.config(font=FONT, command=lambda: self.create_metadata_file(text_entry))
        button_step4.pack(side=SIDE_TOP, fill=FILL_X, pady=BUTTON_PADY3, expand=EXPAND_YES)
        self.button_step4 = button_step4

    def select_data_file(self, text_entry):
        """
        Select a file and evaluate its file attributes.
        Change the working directory to the directory of the selected file.

        :param text_entry: A text entry object
        """
        # Get the full file name
        full_file_name = self.data_file.get_full_file_name()

        # If a file has been selected
        if full_file_name:
            # Set file attributes
            self.directory_path = full_file_name
            self.file_name = full_file_name
            self.file_extension = full_file_name

            # Get file attributes
            directory_path = self.directory_path
            file_name = self.file_name
            file_extension = self.file_extension

            # Change the working directory
            os.chdir(directory_path)

            # Evaluate file attributes
            self.evaluate_file_attributes(directory_path, file_name, file_extension, text_entry)

    def evaluate_file_attributes(self, directory_path, file_name, file_extension, text_entry):
        """
        Evaluate the file extension.
        Accept supported data files only.

        :param directory_path: The path to the file directory
        :param file_name: The physical name of the (data) file without extension
        :param file_extension: The file extension
        :param text_entry: A text entry object
        """
        # Validate the file extension
        valid_extension = self.data_file.validate_file_extension(file_extension)
        # Validate the file name
        valid_file_name = self.entry_fields.valid_data_file_name(file_name)

        # If invalid file
        if valid_extension and not valid_file_name:
            # Show warning
            tkinter.messagebox.showwarning(INVALID_NAME, INVALID_NAME_MESSAGE)

        # If valid file name and extension
        if valid_extension and valid_file_name:
            # Remove files if they exist
            self.remove_metadata_files(file_name)
            self.remove_syntax_file(file_name, file_extension)
            self.remove_output_files(file_name)
            # If SAS data file
            if file_extension == SAS_DATA:
                # Evaluate catalog file
                self.evaluate_catalog_file(directory_path, file_name, text_entry)
            else:
                # Accept data file
                self.data_file_accepted(directory_path, file_name, text_entry)
        elif not valid_extension:
            # Update text entry
            entry_text = STEP1_INVALID_TEXT.format(file_extension)
            self.message_text.insert_message(text_entry, entry_text)

    def remove_metadata_files(self, file_name):
        """
        Remove the metadata files if they exist.

        :param file_name: The physical name of the data file without extension
        """
        metadata_files = self.metadata_file.get_metadata_files(file_name)

        for label, metadata in metadata_files:
            # Check whether the file exists
            file = self.misc.is_non_empty_file(metadata)
            if file:
                os.remove(metadata)

    def remove_syntax_file(self, file_name, file_extension):
        """
        Remove the syntax file if it exists.

        :param file_name: The physical name of the data file without extension
        :param file_extension: The file extension
        """
        # Get syntax name
        syntax_extension = self.syntax_file.get_syntax_extension(file_extension)
        file_name = ''.join([file_name, syntax_extension])

        syntax = self.misc.is_non_empty_file(file_name)
        if syntax:
            os.remove(syntax)

    def remove_output_files(self, file_name):
        """
        Remove the output files if they exist.

        :param file_name: The physical name of the data file without extension
        """
        # Get data file
        data_file_name = ''.join([file_name, DATA])
        data_file = self.misc.is_non_empty_file(data_file_name)
        # Get metadata file
        metadata_file_name = ''.join([file_name, METADATA])
        metadata = self.misc.is_non_empty_file(metadata_file_name)

        # Remove files
        if data_file:
            os.remove(data_file)
        elif metadata:
            os.remove(metadata)

    def evaluate_catalog_file(self, directory_path, file_name, text_entry):
        """
        Evaluate the catalog file (SAS catalog).

        :param directory_path: The path to the file directory
        :param file_name: The physical name of the data file without extension
        :param text_entry: A text entry object
        """
        # Get the catalog file status
        catalog_exists = self.data_file.get_catalog_file(file_name)

        # If catalog file exists
        if catalog_exists:
            # Accept data file
            self.data_file_accepted(directory_path, file_name, text_entry)
        else:
            # If a catalog file does not apply to the data file
            if tkinter.messagebox.askyesno(MISSING_CATALOG, MISSING_CATALOG_MESSAGE):
                # Accept data file
                self.data_file_accepted(directory_path, file_name, text_entry)
            else:
                # Update text entry
                entry_text = MISSING_CATALOG_TEXT.format(directory_path, file_name)
                self.message_text.insert_message(text_entry, entry_text)

    def data_file_accepted(self, directory_path, file_name, text_entry):
        """
        Accept the data file.

        :param directory_path: The path to the file directory
        :param file_name: The physical name of the data file without extension
        :param text_entry: A text entry object
        """
        # Update text entry
        entry_text = STEP1_TRUE_TEXT.format(directory_path, file_name)
        self.message_text.insert_message(text_entry, entry_text)

        # Set button state
        self.set_widget_state(self.button_step1, state=1)
        self.set_widget_state(self.button_step2)

    def create_syntax(self, text_entry):
        """
        Create a syntax file in a supported statistical program language.

        :param text_entry: A text entry object
        """
        # Get file attributes
        directory_path = self._directory_path
        file_name = self._file_name
        file_extension = self._file_extension

        # Create and write syntax file
        syntax = self.syntax_file.create_syntax(directory_path, file_name, file_extension)
        self.syntax_file.write_syntax_file(file_name, file_extension, syntax)

        # Update text entry
        syntax_extension = self.syntax_file.get_syntax_extension(file_extension)
        application_name = self.syntax_file.get_data_file_application(file_extension)
        entry_text = STEP2_TRUE_TEXT.format(directory_path, file_name, syntax_extension, application_name)
        self.message_text.insert_message(text_entry, entry_text)

        # Set button state
        self.set_widget_state(self.button_step2, state=1)
        self.set_widget_state(self.button_step3)

    def create_data_file_form(self, root, text_entry):
        """
        Create an entry form with data file entry fields.

        :param root: The top level window
        :param text_entry: A text entry object
        """
        # Get file attributes
        file_name = self.file_name
        file_extension = self.file_extension

        # Hide the top level window
        root.withdraw()

        # Create new top level window
        child_top_level = tkinter.Toplevel()
        # Set default font
        child_top_level.option_add(FONT_OPTION, FONT)
        # Get the name of the data file application
        application_name = self.syntax_file.get_data_file_application(file_extension)
        # Open data file form
        raconverter.data_file_form.DataFileForm(child_top_level, application_name, file_name)
        # Wait until top level window is destroyed
        root.wait_window(child_top_level)
        # Display the top level window
        root.deiconify()

        # Get metadata file
        data_file_information_name = ''.join([file_name, DATA_FILE_INFORMATION_FILE])
        data_file_information = self.misc.is_non_empty_file(data_file_information_name)

        # If data file information exists (has been created)
        if data_file_information:
            # Update text entry
            self.message_text.insert_message(text_entry, STEP3_TRUE_TEXT)

            # Set button state
            self.set_widget_state(self.button_step3, state=1)
            self.set_widget_state(self.button_step4)

    def create_metadata_file(self, text_entry):
        """
        Create metadata file "Bilag 9".

        :param text_entry: A text entry object
        """
        # Get file attributes
        directory_path = self.directory_path
        file_name = self.file_name
        file_extension = self.file_extension
        # Get application name
        application_name = self.syntax_file.get_data_file_application(file_extension)

        # Create the metadata file
        metadata = self.metadata_file.create_metadata_file(file_name)
        # Remove files
        self.remove_metadata_files(file_name)
        self.remove_syntax_file(file_name, file_extension)

        # Update text entry
        entry_text = STEP4_TRUE_TEXT.format(directory_path, file_name, application_name)
        self.message_text.insert_message(text_entry, entry_text)
        self.message_text.insert_message(text_entry, metadata, delete=0, index=1)

        # Set button state
        self.set_widget_state(self.button_step4, state=1)
        self.set_widget_state(self.button_step1)


# Execute if module is run as main program
if __name__ == MAIN:
    # Create the root window
    tk = tkinter.Tk()
    # Set default font
    tk.option_add(FONT_OPTION, FONT)
    # Instantiate the main application
    MainApplication(tk)
    # Start main application
    tk.mainloop()

Leave a Reply

Your email address will not be published.