Skip to content

Commit 9a97153

Browse files
jared-duvalclaude
andcommitted
Add new budget template buttons and larger window
- Increase window size to 750x580 for better text visibility on Windows - Add "Start a New Budget" section with 3 buttons (3, 5, 10 year) - Bundle OSP budget templates with the application - Fix Windows "Open Folder" functionality - Update PyInstaller specs to include template files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c1fc6b7 commit 9a97153

File tree

6 files changed

+99
-14
lines changed

6 files changed

+99
-14
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ jobs:
160160
['budget_justification_gui.py'],
161161
pathex=[],
162162
binaries=[('bundled_bin/pandoc.exe', '.')],
163-
datas=[],
163+
datas=[('OSP-3-Year-Budget-Template-August-2025.xlsx', '.'), ('OSP-5-Year-Budget-Template-August-2025.xlsx', '.'), ('OSP-10-Year-Budget-Template-August-2025.xlsx', '.')],
164164
hiddenimports=['openpyxl', 'openpyxl.cell', 'openpyxl.cell._writer', 'openpyxl.styles', 'docx', 'docx.shared', 'docx.enum.text', 'tkinter', 'tkinter.filedialog', 'tkinter.messagebox', 'tkinter.ttk'],
165165
hookspath=[],
166166
hooksconfig={},
226 KB
Binary file not shown.
169 KB
Binary file not shown.
173 KB
Binary file not shown.

budget_justification_gui.py

Lines changed: 93 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import threading
1010
import os
1111
import sys
12+
import shutil
1213
from pathlib import Path
1314

1415
# Import the main script functionality
@@ -62,15 +63,15 @@ def get_pandoc_path():
6263
return bundled_generic
6364

6465
# Fall back to system Pandoc
65-
import shutil
6666
system_pandoc = shutil.which('pandoc')
6767
return system_pandoc
6868

69+
6970
class BudgetJustificationGUI:
7071
def __init__(self, root):
7172
self.root = root
7273
self.root.title("Budget Justification Generator")
73-
self.root.geometry("700x450")
74+
self.root.geometry("750x580")
7475
self.root.resizable(False, False)
7576

7677
# Variables
@@ -79,6 +80,13 @@ def __init__(self, root):
7980
self.status_text = tk.StringVar(value="Ready to generate budget justification")
8081
self.processing = False
8182

83+
# Template filenames
84+
self.templates = {
85+
3: "OSP-3-Year-Budget-Template-August-2025.xlsx",
86+
5: "OSP-5-Year-Budget-Template-August-2025.xlsx",
87+
10: "OSP-10-Year-Budget-Template-August-2025.xlsx"
88+
}
89+
8290
self.create_widgets()
8391

8492
def create_widgets(self):
@@ -92,13 +100,50 @@ def create_widgets(self):
92100
title.pack(pady=20)
93101

94102
# Main content
95-
content = tk.Frame(self.root, padx=30, pady=20)
103+
content = tk.Frame(self.root, padx=30, pady=15)
96104
content.pack(fill=tk.BOTH, expand=True)
97105

106+
# New Budget section
107+
new_budget_frame = tk.LabelFrame(content, text="Start a New Budget",
108+
font=("Arial", 12, "bold"), padx=15, pady=10)
109+
new_budget_frame.pack(fill=tk.X, pady=(0, 12))
110+
111+
# Container for buttons to center them
112+
btn_container = tk.Frame(new_budget_frame)
113+
btn_container.pack()
114+
115+
btn_3yr = tk.Button(btn_container, text="New 3-Year Budget",
116+
command=lambda: self.create_new_budget(3),
117+
font=("Arial", 10), bg="#5C8A4D", fg="white",
118+
activebackground="#4A7340", activeforeground="white",
119+
highlightbackground="#5C8A4D",
120+
padx=12, pady=5, cursor="hand2")
121+
btn_3yr.pack(side=tk.LEFT, padx=5)
122+
123+
btn_5yr = tk.Button(btn_container, text="New 5-Year Budget",
124+
command=lambda: self.create_new_budget(5),
125+
font=("Arial", 10), bg="#5C8A4D", fg="white",
126+
activebackground="#4A7340", activeforeground="white",
127+
highlightbackground="#5C8A4D",
128+
padx=12, pady=5, cursor="hand2")
129+
btn_5yr.pack(side=tk.LEFT, padx=5)
130+
131+
btn_10yr = tk.Button(btn_container, text="New 10-Year Budget",
132+
command=lambda: self.create_new_budget(10),
133+
font=("Arial", 10), bg="#5C8A4D", fg="white",
134+
activebackground="#4A7340", activeforeground="white",
135+
highlightbackground="#5C8A4D",
136+
padx=12, pady=5, cursor="hand2")
137+
btn_10yr.pack(side=tk.LEFT, padx=5)
138+
139+
# Separator
140+
separator = ttk.Separator(content, orient='horizontal')
141+
separator.pack(fill=tk.X, pady=10)
142+
98143
# Excel file selection
99144
file_frame = tk.LabelFrame(content, text="1. Select Excel Budget File",
100-
font=("Arial", 12, "bold"), padx=15, pady=15)
101-
file_frame.pack(fill=tk.X, pady=(0, 15))
145+
font=("Arial", 12, "bold"), padx=15, pady=12)
146+
file_frame.pack(fill=tk.X, pady=(0, 12))
102147

