Add dates, gratitude, calc, timer - dates.py: Important dates and anniversaries - gratitude.py: Gratitude log - calc.py: Calculator and unit converter - timer.py: Countdown and stopwatch - 31 tools total!

This commit is contained in:
2026-01-30 23:57:25 -06:00
parent 3c9dc28852
commit cb4e350c10
8 changed files with 612 additions and 0 deletions

140
tools/calc.py Executable file
View File

@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""
calc - Quick calculator and unit converter
Math and conversions from the command line.
"""
import sys
import math
from datetime import datetime, timedelta
# Common conversions
CONVERSIONS = {
# Length
'km_to_mi': lambda x: x * 0.621371,
'mi_to_km': lambda x: x * 1.60934,
'm_to_ft': lambda x: x * 3.28084,
'ft_to_m': lambda x: x * 0.3048,
'in_to_cm': lambda x: x * 2.54,
'cm_to_in': lambda x: x / 2.54,
# Weight
'kg_to_lb': lambda x: x * 2.20462,
'lb_to_kg': lambda x: x / 2.20462,
'oz_to_g': lambda x: x * 28.3495,
'g_to_oz': lambda x: x / 28.3495,
# Temperature
'c_to_f': lambda x: x * 9/5 + 32,
'f_to_c': lambda x: (x - 32) * 5/9,
# Volume
'l_to_gal': lambda x: x * 0.264172,
'gal_to_l': lambda x: x * 3.78541,
# Data
'mb_to_gb': lambda x: x / 1024,
'gb_to_mb': lambda x: x * 1024,
'gb_to_tb': lambda x: x / 1024,
'tb_to_gb': lambda x: x * 1024,
}
def calculate(expr: str):
"""Evaluate a math expression."""
# Add math functions to namespace
namespace = {
'sqrt': math.sqrt,
'sin': math.sin,
'cos': math.cos,
'tan': math.tan,
'log': math.log,
'log10': math.log10,
'exp': math.exp,
'pi': math.pi,
'e': math.e,
'abs': abs,
'pow': pow,
'round': round,
}
try:
result = eval(expr, {"__builtins__": {}}, namespace)
print(f"= {result}")
except Exception as e:
print(f"Error: {e}")
def convert(value: float, conversion: str):
"""Convert between units."""
if conversion in CONVERSIONS:
result = CONVERSIONS[conversion](value)
units = conversion.split('_to_')
print(f"{value} {units[0]} = {result:.4f} {units[1]}")
else:
print(f"Unknown conversion: {conversion}")
print("Available:", ', '.join(CONVERSIONS.keys()))
def time_until(target: str):
"""Calculate time until a date."""
try:
if len(target) == 10: # YYYY-MM-DD
target_date = datetime.strptime(target, "%Y-%m-%d")
else:
target_date = datetime.strptime(target, "%Y-%m-%d %H:%M")
diff = target_date - datetime.now()
if diff.total_seconds() < 0:
print("That date has passed")
return
days = diff.days
hours, rem = divmod(diff.seconds, 3600)
minutes = rem // 60
print(f"Time until {target}:")
print(f" {days} days, {hours} hours, {minutes} minutes")
except:
print("Date format: YYYY-MM-DD or YYYY-MM-DD HH:MM")
def percentage(part: float, whole: float):
"""Calculate percentage."""
pct = (part / whole) * 100
print(f"{part} / {whole} = {pct:.2f}%")
def main():
if len(sys.argv) < 2:
print("Usage:")
print(" calc <expression> - Math calculation")
print(" calc <value> <conversion> - Unit conversion")
print(" calc until <YYYY-MM-DD> - Time until date")
print(" calc pct <part> <whole> - Percentage")
print("")
print("Examples:")
print(" calc '2 + 2'")
print(" calc 'sqrt(16) + pi'")
print(" calc 100 km_to_mi")
print(" calc 72 f_to_c")
print(" calc until 2026-12-31")
print("")
print("Conversions:", ', '.join(sorted(CONVERSIONS.keys())))
return
cmd = sys.argv[1]
if cmd == 'until' and len(sys.argv) > 2:
time_until(' '.join(sys.argv[2:]))
elif cmd == 'pct' and len(sys.argv) > 3:
percentage(float(sys.argv[2]), float(sys.argv[3]))
elif len(sys.argv) == 3 and sys.argv[2] in CONVERSIONS:
convert(float(sys.argv[1]), sys.argv[2])
else:
# Treat as expression
expr = ' '.join(sys.argv[1:])
calculate(expr)
if __name__ == "__main__":
main()