import tkinter as tk from tkinter import ttk, messagebox, filedialog import oracledb import pandas as pd import configparser import os class OracleDataExplorer: def __init__(self, root): self.root = root self.root.title("Oracle Database Explorer") self.root.geometry("1400x900") # Initialize configuration self.config = configparser.ConfigParser() self.config_file = "config.ini" self.load_config() # Database connection self.connection = None self.connected = False # Table configuration self.table_config = { "cart": { "order_no_col": "ord_no", "loc_code_col": "loc_cd", "query_by": "cart_id" }, "ace_order": { "order_no_col": "ord_no", "app_num_col": "card_app_num" }, "orders": { "order_no_col": "order_number", "query_by": "order_number" } # Add all tables here } # Create UI self.create_connection_frame() self.create_search_frame() self.create_results_frame() self.create_export_frame() self.create_config_frame() # Load saved credentials self.load_saved_credentials() def load_config(self): if not os.path.exists(self.config_file): self.config['DATABASE'] = { 'username': '', 'password': '', 'dsn': 'localhost:1521/ORCLPDB1', 'save_credentials': 'False' } self.save_config() else: self.config.read(self.config_file) def save_config(self): with open(self.config_file, 'w') as configfile: self.config.write(configfile) def load_saved_credentials(self): if self.config.getboolean('DATABASE', 'save_credentials', fallback=False): self.username.insert(0, self.config['DATABASE'].get('username', '')) self.password.insert(0, self.config['DATABASE'].get('password', '')) self.dsn.delete(0, tk.END) self.dsn.insert(0, self.config['DATABASE'].get('dsn', 'localhost:1521/ORCLPDB1')) self.save_creds_var.set(True) def create_connection_frame(self): frame = ttk.LabelFrame(self.root, text="Database Connection", padding=10) frame.grid(row=0, column=0, columnspan=2, padx=10, pady=5, sticky="ew") ttk.Label(frame, text="Username:").grid(row=0, column=0, sticky="e") self.username = ttk.Entry(frame) self.username.grid(row=0, column=1, padx=5, pady=2, sticky="ew") ttk.Label(frame, text="Password:").grid(row=1, column=0, sticky="e") self.password = ttk.Entry(frame, show="*") self.password.grid(row=1, column=1, padx=5, pady=2, sticky="ew") ttk.Label(frame, text="DSN:").grid(row=2, column=0, sticky="e") self.dsn = ttk.Entry(frame) self.dsn.grid(row=2, column=1, padx=5, pady=2, sticky="ew") self.connect_btn = ttk.Button(frame, text="Connect", command=self.connect_db) self.connect_btn.grid(row=3, column=0, columnspan=2, pady=5) def create_config_frame(self): frame = ttk.LabelFrame(self.root, text="Configuration", padding=10) frame.grid(row=3, column=0, columnspan=2, padx=10, pady=5, sticky="ew") self.save_creds_var = tk.BooleanVar(value=False) ttk.Checkbutton( frame, text="Save credentials (not recommended for shared computers)", variable=self.save_creds_var ).grid(row=0, column=0, sticky="w") ttk.Button( frame, text="Save Configuration", command=self.save_current_config ).grid(row=0, column=1, sticky="e") def save_current_config(self): self.config['DATABASE']['username'] = self.username.get() self.config['DATABASE']['password'] = self.password.get() self.config['DATABASE']['dsn'] = self.dsn.get() self.config['DATABASE']['save_credentials'] = str(self.save_creds_var.get()) self.save_config() messagebox.showinfo("Success", "Configuration saved to config.ini") def create_search_frame(self): frame = ttk.LabelFrame(self.root, text="Search Criteria", padding=10) frame.grid(row=1, column=0, padx=10, pady=5, sticky="nsew") self.search_mode = tk.StringVar(value="cart_id") ttk.Radiobutton(frame, text="Search by Cart ID", variable=self.search_mode, value="cart_id").grid(row=0, column=0, sticky="w") ttk.Radiobutton(frame, text="Search by Order No + Loc Code", variable=self.search_mode, value="order_loc").grid(row=1, column=0, sticky="w") ttk.Label(frame, text="Cart ID:").grid(row=2, column=0, sticky="e") self.cart_id_entry = ttk.Entry(frame) self.cart_id_entry.grid(row=2, column=1, padx=5, pady=2, sticky="ew") ttk.Label(frame, text="Order No:").grid(row=3, column=0, sticky="e") self.order_no_entry = ttk.Entry(frame) self.order_no_entry.grid(row=3, column=1, padx=5, pady=2, sticky="ew") ttk.Label(frame, text="Loc Code:").grid(row=4, column=0, sticky="e") self.loc_code_entry = ttk.Entry(frame) self.loc_code_entry.grid(row=4, column=1, padx=5, pady=2, sticky="ew") self.search_btn = ttk.Button(frame, text="Search", command=self.search_data, state=tk.DISABLED) self.search_btn.grid(row=5, column=0, columnspan=2, pady=5) self.search_mode.trace("w", self.update_search_ui) self.update_search_ui() def create_results_frame(self): frame = ttk.LabelFrame(self.root, text="Results", padding=10) frame.grid(row=1, column=1, rowspan=2, padx=10, pady=5, sticky="nsew") # Create notebook for multiple tables self.results_notebook = ttk.Notebook(frame) self.results_notebook.pack(fill=tk.BOTH, expand=True) # Search within results search_frame = ttk.Frame(frame) search_frame.pack(fill=tk.X, pady=5) ttk.Label(search_frame, text="Search in results:").pack(side=tk.LEFT) self.search_entry = ttk.Entry(search_frame) self.search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) ttk.Button(search_frame, text="Find", command=self.find_in_results).pack(side=tk.LEFT) self.root.columnconfigure(1, weight=1) self.root.rowconfigure(1, weight=1) def create_export_frame(self): frame = ttk.Frame(self.root) frame.grid(row=2, column=0, padx=10, pady=5, sticky="ew") self.export_btn = ttk.Button(frame, text="Export to Excel", command=self.export_to_excel, state=tk.DISABLED) self.export_btn.pack(pady=5) def update_search_ui(self, *args): mode = self.search_mode.get() if mode == "cart_id": self.cart_id_entry.config(state=tk.NORMAL) self.order_no_entry.config(state=tk.DISABLED) self.loc_code_entry.config(state=tk.DISABLED) else: self.cart_id_entry.config(state=tk.DISABLED) self.order_no_entry.config(state=tk.NORMAL) self.loc_code_entry.config(state=tk.NORMAL) def connect_db(self): try: if self.save_creds_var.get(): self.save_current_config() self.connection = oracledb.connect( user=self.username.get(), password=self.password.get(), dsn=self.dsn.get() ) self.connected = True self.connect_btn.config(text="Connected", style="Success.TButton") self.search_btn.config(state=tk.NORMAL) messagebox.showinfo("Success", "Database connection established") except Exception as e: messagebox.showerror("Connection Error", f"Failed to connect: {str(e)}") self.connected = False self.connect_btn.config(text="Connect") def get_join_condition(self, table): config = self.table_config.get(table, {}) if "query_by" not in config: return None if config["query_by"] == "cart_id": if "cart_id" not in self.base_params: return None return { "join_on": f"{config.get('order_no_col', 'ord_no')} = rd_no", "params": {"ord_no": self.base_params["ord_no"]} } elif isinstance(config["query_by"], list): conditions = [] params = {} for col in config["query_by"]: if col+"_col" in config and col in self.base_params: db_col = config[col+"_col"] conditions.append(f"{db_col} = :{col}") params[col] = self.base_params[col] if not conditions: return None return {"join_on": " AND ".join(conditions), "params": params} else: if config["query_by"] not in self.base_params: return None db_col = config.get(config["query_by"]+"_col", config["query_by"]) return { "join_on": f"{db_col} = :{config['query_by']}", "params": {config["query_by"]: self.base_params[config["query_by"]]} } def search_data(self): if not self.connected: messagebox.showerror("Error", "Not connected to database") return # Clear previous results for child in self.results_notebook.winfo_children(): child.destroy() # Get base parameters search_mode = self.search_mode.get() try: with self.connection.cursor() as cursor: if search_mode == "cart_id": cart_id = self.cart_id_entry.get() if not cart_id: messagebox.showerror("Error", "Please enter Cart ID") return # Get cart details cursor.execute(f""" SELECT {self.table_config['cart']['order_no_col']}, {self.table_config['cart']['loc_code_col']} FROM cart WHERE cart_id = :cart_id """, {"cart_id": cart_id}) cart_result = cursor.fetchone() if not cart_result: messagebox.showerror("Error", f"No cart found with ID: {cart_id}") return ord_no, loc_cd = cart_result self.base_params = { "cart_id": cart_id, "ord_no": ord_no, "loc_cd": loc_cd } else: ord_no = self.order_no_entry.get() loc_cd = self.loc_code_entry.get() if not ord_no or not loc_cd: messagebox.showerror("Error", "Please enter both Order No and Loc Code") return self.base_params = { "ord_no": ord_no, "loc_cd": loc_cd } # Get card_app_num from ace_order if table exists if "ace_order" in self.table_config: cursor.execute(f""" SELECT {self.table_config['ace_order']['app_num_col']} FROM ace_order WHERE {self.table_config['ace_order']['order_no_col']} = rd_no """, {"ord_no": self.base_params["ord_no"]}) ace_result = cursor.fetchone() if ace_result: self.base_params["card_app_num"] = ace_result[0] # Query all configured tables self.all_data = {} tables_queried = 0 for table in self.table_config.keys(): join_condition = self.get_join_condition(table) if not join_condition: continue try: query = f"SELECT * FROM {table} WHERE {join_condition['join_on']}" cursor.execute(query, join_condition["params"]) columns = [col[0] for col in cursor.description] data = cursor.fetchall() if data: self.all_data[table] = { "columns": columns, "data": data, "query": query, "params": join_condition["params"] } self.display_table_data(table, columns, data) tables_queried += 1 except oracledb.DatabaseError as e: print(f"Error querying {table}: {e}") continue if tables_queried == 0: messagebox.showinfo("No Results", "No matching records found in any table") self.export_btn.config(state=tk.DISABLED) else: self.export_btn.config(state=tk.NORMAL) messagebox.showinfo("Search Complete", f"Found results in {tables_queried} tables") except Exception as e: messagebox.showerror("Search Error", f"An error occurred: {str(e)}") def display_table_data(self, table_name, columns, data): # Create a frame for this table frame = ttk.Frame(self.results_notebook) self.results_notebook.add(frame, text=table_name) # Create a canvas for scrolling canvas = tk.Canvas(frame, borderwidth=0) vsb = ttk.Scrollbar(frame, orient="vertical", command=canvas.yview) hsb = ttk.Scrollbar(frame, orient="horizontal", command=canvas.xview) canvas.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) vsb.pack(side="right", fill="y") hsb.pack(side="bottom", fill="x") canvas.pack(side="left", fill="both", expand=True) # Create the table inside canvas table_frame = ttk.Frame(canvas) canvas.create_window((0,0), window=table_frame, anchor="nw") # Configure scrolling def on_frame_configure(event): canvas.configure(scrollregion=canvas.bbox("all")) table_frame.bind("<Configure>", on_frame_configure) # Bind mouse wheel for scrolling def on_mousewheel(event): canvas.yview_scroll(int(-1*(event.delta/120)), "units") canvas.bind_all("<MouseWheel>", on_mousewheel) # Display records based on count if len(data) == 1: # Single record - display vertically for i, (col, val) in enumerate(zip(columns, data[0])): row_frame = ttk.Frame(table_frame) row_frame.pack(fill=tk.X, padx=5, pady=2) ttk.Label(row_frame, text=f"{col}:", width=20, anchor="e").pack(side=tk.LEFT) ttk.Label(row_frame, text=str(val), anchor="w").pack(side=tk.LEFT, fill=tk.X, expand=True) else: # Multiple records - display horizontally in a table tree = ttk.Treeview(table_frame, columns=columns, show="headings") for col in columns: tree.heading(col, text=col) tree.column(col, width=120, anchor="w", stretch=tk.YES) for row in data: tree.insert("", tk.END, values=row) tree.pack(fill=tk.BOTH, expand=True) # Enable copying def copy_selection(): selected = tree.selection() if selected: items = [tree.item(item)['values'] for item in selected] self.root.clipboard_clear() self.root.clipboard_append("\n".join("\t".join(map(str, item)) for item in items)) # Context menu menu = tk.Menu(self.root, tearoff=0) menu.add_command(label="Copy Selected", command=copy_selection) menu.add_command(label="Copy All", command=lambda: self.copy_all_data(table_name)) def show_menu(event): menu.tk_popup(event.x_root, event.y_root) tree.bind("<Button-3>", show_menu) def copy_all_data(self, table_name): if table_name in self.all_data: data = self.all_data[table_name] df = pd.DataFrame(data["data"], columns=data["columns"]) self.root.clipboard_clear() self.root.clipboard_append(df.to_string(index=False)) def find_in_results(self): search_term = self.search_entry.get().lower() if not search_term: return current_tab = self.results_notebook.tab(self.results_notebook.select(), "text") if current_tab not in self.all_data: return tab_frame = self.results_notebook.nametowidget(self.results_notebook.select()) canvas = tab_frame.winfo_children()[0] table_frame = canvas.winfo_children()[0] # Handle both single record and table views if len(self.all_data[current_tab]["data"]) == 1: # Single record view - search in labels for widget in table_frame.winfo_children(): if isinstance(widget, ttk.Frame): for label in widget.winfo_children(): if isinstance(label, ttk.Label) and search_term in label.cget("text").lower(): widget.config(style="Highlight.TFrame") canvas.yview_moveto(widget.winfo_y()/table_frame.winfo_height()) else: # Table view - search in treeview tree = table_frame.winfo_children()[0] for item in tree.get_children(): tree.item(item, tags=()) found = False for item in tree.get_children(): values = tree.item(item)['values'] for value in values: if search_term in str(value).lower(): tree.item(item, tags=("found",)) found = True tree.see(item) break if found: tree.tag_configure("found", background="yellow") def export_to_excel(self): if not hasattr(self, 'all_data') or not self.all_data: messagebox.showerror("Error", "No data to export") return file_path = filedialog.asksaveasfilename( defaultextension=".xlsx", filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")] ) if not file_path: return try: with pd.ExcelWriter(file_path) as writer: for table_name, table_data in self.all_data.items(): df = pd.DataFrame(table_data["data"], columns=table_data["columns"]) df.to_excel(writer, sheet_name=table_name[:31], index=False) messagebox.showinfo("Success", f"Data exported to {file_path}") except Exception as e: messagebox.showerror("Export Error", f"Failed to export: {str(e)}") if __name__ == "__main__": root = tk.Tk() try: oracledb.init_oracle_client() except: pass # Thin mode will be used app = OracleDataExplorer(root) root.mainloop() Continue reading...