Data File Form Module

import re
import tkinter
import tkinter.messagebox

import raconverter
import raconverter.data_file_reference
import raconverter.entry_fields
from raconverter.constants import *


class DataFileForm:
    """
    Create an entry form with data file entry fields.

    Collect valid entry text related to the data file and optional data file reference(s).
    Write a metadata file with valid and formatted data file information.

    :param top_level: The top level window
    :param application_name: The name of the statistical application used to create the data file
    :param file_name: None | The physical name of the data file without extension
    """

    def __init__(self, top_level, application_name=None, file_name=None):
        self.top_level = top_level
        self.application_name = application_name
        self.file_name = file_name
        self._data_file_references = []

        # Instantiate classes
        self.entry_fields = raconverter.entry_fields.EntryFields()
        self.misc = raconverter.Misc()

        # Add content
        self.top_level.title(DATA_FILE_FORM_TITLE)
        self.entry_field_objects = self.create_entry_fields(self.top_level, self.file_name)
        self.add_entry_fields_tooltips(self.entry_field_objects)
        self.add_buttons(self.top_level, self.application_name, self.file_name, self.entry_field_objects,
                         self._data_file_references)

        # Set focus
        focus_widget = self.entry_field_objects[DATA_FILE_DESCRIPTION]
        focus_widget.focus_force()

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

    @property
    def data_file_references(self):
        """
        Get internal list with valid data file reference(s).
        Valid data file reference: [data file name, variable(s) other data file, variable(s) this data file]

        :return: An empty list | A list with valid entry text (sublist for each data file reference)
        """
        return self._data_file_references

    @data_file_references.setter
    def data_file_references(self, valid_entry_text):
        """
        Append valid entry text to a list.

        :param valid_entry_text: A list with valid entry text (sublist for each data file reference)
        """
        self._data_file_references.extend(valid_entry_text)

    def create_entry_fields(self, top_level, file_name):
        """
        Create entry frames with entry label and field.

        :param top_level: The top level window
        :param file_name: The physical name of the data file without extension
        :return: A dictionary with the entry fields
        """
        data_file_name = self.add_data_file_name(top_level, file_name)
        data_file_description = self.add_data_file_description(top_level)
        key_variable = self.add_key_variable(top_level)

        entry_fields = {DATA_FILE_NAME: data_file_name,
                        DATA_FILE_DESCRIPTION: data_file_description,
                        KEY_VARIABLE: key_variable}

        return entry_fields

    def add_data_file_name(self, top_level, file_name):
        """
        Add entry field.

        :param top_level: The top level window
        :param file_name: The physical name of the data file without extension
        :return: Entry field with data file name
        """
        data_file_name = self.entry_fields.entry_data_file_name(top_level, LABEL_WIDTH17, ENTRY_WIDTH36, DATA_FILE_NAME,
                                                                file_name)

        return data_file_name

    def add_data_file_description(self, top_level):
        """
        Add entry field.

        :param top_level: The top level window
        :return: Entry field with data file description
        """
        data_file_description = self.entry_fields.entry_standard(top_level, LABEL_WIDTH17, ENTRY_WIDTH36,
                                                                 DATA_FILE_DESCRIPTION)

        return data_file_description

    def add_key_variable(self, top_level):
        """
        Add entry field.

        :param top_level: The top level window
        :return: Entry field with key variable(s)
        """
        key_variable = self.entry_fields.entry_variable_name(top_level, LABEL_WIDTH17, ENTRY_WIDTH36, KEY_VARIABLE)

        return key_variable

    def add_entry_fields_tooltips(self, entry_fields):
        """
        Add entry field tooltips.

        :param entry_fields: A dictionary with the entry fields
        """
        self.entry_fields.add_tooltip(entry_fields[DATA_FILE_NAME], DATA_FILE_NAME_TOOLTIP)
        self.entry_fields.add_tooltip(entry_fields[DATA_FILE_DESCRIPTION], DATA_FILE_DESCRIPTION_TOOLTIP)
        self.entry_fields.add_tooltip(entry_fields[KEY_VARIABLE], KEY_VARIABLE_TOOLTIP)

    def add_buttons(self, top_level, application_name, file_name, entry_fields, data_file_references):
        """
        Add buttons.

        :param top_level: The top level window
        :param application_name: The name of the statistical application used to create the data file
        :param file_name: The physical name of the data file without extension
        :param entry_fields: A dictionary with the entry fields
        :param data_file_references: A list with valid entry text (sublist for each data file reference)
        """
        button_frame = tkinter.Frame(top_level)
        button_frame.pack(side=SIDE_TOP, padx=BUTTON_FRAME_PADX7, pady=BUTTON_FRAME_PADY10, fill=FILL_X,
                          expand=EXPAND_YES)
        # Button done
        button_done = tkinter.Button(button_frame, font=FONT, height=BUTTON_HEIGHT1, text=BUTTON_DONE)
        button_done.config(command=lambda: self.button_done(top_level, application_name, file_name, entry_fields,
                                                            data_file_references))
        button_done.pack(side=SIDE_LEFT, anchor=ANCHOR_EAST, padx=BUTTON_PADX3, fill=FILL_X, expand=EXPAND_YES)
        # Button quit
        button_quit = tkinter.Button(button_frame, font=FONT, height=BUTTON_HEIGHT1, text=BUTTON_QUIT)
        button_quit.config(command=lambda: self.button_quit(top_level))
        button_quit.pack(side=SIDE_RIGHT, anchor=ANCHOR_EAST, padx=BUTTON_PADX3, fill=FILL_X, expand=EXPAND_YES)

    def button_done(self, top_level, application_name, file_name, entry_fields, data_file_references):
        """
        Validate and format entry text from the data file/data file reference entry fields.
        Write data file information to a metadata file and destroy the top level window.

        :param top_level: The top level window
        :param application_name: The name of the statistical application used to create the data file
        :param file_name: The physical name of the data file without extension
        :param entry_fields: A dictionary with the entry fields
        :param data_file_references: A list with valid entry text (sublist for each data file reference)
        """
        # Get entry text from the entry fields
        data_file_entries = self.get_valid_entry_text(entry_fields)

        # If valid entry text
        if data_file_entries:
            # Hide the top level window
            top_level.withdraw()
            # Ask get data file references (yes/no)
            if tkinter.messagebox.askyesno(ASK_ADD_DATA_FILE_REFERENCE, ASK_ADD_DATA_FILE_REFERENCE_MESSAGE):
                self.create_data_file_reference_form(top_level)
            # Format data file references
            data_file_references = self.format_data_file_references(data_file_references)
            # Create data file information
            data_file_information = self.create_data_file_information(application_name, data_file_entries,
                                                                      data_file_references)
            # Write data file information to a metadata file
            self.write_data_file_information(file_name, data_file_information)
            # Destroy root
            top_level.destroy()

    @staticmethod
    def button_quit(top_level):
        """
        Destroy the top level window.

        :param top_level: The top level window
        """
        top_level.destroy()

    def get_valid_entry_text(self, entry_fields):
        """
        Get valid entry text from the entry fields.

        :param entry_fields: A dictionary with the entry fields
        :return: An empty list | A list with valid entry text
        """
        valid_entry_text = []

        # Get the entry text
        data_file_name = entry_fields[DATA_FILE_NAME].get()
        data_file_description = entry_fields[DATA_FILE_DESCRIPTION].get()
        key_variable = entry_fields[KEY_VARIABLE].get()

        # Validate the entry text
        valid_data_file_name = self.entry_fields.validate_data_file_name(entry_fields[DATA_FILE_NAME])
        valid_variable_name = self.entry_fields.validate_variable_name(entry_fields[KEY_VARIABLE])
        # If valid entry text
        if valid_data_file_name and valid_variable_name:
            # Add valid entry text to list
            valid_entry_text = [data_file_name, data_file_description, key_variable]
            # Strip leading, trailing, and multiple spaces in entry text
            valid_entry_text = [' '.join(x.split()) for x in valid_entry_text]

            # Reset entry text
            for key, entry_field in entry_fields.items():
                entry_field.delete(INDEX_START0, INDEX_END)

        return valid_entry_text

    def create_data_file_reference_form(self, top_level):
        """
        Create an entry form with data file reference entry fields.

        :param top_level: The top level window
        """
        # Create new top level window
        child_top_level = tkinter.Toplevel()
        # Set default font
        child_top_level.option_add(FONT_OPTION, FONT)
        # Open data file reference form
        data_file_references = raconverter.data_file_reference.DataFileReference(child_top_level)
        # Wait until the new top level window is destroyed
        top_level.wait_window(child_top_level)

        # Get valid entry text from the data file reference entry fields
        valid_entry_text = data_file_references.data_file_references
        # Set instance attribute
        self.data_file_references = valid_entry_text

    def format_data_file_references(self, data_file_references):
        """
        Format valid entry text from data file reference(s).

        :param data_file_references: A list with valid entry text (sublist for each data file reference)
        :return: A list with valid and formatted data file reference(s)
        """
        formatted_data_file_references = []

        for text_entries in data_file_references:
            # If varible other data file has multiple variable names
            if re.search('\s', text_entries[1]) is not None:
                data_file_reference = self.variable_other_data_file_multiple_names(text_entries)
                formatted_data_file_references.append(data_file_reference)
            else:
                data_file_reference = self.variable_other_data_file_single_name(text_entries)
                formatted_data_file_references.append(data_file_reference)

        return formatted_data_file_references

    @staticmethod
    def variable_other_data_file_multiple_names(entries):
        """
        Format valid entry text from a data file reference.

        :param entries: A list with valid entry text items from a data file reference
        :return: A valid and formatted data file reference (string)
        """
        # If variable this data file has multiple variable names
        if re.search('\s', entries[2]) is not None:
            data_file_reference = DOUBLE_QUOTE_BOTH_VARIABLES.format(entries[0], entries[1], entries[2])
        else:
            data_file_reference = DOUBLE_QUOTE_VARIABLE_OTHER.format(entries[0], entries[1], entries[2])

        return data_file_reference

    @staticmethod
    def variable_other_data_file_single_name(entries):
        """
        Format valid entry text from a data file reference.

        :param entries: A list with valid entry text items from a data file reference
        :return: A valid and formatted data file reference (string)
        """
        # If variable this data file has multiple variable names
        if re.search('\s', entries[2]) is not None:
            data_file_reference = DOUBLE_QUOTE_VARIABLE_THIS.format(entries[0], entries[1], entries[2])
        else:
            data_file_reference = DOUBLE_QUOTE_NO_VARIABLES.format(entries[0], entries[1], entries[2])

        return data_file_reference

    @staticmethod
    def create_data_file_information(application_name, data_file_entries, data_file_references):
        """
        Add valid and formatted data file entries/data file reference(s) to a list.

        :param application_name: The name of the statistical application used to create the data file
        :param data_file_entries: A list with valid entry text
        :param data_file_references: A list with valid and formatted data file reference(s)
        :return: A list with data file information
        """
        data_file_information = []

        # Get entry text
        data_file_name = data_file_entries[0]
        data_file_description = data_file_entries[1]
        key_variable = data_file_entries[2]

        # Add data file information
        data_file_information.append(APPLICATION_NAME_LABEL)
        data_file_information.append(application_name)
        data_file_information.append(NEW_LINE)
        data_file_information.append(DATA_FILE_NAME_LABEL)
        data_file_information.append(data_file_name)
        data_file_information.append(NEW_LINE)
        data_file_information.append(DATA_FILE_DESCRIPTION_LABEL)
        data_file_information.append(data_file_description)
        data_file_information.append(NEW_LINE)
        data_file_information.append(KEY_VARIABLE_LABEL)
        data_file_information.append(key_variable)
        data_file_information.append(NEW_LINE)
        data_file_information.append(DATA_FILE_REFERENCE_LABEL)
        data_file_information.extend(data_file_references)
        # If the list with data file reference(s) is empty
        if not data_file_references:
            data_file_information.append(NEW_LINE)
        data_file_information.append(NEW_LINE)

        return data_file_information

    @staticmethod
    def write_data_file_information(file_name, data_file_information):
        """
        Write a UTF-8 encoded metadata file with data file information.
        The path is automatically set to the current working directory.

        :param file_name: The physical name of the data file without extension
        :param data_file_information: A list with the data file information
        """
        list_content = []

        # Add line breaks
        for item in data_file_information:
            list_content.extend('%s\n' % item)
        # Convert list to string
        string_content = ''.join(list_content)
        # Convert string to bytes (encode)
        byte_syntax = string_content.encode(UTF8)

        # Get file name
        file = ''.join([file_name, DATA_FILE_INFORMATION_FILE])

        # Write syntax file
        with open(file, WRITE_MODE, encoding=UTF8) as f:
            # Convert bytes to string (decode)
            f.write(byte_syntax.decode(UTF8))

Leave a Reply

Your email address will not be published.