#!/usr/bin/env python3 # cdates - Simple terminal calendar with events # Copyright (c) 2025 640kb.neocities.org # # This project is licensed under the Blue Oak Model License 1.0.0. # Full text available at: https://blueoakcouncil.org/license/1.0.0 # Version 1.0 : initial release (07jun2025) # cdates : a simple calendar that uses an events file (caldates) # This is a simple personal use calendar that I just wanted to toss out there. # It lists a max of 12 events per monthly calendar display. # install: chmod +x cdates && sudo install cdates caldates /usr/local/bin # uninstall: sudo rm /usr/local/bin/{cdates,caldates} import calendar import os import sys from datetime import datetime def show_help(): help_text = """ cdates - a simple calendar with events Usage: cdates [option] Options: -h, --help, /? (this help) -edit (sudo editorName /usr/local/bin/caldates) Install: sudo install cdates caldates /usr/local/bin/ Uninstall: sudo rm /usr/local/bin/cdates /usr/local/bin/caldates Description: Shows the current month's calendar with events from a local 'caldates' file. Days with events or current day are marked with an '*'. Includes this month and the first week of next month. Max 12 events displayed. caldates file format: (per line) Example: Jun 15 Birthday Party Jul 1 Rent Due Dec 25 Christmas Day # this is a comment and ignored. """ print(help_text) def read_events(): """Read events from cdates file in the same directory as the script""" events = {} # Get the directory where the script is located script_dir = os.path.dirname(os.path.realpath(__file__)) file_path = os.path.join(script_dir, "caldates") try: with open(file_path, 'r') as f: for line in f: line = line.strip() if line and not line.startswith("#"): parts = line.split(maxsplit=2) if len(parts) >= 3: month_name, day, desc = parts month = datetime.strptime(month_name, "%b").month events[(month, int(day))] = desc except FileNotFoundError: pass return events def generate_calendar(): calendar.setfirstweekday(calendar.SUNDAY) now = datetime.now() month_name = now.strftime("%b") year = now.year today = now.day events = read_events() # Calendar grid setup cal = calendar.monthcalendar(year, now.month) border = "+-----+-----+-----+-----+-----+-----+-----+" header_border = "+" + ("-" * (len(border)-2)) + "+" # Prepare events text current_month = now.month next_month = current_month % 12 + 1 next_year = year + 1 if next_month == 1 else year event_lines = [] for (month, day), desc in sorted(events.items()): if month == current_month or (month == next_month and day <= 7): month_abbr = datetime(year if month == current_month else next_year, month, 1).strftime("%b") event_lines.append(f"{month_abbr} {day:2}: {desc}") # Fixed column position for events EVENT_COLUMN = 45 # Build calendar lines calendar_lines = [] calendar_lines.append(header_border) calendar_lines.append(f"|{f' {month_name} {year} '.center(len(header_border)-2)}|") calendar_lines.append(border) calendar_lines.append("| Sun | Mon | Tue | Wed | Thu | Fri | Sat |") calendar_lines.append(border) # Calendar weeks for week in cal: week_str = "|" for day in week: if day == 0: week_str += " |" else: day_str = f"{day:2}" if day == today: day_str += "*" elif (now.month, day) in events: day_str += "*" else: day_str += " " week_str += f" {day_str} |" calendar_lines.append(week_str) calendar_lines.append(border) # Combine calendar and events side by side max_lines = max(len(calendar_lines), len(event_lines)) output_lines = [] event_start_line = 3 # Line with Sun-Sat headers for i in range(max_lines): if i < len(calendar_lines): line = calendar_lines[i] else: line = " " * len(border) if i >= event_start_line and (i - event_start_line) < len(event_lines): line = line.ljust(EVENT_COLUMN) + event_lines[i - event_start_line] output_lines.append(line) print("\n".join(output_lines)) if __name__ == "__main__": # Check for help arguments if len(sys.argv) > 1: arg = sys.argv[1] if arg in ('-h', '--help', '/?', '-?', '--h'): show_help() elif arg == '-edit': editor = "xed" # Hardcoded editor os.execvp("sudo", ["sudo", editor, "/usr/local/bin/caldates"]) else: print(f"Unknown option: {arg}") show_help() else: generate_calendar()