GUI Chnages, progress bars, more error handling.
This commit is contained in:
parent
864622bdf4
commit
36c17bbedc
|
@ -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
198
main.py
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user