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