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