Skip to content

Instantly share code, notes, and snippets.

@AlphaGameDeveloper
Created April 28, 2025 16:41
Show Gist options
  • Save AlphaGameDeveloper/ae5fba1704e05b69a6966eb8789e7808 to your computer and use it in GitHub Desktop.
Save AlphaGameDeveloper/ae5fba1704e05b69a6966eb8789e7808 to your computer and use it in GitHub Desktop.
San Marin High School bell schedule CLI
#!/usr/bin/env python3
import datetime
import sys
import argparse
import json
def time_until_next_bell(schedule, current_time):
verbose(f"Calculating time until next bell from {current_time}")
for i, period in enumerate(schedule):
if len(period) == 2: # Only the name and start time
bell_time = datetime.datetime.strptime(period[1], "%H:%M").time()
else: # Name, start time, and end time
bell_time = datetime.datetime.strptime(period[1], "%H:%M").time()
verbose(f"Checking bell time {bell_time} for period {period[0]}")
if current_time < bell_time:
time_diff = (datetime.datetime.combine(date, bell_time) - datetime.datetime.combine(date, current_time)).total_seconds() / 60
verbose(f"Next bell is for {period[0]} at {bell_time}, in {time_diff:.0f} minutes")
return time_diff, period, i
verbose("No more bells found for today")
return None, None, None
def load_fancy_names(writeIfNotFound=False):
config_file = "/opt/smhs-bell.json"
verbose("Checking for configuration file '" + config_file + "'")
default = {
"Period 1": "Period 1",
"Period 2": "Period 2",
"Period 3": "Period 3",
"Period 4": "Period 4",
"Period 5": "Period 5",
"Period 6": "Period 6",
"Period 7": "Period 7",
"Warning Bell": "Warning Bell",
"Break": "Break",
"Lunch": "Lunch",
"Access": "Access",
"Rally": "Rally"
}
try:
with open(config_file, "r") as f:
fancy_names = json.load(f)
verbose("Fancy names loaded successfully")
except FileNotFoundError:
verbose("Configuration file not found, using default names")
if writeIfNotFound:
verbose("Creating default configuration file")
with open(config_file, "w") as f:
json.dump(default, f, indent=4)
fancy_names = default
else:
verbose("Default names will be used")
fancy_names = default
fancy_names = {}
except json.JSONDecodeError:
verbose("Error decoding JSON from configuration file, using default names")
fancy_names = {}
except Exception as e:
verbose(f"Unexpected error: {e}")
fancy_names = {}
return fancy_names
def verbose(message):
"""Prints a message if verbose mode is enabled."""
if args.verbose:
print("Verbose: ", message)
if __name__ == "__main__":
# Set up argument parser
parser = argparse.ArgumentParser(description="Bell schedule for San Marin High School.")
parser.add_argument("-d", "--date", type=str, help="Date in YYYY-MM-DD format")
parser.add_argument("-t", "--time", type=str, help="Time in HH:MM format")
parser.add_argument("-r", "--rally", action="store_true", help="Rally day schedule")
parser.add_argument("-m", "--minimum", action="store_true", help="Minimum day schedule")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
parser.add_argument("-c", "--config", action="store_true", help="Create default configuration file if not found")
args = parser.parse_args()
verbose("Arguments parsed")
# Get the current date and time if not provided
if args.date:
date = datetime.datetime.strptime(args.date, "%Y-%m-%d").date()
verbose(f"Using provided date: {date}")
else:
date = datetime.date.today()
verbose(f"Using current date: {date}")
if args.time:
time = datetime.datetime.strptime(args.time, "%H:%M").time()
verbose(f"Using provided time: {time}")
else:
time = datetime.datetime.now().time()
verbose(f"Using current time: {time}")
# Get the day of the week (0=Monday, 6=Sunday)
day_of_week = date.weekday()
verbose(f"Day of week: {day_of_week} ({date.strftime('%A')})")
# store the bell schedule in a dictionary
verbose("Loading bell schedule")
bell_schedule = {
"Monday": [
("Warning Bell", "08:25"),
("Period 1", "08:30", "09:20"),
("Period 2", "09:25", "10:15"),
("Break", "10:15", "10:25"),
("Period 3", "10:30", "11:20"),
("Period 4", "11:25", "12:20"),
("Lunch", "12:20", "12:55"),
("Period 5", "13:00", "13:50"),
("Period 6", "13:55", "14:45"),
("Period 7", "14:50", "15:40")
],
"Tuesday": [
("Warning Bell", "08:25"),
("Period 1", "08:30", "10:00"),
("Break", "10:00", "10:10"),
("Period 3", "10:15", "11:45"),
("Lunch", "11:45", "12:20"),
("Period 5", "12:25", "13:55"),
("Break", "13:55", "14:05"),
("Period 7", "14:10", "15:40")
],
"Wednesday": [
("Warning Bell", "08:25"),
("Period 2", "08:30", "10:00"),
("Access", "10:05", "10:55"),
("Break", "10:55", "11:05"),
("Period 4", "11:10", "12:40"),
("Lunch", "12:40", "13:15"),
("Period 6", "13:20", "14:50"),
("Staff Meetings", "15:00", "15:45")
],
"Thursday": [
("Warning Bell", "08:25"),
("Period 1", "08:30", "09:20"),
("Period 2", "09:25", "10:15"),
("Break", "10:15", "10:25"),
("Period 3", "10:30", "11:20"),
("Period 4", "11:25", "12:20"),
("Lunch", "12:20", "12:55"),
("Period 5", "13:00", "13:50"),
("Period 6", "13:55", "14:45"),
("Period 7", "14:50", "15:40")
],
"Friday": [
("Warning Bell", "08:25"),
("Period 1", "08:30", "09:20"),
("Period 2", "09:25", "10:15"),
("Break", "10:15", "10:25"),
("Period 3", "10:30", "11:20"),
("Period 4", "11:25", "12:20"),
("Lunch", "12:20", "12:55"),
("Period 5", "13:00", "13:50"),
("Period 6", "13:55", "14:45"),
("Period 7", "14:50", "15:40")
],
"Saturday": [],
"Sunday": [],
# Rally
"Rally": [
("Warning Bell", "08:25"),
("Period 1", "08:30", "09:10"),
("Period 2", "09:15", "09:55"),
("Break", "09:55", "10:05"),
("Period 3", "10:10", "10:50"),
("Period 4", "10:55", "11:35"),
("Rally", "11:40", "12:25"),
("Lunch", "12:25", "13:00"),
("Period 5", "13:05", "13:45"),
("Period 6", "13:50", "14:30"),
("Period 7", "14:35", "15:15")
],
# Minimum
"Minimum": [
("Warning Bell", "08:25"),
("Period 1", "08:30", "09:05"),
("Period 2", "09:10", "09:45"),
("Period 3", "09:50", "10:25"),
("Period 4", "10:30", "11:05"),
("Break", "11:05", "11:15"),
("Period 5", "11:20", "11:55"),
("Period 6", "12:00", "12:35"),
("Period 7", "12:40", "13:15")
]
}
verbose("Bell schedule loaded")
# get the schedule for the day
verbose("Determining schedule type for the day")
if args.rally:
schedule = bell_schedule["Rally"]
verbose("Using rally day schedule")
elif args.minimum:
schedule = bell_schedule["Minimum"]
verbose("Using minimum day schedule")
else:
schedule = bell_schedule.get(date.strftime("%A"), None)
verbose(f"Using {date.strftime('%A')} schedule")
if not schedule:
verbose("No schedule found for this day")
print("No schedule available for this day.")
sys.exit(1)
verbose(f"Schedule has {len(schedule)} periods/events")
# Check if the current time is within the schedule
current_time = datetime.datetime.combine(date, time).time()
verbose(f"Checking current time: {current_time}")
next_bell, next_period, period_index = time_until_next_bell(schedule, current_time)
fancy = load_fancy_names(args.config)
fancy_next_period = fancy.get(next_period[0], next_period[0])
if next_bell is not None:
verbose(f"Next bell in {next_bell:.0f} minutes")
print(f"Next bell in {next_bell:.0f} minutes. You then will have {fancy_next_period} at {next_period[1]}.")
else:
verbose("No more bells for today")
print("No more bells for today.")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment