from attribution import AttributionReport __author__ = 'tsouza' import Tkinter as tk from ttk import Progressbar, Combobox import tkFileDialog import tkMessageBox from utils import get_dropbox_dir, ThreadedTask import Queue class AttributeGUI(tk.Tk): def __init__(self): tk.Tk.__init__(self) self.months = 6 self.footer_length = 6 self.parent = self self.salesforce_filename = None self.deposit_filename = None self.output_directoryname = None self.defaults = { 'sf_label': u"", 'dp_label': u"", 'output_label': u"", 'sf_footer_label': u"SalesForce Footer Rows", 'months_label': u"# Months to Run Report" } self.initGUI() self.reset() def reset(self): """ 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.dp_label_var.set(self.defaults['dp_label']) self.output_frame.output_label_var.set(self.defaults['output_label']) self.run_frame.run_button.config(text="Run") self.enable_all() self._check_ready_to_run() def initGUI(self): self.title('Attribution Report') self.grid() self.minsize(width=300, height=200) # Input row = 0 self.input_frame = self.setup_input_frame() self.input_frame.grid(row=row, column=0) self.rowconfigure(row, weight=0) # Output row += 1 self.output_frame = self.setup_output_frame() self.output_frame.grid(row=row, column=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 row += 1 self.run_frame = self.setup_run_frame() self.run_frame.grid(row=row, column=0) self.rowconfigure(row, weight=0) self.update() self.minsize(self.winfo_width(), self.winfo_height()) self.resizable(True, False) # ___ ___ _ __ __ ___ ___ # | __| _ \ /_\ | \/ | __/ __| # | _|| / / _ \| |\/| | _|\__ \ # |_| |_|_\/_/ \_\_| |_|___|___/ def setup_input_frame(self): input_frame = tk.LabelFrame(self, text="Input Files") input_frame.grid() row = 0 input_frame.sf_button = tk.Button(input_frame, text=u"Browse", command=self.callback_sf_browse_click) input_frame.sf_button.grid(column=0, row=row) input_frame.sf_label_var = tk.StringVar() input_frame.sf_label = tk.Label(input_frame, textvariable=input_frame.sf_label_var, anchor="w") input_frame.sf_label.grid(column=1, row=row, sticky='nsew') input_frame.sf_label_var.set(self.defaults['sf_label']) row += 1 input_frame.dp_button = tk.Button(input_frame, text=u"Browse", command=self.callback_dp_browse_click) input_frame.dp_button.grid(column=0, row=row) input_frame.dp_label_var = tk.StringVar() input_frame.dp_label = tk.Label(input_frame, textvariable=input_frame.dp_label_var, anchor="w") input_frame.dp_label.grid(column=1, row=row, sticky='nsew') input_frame.dp_label_var.set(self.defaults['dp_label']) input_frame.grid_columnconfigure(1, weight=1) return input_frame def setup_output_frame(self): output_frame = tk.LabelFrame(self, text="Output Directory") output_frame.grid() row = 0 output_frame.output_button = tk.Button(output_frame, text=u"Browse", command=self.callback_output_browse_click) output_frame.output_button.grid(column=0, row=row) output_frame.output_label_var = tk.StringVar() output_frame.sf_label = tk.Label(output_frame, textvariable=output_frame.output_label_var, anchor="w") output_frame.sf_label.grid(column=1, row=row, sticky='nsew') output_frame.output_label_var.set(self.defaults['output_label']) output_frame.grid_columnconfigure(0, weight=1) 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("<>", 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("<>", self.callback_change_footer) options_frame.grid_columnconfigure(0, weight=1) return options_frame def setup_run_frame(self): run_frame = tk.LabelFrame(text="Output Directory") run_frame.grid() row = 0 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) 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) return run_frame # ___ _ _ _ ___ _ ___ _ _____ # / __| /_\ | | | | | _ ) /_\ / __| |/ / __| # | (__ / _ \| |__| |__| _ \/ _ \ (__| ' <\__ \ # \___/_/ \_\____|____|___/_/ \_\___|_|\_\___/ def callback_sf_browse_click(self): """ Get the filename from a prompt, set the dataframe, show errors if any, check if able to run now. """ self.salesforce_filename = self._get_file(title="Salesforce Export File") if self.salesforce_filename: df_parse_successful = self.report.set_dataframe_sf(self.salesforce_filename) if df_parse_successful: self.input_frame.sf_label_var.set(self.salesforce_filename) else: self.input_frame.sf_label_var.set(self.defaults['sf_label']) self._show_column_error("Salesforce", self.report.REQUIRED_SF_COLUMNS) self._check_ready_to_run() def callback_dp_browse_click(self): """ Get the filename from a prompt, set the dataframe, show errors if any, check if able to run now. """ self.deposit_filename = self._get_file(title="Deposit Data Export File") if self.deposit_filename: df_parse_successful = self.report.set_dataframe_deposit(self.deposit_filename) if df_parse_successful: self.input_frame.dp_label_var.set(self.deposit_filename) else: self.input_frame.dp_label_var.set(self.defaults['dp_label']) self._show_column_error("Deposit Data", self.report.REQUIRED_DP_COLUMNS) self._check_ready_to_run() def callback_output_browse_click(self): """ Get the directory name from a prompt, and set the report directory name. """ self.output_directoryname = tkFileDialog.askdirectory( initialdir=get_dropbox_dir(), ) if self.output_directoryname: self.report.set_output_dir(self.output_directoryname) self.output_frame.output_label_var.set(self.output_directoryname) 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): """ 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. """ self.disable_all() self.progress_bar.start() self.run_frame.run_button.config(text="Running...") self.queue = Queue.Queue() ThreadedTask(self.queue, self.report).start() 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): """ Disables and re-enables the Run button when criteria is met. """ if self.report.salesforce_df is not None \ and self.report.deposit_df is not None \ and self.report.output_dir: self.run_frame.run_button.config(state=tk.NORMAL) else: self.run_frame.run_button.config(state=tk.DISABLED) 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): """ Multi-threading the running stuff so that the UI doesn't lock. """ try: self.queue.get(0) # Show result of the task if needed self.progress_bar.stop() self.reset() except Queue.Empty: self.after(100, self._process_queue) def main(): app = AttributeGUI() app.mainloop() if __name__ == "__main__": main()