Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 102 additions & 46 deletions SMTplugins/Calendar/calendarWidget.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
# calendarWidget.py
# Displays a monthly calendar with Google Calendar event integration (optional)
# Falls back to a static calendar view if no credentials are provided

from widget import Widget
from datetime import datetime, date
import calendar
import json
import os
import time


class calendarWidget(Widget):

def __init__(self):
self._preferences = self.widgetDefaultPreferences
self._events = {} # date_str -> list of event names
# -------- FILES --------
self.state_file = "calendar_state.json"
self.events_file = "calendar_events.json"

# -------- DEFAULT STATE --------
self.current_date = date.today()

# -------- LOAD MONTH STATE --------
if os.path.exists(self.state_file):
try:
with open(self.state_file, "r") as f:
state = json.load(f)
if "current_date" in state:
self.current_date = date.fromisoformat(state["current_date"])
except:
print("State file corrupted, resetting")

# -------- LOAD EVENTS --------
if os.path.exists(self.events_file):
try:
with open(self.events_file, "r") as f:
self._events = json.load(f)
except:
print("Events file corrupted, resetting")
self._events = {}
else:
self._events = {}

# -------- METADATA --------
@property
def widgetName(self):
return "Calendar Widget"
Expand All @@ -23,17 +48,16 @@ def widgetID(self):

@property
def widgetHTML(self):
"""Returns the HTML template name; Flask renders it via Jinja."""
return "calendar_widget.html"

# -------- MAIN DATA --------
@property
def widgetData(self):
"""Returns current calendar data as a JSON-serialisable dict."""
today = date.today()
year = today.year
month = today.month
year = self.current_date.year
month = self.current_date.month

cal = calendar.Calendar(firstweekday=6) # week starts Sunday
cal = calendar.Calendar(firstweekday=6)
weeks = cal.monthdatescalendar(year, month)

weeks_data = []
Expand All @@ -50,12 +74,13 @@ def widgetData(self):
weeks_data.append(days)

return {
"month_name": today.strftime("%B"),
"month_name": self.current_date.strftime("%B"),
"year": year,
"weeks": weeks_data,
"day_headers": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
}

# -------- PREFERENCES --------
@property
def widgetPreferences(self):
return self._preferences
Expand All @@ -68,56 +93,87 @@ def widgetPreferences(self, value):
def widgetDefaultPreferences(self):
return {
"show_week_numbers": False,
"use_google_cal": False, # Set True + provide creds to enable
"use_google_cal": False,
"google_cal_id": "primary"
}

@property
def updateTimer(self):
# Refresh every 10 minutes
return 600_000

# -------- SAVE HELPERS --------
def _save_state(self):
with open(self.state_file, "w") as f:
json.dump({
"current_date": self.current_date.isoformat()
}, f)

def _save_events(self):
with open(self.events_file, "w") as f:
json.dump(self._events, f)

# -------- OPTIONAL DEFAULT DATA --------
def update(self):
"""Called by the widget subsystem on a timer. Fetches events if enabled."""
if self._preferences.get("use_google_cal"):
self._fetch_google_events()
else:
# Placeholder: inject a couple of demo events so the UI isn't empty
if not self._events:
today = date.today().isoformat()
self._events = {
today: ["Class 4PM – LC 22", "Gym 5PM"]
}
print(f"Calendar Widget updated – {date.today().strftime('%B %Y')}")

def _fetch_google_events(self):
"""
Stub for Google Calendar API integration.
To enable: install google-auth + google-api-python-client,
create OAuth credentials, and fill in the logic below.
"""
try:
# TODO: implement OAuth flow and fetch events for current month
# from googleapiclient.discovery import build
# service = build("calendar", "v3", credentials=creds)
# events_result = service.events().list(...).execute()
raise NotImplementedError("Google Calendar auth not yet configured.")
except Exception as e:
print(f"[calendarWidget] Google Calendar fetch failed: {e}")
self._events[today] = [
{"id": int(time.time()*1000), "title": "Class 4PM – LC 22"},
{"id": int(time.time()*1000)+1, "title": "Gym 5PM"}
]
self._save_events()

# -------- EVENT HANDLER --------
def handle_event(self, event, args):
"""
Supported events:
- "prev_month" : (future) navigate to previous month
- "next_month" : (future) navigate to next month
- "add_event" : args = {"date": "YYYY-MM-DD", "title": "..."}
"""

# -------- ADD TASK --------
if event == "add_event":
date_str = args.get("date")
title = args.get("title", "Event")

if date_str:
if date_str not in self._events:
self._events[date_str] = []
self._events[date_str].append(title)
print(f"[calendarWidget] Added event '{title}' on {date_str}")

self._events[date_str].append({
"id": int(time.time()*1000),
"title": title
})

self._save_events()

# -------- NEXT MONTH --------
elif event == "next_month":
print("NEXT MONTH TRIGGERED")

if self.current_date.month == 12:
self.current_date = self.current_date.replace(
year=self.current_date.year + 1,
month=1
)
else:
self.current_date = self.current_date.replace(
month=self.current_date.month + 1
)

self._save_state()
print("NEW MONTH:", self.current_date)

# -------- PREVIOUS MONTH --------
elif event == "prev_month":
print("PREV MONTH TRIGGERED")

if self.current_date.month == 1:
self.current_date = self.current_date.replace(
year=self.current_date.year - 1,
month=12
)
else:
self.current_date = self.current_date.replace(
month=self.current_date.month - 1
)

self._save_state()
print("NEW MONTH:", self.current_date)

else:
print(f"[calendarWidget] Unhandled event: {event} args={args}")
1 change: 1 addition & 0 deletions SMTplugins/Calendar/calendar_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

@calendar_bp.route("/widget/calendar")
def calendar_view():
print("RENDERING MONTH:", _widget.current_date) # debug
return render_template("calendar_widget.html", data=_widget.widgetData)

@calendar_bp.route("/api/calendar/data")
Expand Down
1 change: 1 addition & 0 deletions calendar_events.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"2026-04-29": ["Class 4PM \u2013 LC 22", "Gym 5PM"], "2026-04-30": []}
1 change: 1 addition & 0 deletions calendar_state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"current_date": "2026-04-29"}
2 changes: 1 addition & 1 deletion flaskServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

#allows for easier starting of flask from start file
def run_flask():
app.run(debug=True, port=5000, use_reloader=False)
app.run(debug=True, port=8000, use_reloader=False)



Expand Down
8 changes: 4 additions & 4 deletions layout_client.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
"col": 3
},
{
"id": "bj-container",
"name": "Blackjack",
"class": "blackjack-widget",
"css_name": "blackjack_widget.css",
"id": "calendar",
"name": "Calendar",
"class": "calendar-widget",
"css_name": "calendar_widget.css",
"row": 2,
"col": 2
},
Expand Down
Loading