GUI Chnages, progress bars, more error handling.

This commit is contained in:
Tyrel Souza 2015-07-20 14:18:51 -04:00
parent 864622bdf4
commit 36c17bbedc
2 changed files with 156 additions and 48 deletions

View File

@ -63,6 +63,8 @@ class AttributionReport(object):
salesforce_df = self._get_dataframe_by_extension(fname, date_cols=[self.SF_DATE_COLUMN, ]) salesforce_df = self._get_dataframe_by_extension(fname, date_cols=[self.SF_DATE_COLUMN, ])
except IndexError: except IndexError:
return False return False
except ValueError:
return False
except: except:
raise raise
@ -201,11 +203,11 @@ class AttributionReport(object):
for key, df in self.frames: for key, df in self.frames:
fname = '{0}_Attribution_Report_{1}_Match.xlsx'.format(datetime.date.today(), key) fname = '{0}_Attribution_Report_{1}_Match.xlsx'.format(datetime.date.today(), key)
xls_path = os.path.join(self.output_dir, fname) output_path = os.path.join(self.output_dir, fname)
deduped_df = df.drop_duplicates() deduped_df = df.drop_duplicates()
with pd.ExcelWriter(xls_path, engine='xlsxwriter') as writer: with pd.ExcelWriter(output_path, engine='xlsxwriter') as writer:
deduped_df.to_excel(writer, sheet_name='Sheet1', index=False) deduped_df.to_excel(writer, sheet_name='Sheet1', index=False)
# Open the window where the files are # Open the window where the files are

198
main.py
View File

