From 15f2d8dcb8f30da7e4980e331c2912e4451f012f Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Wed, 17 Sep 2025 21:19:01 -0500 Subject: [PATCH 01/13] =?UTF-8?q?Age=20Calculator:=20fix=20leap-year=20fun?= =?UTF-8?q?ction=20and=20add=20time=20summary=20(days=E2=86=92hours/minute?= =?UTF-8?q?s/seconds)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Age Calculator/calculate.py | 45 ++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/Age Calculator/calculate.py b/Age Calculator/calculate.py index 83134f0..a5756bb 100644 --- a/Age Calculator/calculate.py +++ b/Age Calculator/calculate.py @@ -1,25 +1,25 @@ import time from calendar import isleap -def judge_leap_year(year): - if isleap(year): - return True - else: - return False +# Check if a year is a leap year +def judge_leap(year: int) -> bool: + return isleap(year) -def month_days(month, leap_year): +# Return number of days in a month, considering leap years +def month_days(month: int, leap_year: bool) -> int: if month in [1, 3, 5, 7, 8, 10, 12]: return 31 elif month in [4, 6, 9, 11]: return 30 elif month == 2 and leap_year: return 29 - elif month == 2 and (not leap_year): + else: return 28 - +# User input name = input("Please enter your name: ") -age = input("Please enter your age: ") +age = int(input("Please enter your age: ")) + localtime = time.localtime(time.time()) year = int(age) @@ -29,16 +29,25 @@ def month_days(month, leap_year): begin_year = int(localtime.tm_year) - year end_year = begin_year + year +# Count days in past years for y in range(begin_year, end_year): - if (judge_leap_year(y)): - day = day + 366 + if judge_leap(y): + day += 366 else: - day = day + 365 + day += 365 -leap_year = judge_leap_year(localtime.tm_year) +# Add days from current year +leap_year = judge_leap(localtime.tm_year) for m in range(1, localtime.tm_mon): - day = day + month_days(m, leap_year) - -day = day + localtime.tm_mday -print("\n\t%s's age is %d years or " % (name, year), end="") -print("%d months or %d days" % (month, day)) + day += month_days(m, leap_year) + +# Approximate breakdown (ignores time of day) +hours = day * 24 +minutes = hours * 60 +seconds = minutes * 60 + +print(f"\nHello {name}, you are approximately:") +print(f" {day:,} days") +print(f" {hours:,} hours") +print(f" {minutes:,} minutes") +print(f" {seconds:,} seconds old!") From db19bc8923ed1456bd946059e07fb515419e77f8 Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Thu, 18 Sep 2025 08:33:51 -0500 Subject: [PATCH 02/13] Digital Clock: add separate date label below time --- Digital Clock/main.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Digital Clock/main.py b/Digital Clock/main.py index 831562b..d65d7c5 100644 --- a/Digital Clock/main.py +++ b/Digital Clock/main.py @@ -12,6 +12,13 @@ ) clock_label.place(x=50, y=50) +# NEW date label +date_label = Label( + window, bg="black", fg="white", font=("Arial", 14) +) +date_label.pack(pady=(0, 10), anchor="center") + + def update_label(): current_time = strftime("%H: %M: %S\n %d-%m-%Y ") @@ -20,4 +27,8 @@ def update_label(): clock_label.pack(anchor="center") update_label() -window.mainloop() \ No newline at end of file +window.mainloop()def update_label(): + clock_label.configure(text=strftime("%H:%M:%S")) + date_label.configure(text=strftime("%A, %b %d, %Y")) + clock_label.after(1000, update_label) + From 0173fc92fb118a40a50d037f1c60e729b57e78b8 Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Thu, 18 Sep 2025 08:41:16 -0500 Subject: [PATCH 03/13] Digital Clock: add 12/24-hour toggle button and 'f' shortcut --- Digital Clock/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Digital Clock/main.py b/Digital Clock/main.py index d65d7c5..3266afa 100644 --- a/Digital Clock/main.py +++ b/Digital Clock/main.py @@ -1,5 +1,5 @@ from time import strftime -from tkinter import Label, Tk +from tkinter import Label, Tk , Button window = Tk() window.title("Digital Clock") @@ -27,7 +27,7 @@ def update_label(): clock_label.pack(anchor="center") update_label() -window.mainloop()def update_label(): +window.mainloop() def update_label(): clock_label.configure(text=strftime("%H:%M:%S")) date_label.configure(text=strftime("%A, %b %d, %Y")) clock_label.after(1000, update_label) From 026b56e1b9e2de9cd2234f49e7ce584e8d03c5a8 Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Thu, 18 Sep 2025 08:53:14 -0500 Subject: [PATCH 04/13] Digital Clock: consolidate update loop and refresh every second --- Digital Clock/main.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/Digital Clock/main.py b/Digital Clock/main.py index 3266afa..632af37 100644 --- a/Digital Clock/main.py +++ b/Digital Clock/main.py @@ -7,12 +7,14 @@ window.configure(bg="green") window.resizable(False, False) +use_24h = True + clock_label = Label( window, bg="black", fg="green", font=("Arial", 30, "bold"), relief="flat" ) clock_label.place(x=50, y=50) -# NEW date label + date_label = Label( window, bg="black", fg="white", font=("Arial", 14) ) @@ -20,15 +22,29 @@ +def toggle_format(_evt=None): + global use_24h + use_24h = not use_24h + fmt_btn.config(text="Switch to 24-hour" if not use_24h else "Switch to 12-hour") + +fmt_btn = Button(window, text="Switch to 12-hour", command=toggle_format) +fmt_btn.pack(pady=(0, 8)) +window.bind("", toggle_format) # press 'f' to toggle + + + def update_label(): - current_time = strftime("%H: %M: %S\n %d-%m-%Y ") - clock_label.configure(text=current_time) - clock_label.after(80, update_label) - clock_label.pack(anchor="center") + + if use_24h: + time_text = strftime("%H:%M:%S") + else: + # strip leading zero in 12h mode for a cleaner look + time_text = strftime("%I:%M:%S %p").lstrip("0") + clock_label.configure(text=time_text) + date_label.configure(text=strftime("%A, %b %d, %Y")) + window.after(1000, update_label) update_label() -window.mainloop() def update_label(): - clock_label.configure(text=strftime("%H:%M:%S")) - date_label.configure(text=strftime("%A, %b %d, %Y")) - clock_label.after(1000, update_label) +window.mainloop() + From 0a32153debe0040f48bdcca55641f95384a461f7 Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Thu, 18 Sep 2025 21:03:18 -0500 Subject: [PATCH 05/13] Geographic Distance: add validate_coordinates() helper and use it in main() --- Geographic Distance/geographic_distance.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Geographic Distance/geographic_distance.py b/Geographic Distance/geographic_distance.py index bf6a0e2..bf5208d 100644 --- a/Geographic Distance/geographic_distance.py +++ b/Geographic Distance/geographic_distance.py @@ -17,6 +17,18 @@ def calculate_distance_and_time(coord1, coord2, avg_speed): return distance, travel_time + def validate_coordinates(coord): + """Ensure latitude and longitude are within valid ranges.""" + lat, lon = coord + if not (-90 <= lat <= 90): + raise ValueError(f"Latitude {lat} out of range (-90..90)") + if not (-180 <= lon <= 180): + raise ValueError(f"Longitude {lon} out of range (-180..180)") + return coord + + + + def main(): # Coordinates (latitude, longitude) From 392a9e6041d8fe4f260fc2c02df8e7bec98e99b5 Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Thu, 18 Sep 2025 21:06:12 -0500 Subject: [PATCH 06/13] Geographic Distance: add km_to_miles() and show distance in km and miles --- Geographic Distance/geographic_distance.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Geographic Distance/geographic_distance.py b/Geographic Distance/geographic_distance.py index bf5208d..1dff49c 100644 --- a/Geographic Distance/geographic_distance.py +++ b/Geographic Distance/geographic_distance.py @@ -26,6 +26,10 @@ def validate_coordinates(coord): raise ValueError(f"Longitude {lon} out of range (-180..180)") return coord + def km_to_miles(km: float) -> float: + """Convert kilometers to miles.""" + return km * 0.621371 + From 672129d25e352975e0783874d71461d0e6a92e11 Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Thu, 18 Sep 2025 21:07:48 -0500 Subject: [PATCH 07/13] Geographic Distance: format travel time as hours and minutes --- Geographic Distance/geographic_distance.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Geographic Distance/geographic_distance.py b/Geographic Distance/geographic_distance.py index 1dff49c..c91889e 100644 --- a/Geographic Distance/geographic_distance.py +++ b/Geographic Distance/geographic_distance.py @@ -30,6 +30,16 @@ def km_to_miles(km: float) -> float: """Convert kilometers to miles.""" return km * 0.621371 + def format_travel_time(hours: float) -> str: + """Format fractional hours as 'Hh Mm'.""" + h = int(hours) + m = int(round((hours - h) * 60)) + if m == 60: + h += 1 + m = 0 + return f"{h}h {m}m" + + From d4f80b85bc5c02c359e41609b1b48d782122166f Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Fri, 19 Sep 2025 17:57:01 -0500 Subject: [PATCH 08/13] Resume Builder: add interactive internships collector --- Resume Builder/resume_builder.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Resume Builder/resume_builder.py b/Resume Builder/resume_builder.py index 8d2211e..af27dfa 100644 --- a/Resume Builder/resume_builder.py +++ b/Resume Builder/resume_builder.py @@ -16,6 +16,8 @@ def clear_screen(): "projects": [], "certifications": [], "achievements": [] + "internships":[] + } # Function to navigate back to the main menu @@ -54,6 +56,27 @@ def add_experience(): break back_to_menu() + # Add internships <-- NEW +def add_internships(): + while True: + clear_screen() + print("Enter Internship Information") + internship = { + "role": prompt("Role/Title: "), + "company": prompt("Company: "), + "location": prompt("Location (optional): "), + "start_date": prompt("Start Date (e.g., Jun 2024): "), + "end_date": prompt("End Date (e.g., Aug 2024 or 'Present'): "), + "details": [s.strip() for s in prompt("Highlights (comma-separated): ").split(",") if s.strip()] + } + resume_data["internships"].append(internship) + + more = prompt("Add another internship? (yes/no): ").strip().lower() + if more == "no": + break + back_to_menu() + + # Add education details def add_education(): while True: From 56dff0e993f4d4d2be7688f6caec86b49bce4bac Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Fri, 19 Sep 2025 18:05:22 -0500 Subject: [PATCH 09/13] Resume Builder: include internships section in generated PDF --- Resume Builder/resume_builder.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Resume Builder/resume_builder.py b/Resume Builder/resume_builder.py index af27dfa..6110535 100644 --- a/Resume Builder/resume_builder.py +++ b/Resume Builder/resume_builder.py @@ -232,6 +232,20 @@ def generate_pdf(): os.system(f"start {pdf_output_path}" if os.name == "nt" else f"open {pdf_output_path}") print(f"Resume generated: {pdf_output_path}") + # Internships + if resume_data["internships"]: + pdf.set_font('Arial', 'B', 12) + pdf.cell(0, 10, "Internships", 0, 1) + pdf.set_font('Arial', '', 11) + for it in resume_data["internships"]: + hdr = f"{it['role']} at {it['company']}" + if it.get("location"): + hdr += f" — {it['location']}" + pdf.cell(0, 10, f"{hdr} ({it['start_date']} - {it['end_date']})", 0, 1) + if it.get("details"): + pdf.multi_cell(0, 10, "Highlights: " + ", ".join(it["details"])) + + # Main Menu using button_dialog from prompt_toolkit def interactive_menu(): while True: From 8dc866742bb80a9c9eda7497dd955c0778bcf0ee Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Fri, 19 Sep 2025 18:11:01 -0500 Subject: [PATCH 10/13] Resume Builder: add Internships menu option and handler --- Resume Builder/resume_builder.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Resume Builder/resume_builder.py b/Resume Builder/resume_builder.py index 6110535..72fc783 100644 --- a/Resume Builder/resume_builder.py +++ b/Resume Builder/resume_builder.py @@ -259,10 +259,11 @@ def interactive_menu(): ("Education", 3), ("Skills", 4), ("Projects", 5), - ("Certifications", 6), - ("Achievements", 7), - ("Generate PDF", 8), - ("Exit", 9) + ("Internships", 6), + ("Certifications", 7), + ("Achievements", 8), + ("Generate PDF", 9), + ("Exit", 10) ] ).run() @@ -276,13 +277,15 @@ def interactive_menu(): add_skills() elif choice == 5: add_projects() - elif choice == 6: - add_certifications() + elif choice == 6: + add_internships() elif choice == 7: - add_achievements() + add_certifications() elif choice == 8: - generate_pdf() + add_achievements() elif choice == 9: + generate_pdf() + elif choice == 10: break # Start the program From 2b0e9788a50b5248da719fa7e7119a484bc6cf0d Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Fri, 19 Sep 2025 18:17:27 -0500 Subject: [PATCH 11/13] Resume Builder: add internships list to resume_data --- Resume Builder/resume_builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resume Builder/resume_builder.py b/Resume Builder/resume_builder.py index 72fc783..379fe39 100644 --- a/Resume Builder/resume_builder.py +++ b/Resume Builder/resume_builder.py @@ -15,7 +15,7 @@ def clear_screen(): "skills": [], "projects": [], "certifications": [], - "achievements": [] + "achievements": [], "internships":[] } @@ -277,7 +277,7 @@ def interactive_menu(): add_skills() elif choice == 5: add_projects() - elif choice == 6: + elif choice == 6: add_internships() elif choice == 7: add_certifications() From d43108cbf32a6a758cb2dba0805f43d2aa66e1ff Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Fri, 19 Sep 2025 18:39:44 -0500 Subject: [PATCH 12/13] Resume Builder: add interactive internships collector --- Resume Builder/resume_builder.py | 43 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/Resume Builder/resume_builder.py b/Resume Builder/resume_builder.py index 379fe39..9e699c3 100644 --- a/Resume Builder/resume_builder.py +++ b/Resume Builder/resume_builder.py @@ -56,25 +56,7 @@ def add_experience(): break back_to_menu() - # Add internships <-- NEW -def add_internships(): - while True: - clear_screen() - print("Enter Internship Information") - internship = { - "role": prompt("Role/Title: "), - "company": prompt("Company: "), - "location": prompt("Location (optional): "), - "start_date": prompt("Start Date (e.g., Jun 2024): "), - "end_date": prompt("End Date (e.g., Aug 2024 or 'Present'): "), - "details": [s.strip() for s in prompt("Highlights (comma-separated): ").split(",") if s.strip()] - } - resume_data["internships"].append(internship) - - more = prompt("Add another internship? (yes/no): ").strip().lower() - if more == "no": - break - back_to_menu() + # Add education details @@ -150,6 +132,29 @@ def add_achievements(): if more == "no": break back_to_menu() + + # Add internships +def add_internships(): + while True: + clear_screen() + print("Enter Internship Information") + internship = { + "role": prompt("Role/Title: "), + "company": prompt("Company: "), + "location": prompt("Location (optional): "), + "start_date": prompt("Start Date (e.g., Jun 2024): "), + "end_date": prompt("End Date (e.g., Aug 2024 or 'Present'): "), + "details": [s.strip() for s in prompt("Highlights (comma-separated): ").split(",") if s.strip()] + } + resume_data["internships"].append(internship) + + more = prompt("Add another internship? (yes/no): ").strip().lower() + if more == "no": + break + back_to_menu() + + + # PDF Generation class class ResumePDF(FPDF): From fd67603e842254b538d145603d3fc169923071eb Mon Sep 17 00:00:00 2001 From: Eric G Butler Jr Date: Fri, 19 Sep 2025 18:47:48 -0500 Subject: [PATCH 13/13] Resume Builder: include interships section in generated PDF --- Resume Builder/resume_builder.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Resume Builder/resume_builder.py b/Resume Builder/resume_builder.py index 9e699c3..8ee0060 100644 --- a/Resume Builder/resume_builder.py +++ b/Resume Builder/resume_builder.py @@ -214,7 +214,21 @@ def generate_pdf(): pdf.cell(0, 10, proj["name"], 0, 1) pdf.multi_cell(0, 10, proj["description"]) pdf.cell(0, 10, f"Technologies Used: {proj['technologies']}", 0, 1) + # Internships + if resume_data["internships"]: + pdf.set_font('Arial', 'B', 12) + pdf.cell(0, 10, "Internships", 0, 1) + pdf.set_font('Arial', '', 11) + for it in resume_data["internships"]: + hdr = f"{it['role']} at {it['company']}" + if it.get("location"): + hdr += f" — {it['location']}" + pdf.cell(0, 10, f"{hdr} ({it['start_date']} - {it['end_date']})", 0, 1) + if it.get("details"): + pdf.multi_cell(0, 10, "Highlights: " + ", ".join(it["details"])) + + # Certifications pdf.set_font('Arial', 'B', 12) pdf.cell(0, 10, "Certifications", 0, 1) @@ -237,19 +251,7 @@ def generate_pdf(): os.system(f"start {pdf_output_path}" if os.name == "nt" else f"open {pdf_output_path}") print(f"Resume generated: {pdf_output_path}") - # Internships - if resume_data["internships"]: - pdf.set_font('Arial', 'B', 12) - pdf.cell(0, 10, "Internships", 0, 1) - pdf.set_font('Arial', '', 11) - for it in resume_data["internships"]: - hdr = f"{it['role']} at {it['company']}" - if it.get("location"): - hdr += f" — {it['location']}" - pdf.cell(0, 10, f"{hdr} ({it['start_date']} - {it['end_date']})", 0, 1) - if it.get("details"): - pdf.multi_cell(0, 10, "Highlights: " + ", ".join(it["details"])) - + # Main Menu using button_dialog from prompt_toolkit def interactive_menu():