103148
excel_entry = tk.Entry(file_frame, textvariable=self.excel_file,
104149
font=("Arial", 11), state='readonly', width=50)
@@ -114,8 +159,8 @@ def create_widgets(self):
114159

115160
# Output directory selection
116161
output_frame = tk.LabelFrame(content, text="2. Select Output Location",
117-
font=("Arial", 12, "bold"), padx=15, pady=15)
118-
output_frame.pack(fill=tk.X, pady=(0, 15))
162+
font=("Arial", 12, "bold"), padx=15, pady=12)
163+
output_frame.pack(fill=tk.X, pady=(0, 12))
119164

120165
output_entry = tk.Entry(output_frame, textvariable=self.output_dir,
121166
font=("Arial", 11), state='readonly', width=50)
@@ -135,22 +180,53 @@ def create_widgets(self):
135180
font=("Arial", 14, "bold"), bg="#1E7A46", fg="white",
136181
activebackground="#165C35", activeforeground="white",
137182
highlightbackground="#1E7A46",
138-
padx=30, pady=15, cursor="hand2")
139-
self.generate_btn.pack(pady=30)
183+
padx=30, pady=12, cursor="hand2")
184+
self.generate_btn.pack(pady=20)
140185

141186
# Progress bar
142-
self.progress = ttk.Progressbar(content, mode='indeterminate', length=600)
187+
self.progress = ttk.Progressbar(content, mode='indeterminate', length=650)
143188

144189
# Status message
145190
status_label = tk.Label(content, textvariable=self.status_text,
146-
font=("Arial", 10), fg="#555", wraplength=600)
191+
font=("Arial", 10), fg="#555", wraplength=650)
147192
status_label.pack()
148193

149194
# Footer
150195
footer = tk.Label(self.root, text="Created By Jared Duval",
151196
font=("Arial", 9), fg="#666", bg="#f0f0f0", pady=10)
152197
footer.pack(side=tk.BOTTOM, fill=tk.X)
153198

199+
def create_new_budget(self, years):
200+
"""Create a new budget file from template"""
201+
template_name = self.templates.get(years)
202+
if not template_name:
203+
messagebox.showerror("Error", f"No template available for {years}-year budget")
204+
return
205+
206+
template_path = get_bundled_path(template_name)
207+
208+
if not os.path.exists(template_path):
209+
messagebox.showerror("Error", f"Template file not found: {template_name}\n\nPlease ensure the template is bundled with the application.")
210+
return
211+
212+
# Ask user where to save the new budget
213+
save_path = filedialog.asksaveasfilename(
214+
title=f"Save New {years}-Year Budget As",
215+
defaultextension=".xlsx",
216+
filetypes=[("Excel files", "*.xlsx")],
217+
initialfile=f"My_{years}_Year_Budget.xlsx",
218+
initialdir=str(Path.home() / "Desktop")
219+
)
220+
221+
if save_path:
222+
try:
223+
shutil.copy2(template_path, save_path)
224+
self.excel_file.set(save_path)
225+
self.status_text.set(f"Created new {years}-year budget: {os.path.basename(save_path)}")
226+
messagebox.showinfo("Success", f"New {years}-year budget created!\n\nFile: {os.path.basename(save_path)}\n\nOpen this file in Excel to fill in your budget details, then return here to generate the justification.")
227+
except Exception as e:
228+
messagebox.showerror("Error", f"Failed to create budget file:\n\n{e}")
229+
154230
def browse_excel(self):
155231
"""Open file dialog to select Excel file"""
156232
filename = filedialog.askopenfilename(
@@ -275,7 +351,11 @@ def show_success(self, files, basename):
275351
# Ask if user wants to open the output folder
276352
if messagebox.askyesno("Open Folder", "Would you like to open the output folder?"):
277353
import subprocess
278-
subprocess.run(['open', self.output_dir.get()])
354+
import platform
355+
if platform.system() == 'Windows':
356+
os.startfile(self.output_dir.get())
357+
else:
358+
subprocess.run(['open', self.output_dir.get()])
279359

280360
def show_error(self, error_msg):
281361
"""Show error message"""
@@ -287,6 +367,7 @@ def show_error(self, error_msg):
287367
messagebox.showerror("Error", f"Failed to generate budget justification:\n\n{error_msg}")
288368
self.status_text.set("Error occurred. Please try again.")
289369

370+
290371
def main():
291372
root = tk.Tk()
292373
app = BudgetJustificationGUI(root)

budget_justification_gui.spec

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ a = Analysis(
99
('bundled_bin/pandoc-arm64', '.'),
1010
('bundled_bin/pandoc-x86_64', '.'),
1111
],
12-
datas=[],
12+
datas=[
13+
('OSP-3-Year-Budget-Template-August-2025.xlsx', '.'),
14+
('OSP-5-Year-Budget-Template-August-2025.xlsx', '.'),
15+
('OSP-10-Year-Budget-Template-August-2025.xlsx', '.'),
16+
],
1317
hiddenimports=[
1418
'openpyxl',
1519
'openpyxl.cell',

0 commit comments

Comments
 (0)