@ -2,7 +2,7 @@ from attribution import AttributionReport
__author__ = 'tsouza' __author__ = 'tsouza'
import Tkinter as tk import Tkinter as tk
from ttk import Progressbar from ttk import Progressbar, Combobox
import tkFileDialog import tkFileDialog
import tkMessageBox import tkMessageBox
from utils import get_dropbox_dir, ThreadedTask from utils import get_dropbox_dir, ThreadedTask
@ -12,6 +12,10 @@ import Queue
class AttributeGUI(tk.Tk): class AttributeGUI(tk.Tk):
def __init__(self): def __init__(self):
tk.Tk.__init__(self) tk.Tk.__init__(self)
self.months = 6
self.footer_length = 6
self.parent = self
self.salesforce_filename = None self.salesforce_filename = None
self.deposit_filename = None self.deposit_filename = None
self.output_directoryname = None self.output_directoryname = None
@ -19,17 +23,24 @@ class AttributeGUI(tk.Tk):
self.defaults = { self.defaults = {
'sf_label': u"<SalesForce Export File...>", 'sf_label': u"<SalesForce Export File...>",
'dp_label': u"<Deposit Export File...>", 'dp_label': u"<Deposit Export File...>",
'output_label': u"<Output directory...>" 'output_label': u"<Output directory...>",
'sf_footer_label': u"SalesForce Footer Rows",
'months_label': u"# Months to Run Report"
} }
self.initGUI() self.initGUI()
self.reset() self.reset()
def reset(self): def reset(self):
self.report = AttributionReport(months=6, footer_length=6) # TODO MAKE MONTHS A DROPDOWN """
Reset everything to a usable state.
"""
self.report = AttributionReport(months=self.months,
footer_length=self.footer_length) # TODO MAKE MONTHS A DROPDOWN
self.input_frame.sf_label_var.set(self.defaults['sf_label']) self.input_frame.sf_label_var.set(self.defaults['sf_label'])
self.input_frame.dp_label_var.set(self.defaults['dp_label']) self.input_frame.dp_label_var.set(self.defaults['dp_label'])
self.output_frame.output_label_var.set(self.defaults['output_label']) self.output_frame.output_label_var.set(self.defaults['output_label'])
self.run_frame.run_button.config(text="Run") self.run_frame.run_button.config(text="Run")
self.enable_all()
self._check_ready_to_run() self._check_ready_to_run()
def initGUI(self): def initGUI(self):
@ -38,23 +49,29 @@ class AttributeGUI(tk.Tk):
self.minsize(width=300, height=200) self.minsize(width=300, height=200)
# Input # Input
row = 0
self.input_frame = self.setup_input_frame() self.input_frame = self.setup_input_frame()
self.input_frame.grid(row=0, column=0) self.input_frame.grid(row=row, column=0)
self.rowconfigure(0, weight=0) self.rowconfigure(row, weight=0)
# Output # Output
row += 1
self.output_frame = self.setup_output_frame() self.output_frame = self.setup_output_frame()
self.output_frame.grid(row=1, column=0) self.output_frame.grid(row=row, column=0)
self.rowconfigure(1, weight=0) self.rowconfigure(row, weight=0)
# Options
row += 1
self.options_frame = self.setup_options_frame()
self.options_frame.grid(row=row, column=0)
self.rowconfigure(row, weight=0)
# Run # Run
row += 1
self.run_frame = self.setup_run_frame() self.run_frame = self.setup_run_frame()
self.run_frame.grid(row=2, column=0) self.run_frame.grid(row=row, column=0)
self.rowconfigure(2, weight=0) self.rowconfigure(row, weight=0)
# Progress bar
self.progress_bar = Progressbar(self, mode="indeterminate")
self.progress_bar.grid(column=0, row=3)
self.update() self.update()
@ -66,7 +83,7 @@ class AttributeGUI(tk.Tk):
# | _|| / / _ \| |\/| | _|\__ \ # | _|| / / _ \| |\/| | _|\__ \
# |_| |_|_\/_/ \_\_| |_|___|___/ # |_| |_|_\/_/ \_\_| |_|___|___/
def setup_input_frame(self): def setup_input_frame(self):
input_frame = tk.LabelFrame(text="Input Files") input_frame = tk.LabelFrame(self, text="Input Files")
input_frame.grid() input_frame.grid()
row = 0 row = 0
input_frame.sf_button = tk.Button(input_frame, text=u"Browse", command=self.callback_sf_browse_click) input_frame.sf_button = tk.Button(input_frame, text=u"Browse", command=self.callback_sf_browse_click)
@ -92,7 +109,7 @@ class AttributeGUI(tk.Tk):
return input_frame return input_frame
def setup_output_frame(self): def setup_output_frame(self):
output_frame = tk.LabelFrame(text="Output Directory") output_frame = tk.LabelFrame(self, text="Output Directory")
output_frame.grid() output_frame.grid()
row = 0 row = 0
@ -108,6 +125,54 @@ class AttributeGUI(tk.Tk):
output_frame.grid_columnconfigure(0, weight=1) output_frame.grid_columnconfigure(0, weight=1)
return output_frame return output_frame
def setup_options_frame(self):
options_frame = tk.LabelFrame(self, text="Options")
options_frame.grid()
row = 0
options_frame.months_label_var = tk.StringVar()
options_frame.months_label = tk.Label(options_frame,
textvariable=options_frame.months_label_var,
anchor="w")
options_frame.months_label.grid(column=0,
row=row,
sticky='nsew')
options_frame.months_label_var.set(self.defaults['months_label'])
options_frame.months_value = tk.IntVar()
options_frame.months_box = Combobox(options_frame,
textvariable=options_frame.months_value,
state="readonly")
options_frame.months_box['values'] = range(1, 12)
options_frame.months_box.current(5)
options_frame.months_box.grid(column=1,
row=row)
options_frame.months_box.bind("<<ComboboxSelected>>",
self.callback_change_months)
row += 1
options_frame.footer_label_var = tk.StringVar()
options_frame.footer_label = tk.Label(options_frame,
textvariable=options_frame.footer_label_var,
anchor="w")
options_frame.footer_label.grid(column=0,
row=row,
sticky='nsew')
options_frame.footer_label_var.set(self.defaults['sf_footer_label'])
options_frame.footer_value = tk.IntVar()
options_frame.footer_box = Combobox(options_frame,
textvariable=options_frame.footer_value,
state="readonly")
options_frame.footer_box['values'] = range(0, 10)
options_frame.footer_box.current(6)
options_frame.footer_box.grid(column=1,
row=row)
options_frame.footer_box.bind("<<ComboboxSelected>>", self.callback_change_footer)
options_frame.grid_columnconfigure(0, weight=1)
return options_frame
def setup_run_frame(self): def setup_run_frame(self):
run_frame = tk.LabelFrame(text="Output Directory") run_frame = tk.LabelFrame(text="Output Directory")
run_frame.grid() run_frame.grid()
@ -115,6 +180,10 @@ class AttributeGUI(tk.Tk):
run_frame.run_button = tk.Button(run_frame, text=u"Run", command=self.callback_run_click) run_frame.run_button = tk.Button(run_frame, text=u"Run", command=self.callback_run_click)
run_frame.run_button.grid(column=0, row=row) run_frame.run_button.grid(column=0, row=row)
row += 1
self.progress_bar = Progressbar(run_frame, mode="indeterminate")
self.progress_bar.grid(column=0, row=row)
run_frame.grid_columnconfigure(0, weight=1) run_frame.grid_columnconfigure(0, weight=1)
return run_frame return run_frame
@ -123,55 +192,38 @@ class AttributeGUI(tk.Tk):
# | (__ / _ \| |__| |__| _ \/ _ \ (__| ' <\__ \ # | (__ / _ \| |__| |__| _ \/ _ \ (__| ' <\__ \
# \___/_/ \_\____|____|___/_/ \_\___|_|\_\___/ # \___/_/ \_\____|____|___/_/ \_\___|_|\_\___/
def callback_sf_browse_click(self): def callback_sf_browse_click(self):
self.salesforce_filename = tkFileDialog.askopenfilename( """
title="Salesforce Export File", Get the filename from a prompt, set the dataframe, show errors if any, check if able to run now.
initialdir=get_dropbox_dir(), """
filetypes=( self.salesforce_filename = self._get_file(title="Salesforce Export File")
("Excel", "*.csv"),
("Excel", "*.xls"),
("Excel", "*.xlsx"),
)
)
if self.salesforce_filename: if self.salesforce_filename:
df_parse_successful = self.report.set_dataframe_sf(self.salesforce_filename) df_parse_successful = self.report.set_dataframe_sf(self.salesforce_filename)
if df_parse_successful: if df_parse_successful:
self.input_frame.sf_label_var.set(self.salesforce_filename) self.input_frame.sf_label_var.set(self.salesforce_filename)
else: else:
self.input_frame.sf_label_var.set(self.defaults['sf_label']) self.input_frame.sf_label_var.set(self.defaults['sf_label'])
tkMessageBox.showerror( self._show_column_error("Salesforce", self.report.REQUIRED_SF_COLUMNS)
"Column Mismatch",
("At a minimum, the Salesforce file must have the following columns:\n\n"
"{0}\n\n"
"Please re-run and select a proper file.".format(", ".join(self.report.REQUIRED_SF_COLUMNS))
)
)
self._check_ready_to_run() self._check_ready_to_run()
def callback_dp_browse_click(self): def callback_dp_browse_click(self):
self.deposit_filename = tkFileDialog.askopenfilename( """
title="Deposit Data Export File", Get the filename from a prompt, set the dataframe, show errors if any, check if able to run now.
initialdir=get_dropbox_dir(), """
filetypes=( self.deposit_filename = self._get_file(title="Deposit Data Export File")
("Excel", "*.csv"),
("Excel", "*.xls"),
("Excel", "*.xlsx"),
)
)
if self.deposit_filename: if self.deposit_filename:
df_parse_successful = self.report.set_dataframe_deposit(self.deposit_filename) df_parse_successful = self.report.set_dataframe_deposit(self.deposit_filename)
if df_parse_successful: if df_parse_successful:
self.input_frame.dp_label_var.set(self.deposit_filename) self.input_frame.dp_label_var.set(self.deposit_filename)
else: else:
self.input_frame.dp_label_var.set(self.defaults['dp_label']) self.input_frame.dp_label_var.set(self.defaults['dp_label'])
tkMessageBox.showerror( self._show_column_error("Deposit Data", self.report.REQUIRED_DP_COLUMNS)
"Column Mismatch",
"At a minimum, the Deposit Data file must have the following columns:\n\n"
"{0}\n\n"
"Please re-run and select a proper file.".format(
",\n".join(self.report.REQUIRED_DP_COLUMNS)))
self._check_ready_to_run() self._check_ready_to_run()
def callback_output_browse_click(self): def callback_output_browse_click(self):
"""
Get the directory name from a prompt, and set the report directory name.
"""
self.output_directoryname = tkFileDialog.askdirectory( self.output_directoryname = tkFileDialog.askdirectory(
initialdir=get_dropbox_dir(), initialdir=get_dropbox_dir(),
) )
@ -180,17 +232,48 @@ class AttributeGUI(tk.Tk):
self.output_frame.output_label_var.set(self.output_directoryname) self.output_frame.output_label_var.set(self.output_directoryname)
self._check_ready_to_run() self._check_ready_to_run()
# Helpers
def disable_all(self):
"""
Disable all inputs.
"""
self.input_frame.sf_button.config(state=tk.DISABLED)
self.input_frame.dp_button.config(state=tk.DISABLED)
self.output_frame.output_button.config(state=tk.DISABLED)
self.options_frame.months_box.config(state=tk.DISABLED)
self.options_frame.footer_box.config(state=tk.DISABLED)
self.run_frame.run_button.config(state=tk.DISABLED)
def enable_all(self):
"""
Enable all inputs.
"""
self.input_frame.sf_button.config(state=tk.NORMAL)
self.input_frame.dp_button.config(state=tk.NORMAL)
self.output_frame.output_button.config(state=tk.NORMAL)
self.options_frame.months_box.config(state=tk.NORMAL)
self.options_frame.footer_box.config(state=tk.NORMAL)
self.run_frame.run_button.config(state=tk.NORMAL)
def callback_run_click(self): def callback_run_click(self):
""" """
When you click run, the progress bar and button notify you that something is happening. When you click run, the progress bar and button notify you that something is happening.
This then starts a thread that sets the report Running and report Saving. This then starts a thread that sets the report Running and report Saving.
""" """
self.disable_all()
self.progress_bar.start() self.progress_bar.start()
self.run_frame.run_button.config(text="Running...") self.run_frame.run_button.config(text="Running...")
self.queue = Queue.Queue() self.queue = Queue.Queue()
ThreadedTask(self.queue, self.report).start() ThreadedTask(self.queue, self.report).start()
self.after(100, self._process_queue) self.after(100, self._process_queue)
def callback_change_months(self, event):
self.months = self.options_frame.months_box.get()
def callback_change_footer(self, event):
self.months = self.options_frame.footer_box.get()
# Private methods
def _check_ready_to_run(self): def _check_ready_to_run(self):
""" """
Disables and re-enables the Run button when criteria is met. Disables and re-enables the Run button when criteria is met.
@ -203,6 +286,29 @@ class AttributeGUI(tk.Tk):
self.run_frame.run_button.config(state=tk.DISABLED) self.run_frame.run_button.config(state=tk.DISABLED)
self.update() self.update()
def _get_file(self, title):
return tkFileDialog.askopenfilename(
title=title,
initialdir=get_dropbox_dir(),
filetypes=(
("Excel", "*.csv"),
("Excel", "*.xls"),
("Excel", "*.xlsx"),
)
)
def _show_column_error(self, title, columns):
"""
Show an error box
"""
tkMessageBox.showerror(
"Column Mismatch",
"At a minimum, the {0} file must have the following columns:\n\n"
"{1}\n\n"
"Please re-run and select a proper file with the correct columns.".format(
title,
",\n".join(columns)))
def _process_queue(self): def _process_queue(self):
""" """
Multi-threading the running stuff so that the UI doesn't lock. Multi-threading the running stuff so that the UI doesn't lock.