From aaa3d71665b2ea8fee3a8a5a6eb5e8242ee0ec7c Mon Sep 17 00:00:00 2001
From: Edward Betts <edward@4angle.com>
Date: Sun, 29 Oct 2023 15:17:24 +0000
Subject: [PATCH] Add Critical Mass: last Friday of every month

Closes: #35
---
 agenda/__init__.py   | 46 ++++++++++++++++++++++++++++++++++++++++++--
 templates/index.html |  1 +
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/agenda/__init__.py b/agenda/__init__.py
index 25ab8bc..d004bc3 100644
--- a/agenda/__init__.py
+++ b/agenda/__init__.py
@@ -307,6 +307,48 @@ def get_us_holidays(input_date: date) -> list[Event]:
     ]
 
 
+def critical_mass(start_date: date) -> list[Event]:
+    """
+    Return a list of the next 12 dates of the last Friday of the month on or after a given date.
+
+    :param start_date: The date from which to start looking for last Fridays.
+    :type start_date: date
+    :return: List of next 12 last Fridays on or after the start_date.
+    :rtype: List[date]
+    """
+    events: list[Event] = []
+
+    # Set current month and year based on the start_date
+    current_month = start_date.month
+    current_year = start_date.year
+
+    for _ in range(12):
+        # Calculate the last day of the current month
+        if current_month == 12:
+            last_day_of_month = date(current_year + 1, 1, 1) - timedelta(days=1)
+        else:
+            last_day_of_month = date(current_year, current_month + 1, 1) - timedelta(
+                days=1
+            )
+
+        # Find the last Friday of the current month
+        last_friday = last_day_of_month
+        while last_friday.weekday() != 4:  # Monday is 0 and Sunday is 6
+            last_friday -= timedelta(days=1)
+
+        # Include it in the list only if it's on or after the start_date
+        if last_friday >= start_date:
+            events.append(Event(name="critical_mass", date=last_friday))
+
+        # Move to the next month
+        current_month += 1
+        if current_month == 13:
+            current_month = 1
+            current_year += 1
+
+    return events
+
+
 def as_date(d: date | datetime) -> date:
     return d.date() if isinstance(d, datetime) else d
 
@@ -478,6 +520,7 @@ def get_data(now: datetime) -> typing.Mapping[str, str | object]:
         "uk_financial_year_end": uk_financial_year_end(today),
         "xmas_last_posting_dates": xmas_last_posting_dates,
         "gwr_advance_tickets": find_gwr_advance_ticket_date(),
+        "critical_mass": critical_mass(today),
         "rockets": thespacedevs.get_launches(rocket_dir, limit=40),
     }
 
@@ -486,8 +529,7 @@ def get_data(now: datetime) -> typing.Mapping[str, str | object]:
     for key, value in reply.items():
         if key in skip:
             continue
-        if "holiday" in key:
-            assert isinstance(value, list)
+        if isinstance(value, list):
             events += value
         else:
             assert isinstance(value, date)
diff --git a/templates/index.html b/templates/index.html
index b942d06..62147c5 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -25,6 +25,7 @@
   "next_up_series": "Next Up documentary",
   "waste_schedule": "Waste schedule",
   "gwr_advance_tickets": "GWR advance tickets",
+  "critical_mass": "Critical Mass",
 }
 %}