|
| 1 | +import logging |
| 2 | +import os |
| 3 | +import sys |
| 4 | +from datetime import date, datetime, time |
| 5 | +from pathlib import Path |
| 6 | + |
| 7 | +import psycopg2 |
| 8 | +from dotenv import load_dotenv |
| 9 | + |
| 10 | +# Add parent directory to path for imports |
| 11 | +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 12 | + |
| 13 | +load_dotenv() |
| 14 | + |
| 15 | + |
| 16 | +def format_value(value): |
| 17 | + """Format values for TypeScript file""" |
| 18 | + if value is None: |
| 19 | + return "null" |
| 20 | + if isinstance(value, date | time | datetime): |
| 21 | + return f'"{value.isoformat()}"' |
| 22 | + if isinstance(value, str): |
| 23 | + escaped_value = ( |
| 24 | + value.replace("\\", "\\\\") |
| 25 | + .replace('"', '\\"') |
| 26 | + .replace("\n", "\\n") |
| 27 | + .replace("\r", "\\r") |
| 28 | + ) |
| 29 | + return f'"{escaped_value}"' |
| 30 | + if isinstance(value, bool): |
| 31 | + return str(value).lower() |
| 32 | + return value |
| 33 | + |
| 34 | + |
| 35 | +def fetch_events_for_static_data(): |
| 36 | + """Fetch all upcoming events from the database for static data generation""" |
| 37 | + conn_string = os.environ.get("SUPABASE_DB_URL") |
| 38 | + logging.info("Connecting to the database...") |
| 39 | + |
| 40 | + with psycopg2.connect(conn_string) as conn, conn.cursor() as cur: |
| 41 | + logging.info("Executing query...") |
| 42 | + query = """ |
| 43 | + SELECT |
| 44 | + e.id, |
| 45 | + e.club_handle, |
| 46 | + e.url, |
| 47 | + e.name, |
| 48 | + e.date, |
| 49 | + e.start_time, |
| 50 | + CASE |
| 51 | + WHEN e.end_time IS NULL THEN e.start_time + interval '1 hour' |
| 52 | + ELSE e.end_time |
| 53 | + END as end_time, |
| 54 | + e.location, |
| 55 | + e.price, |
| 56 | + e.food, |
| 57 | + e.registration, |
| 58 | + e.image_url, |
| 59 | + e.club_type, |
| 60 | + e.added_at, |
| 61 | + e.description |
| 62 | + FROM |
| 63 | + events e |
| 64 | + WHERE |
| 65 | + e.date >= CURRENT_DATE |
| 66 | + ORDER BY e.date ASC, e.start_time ASC; |
| 67 | + """ |
| 68 | + cur.execute(query) |
| 69 | + columns = [desc[0] for desc in cur.description] |
| 70 | + events = [dict(zip(columns, row, strict=False)) for row in cur.fetchall()] |
| 71 | + logging.info(f"Fetched {len(events)} events.") |
| 72 | + return events |
| 73 | + |
| 74 | + |
| 75 | +def generate_recommended_filters(events_data): |
| 76 | + """Generate recommended filters using OpenAI service""" |
| 77 | + try: |
| 78 | + from services.openai_service import generate_recommended_filters |
| 79 | + logging.info("Generating recommended filters using OpenAI...") |
| 80 | + recommended_filters = generate_recommended_filters(events_data) |
| 81 | + |
| 82 | + if not recommended_filters: |
| 83 | + logging.warning("Failed to generate recommended filters") |
| 84 | + return [] |
| 85 | + |
| 86 | + logging.info(f"Generated {len(recommended_filters)} filters: {recommended_filters}") |
| 87 | + return recommended_filters |
| 88 | + except Exception as e: |
| 89 | + logging.error(f"Error generating recommended filters: {e}") |
| 90 | + return [] |
| 91 | + |
| 92 | + |
| 93 | +def main(): |
| 94 | + """Fetches events, generates filters, and writes to staticData.ts""" |
| 95 | + logging.basicConfig( |
| 96 | + level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" |
| 97 | + ) |
| 98 | + try: |
| 99 | + # Fetch upcoming events |
| 100 | + events = fetch_events_for_static_data() |
| 101 | + |
| 102 | + # Generate recommended filters |
| 103 | + recommended_filters = generate_recommended_filters(events) |
| 104 | + |
| 105 | + # Write to staticData.ts |
| 106 | + output_path = ( |
| 107 | + Path(__file__).parent.parent.parent |
| 108 | + / "frontend" |
| 109 | + / "src" |
| 110 | + / "data" |
| 111 | + / "staticData.ts" |
| 112 | + ) |
| 113 | + logging.info(f"Writing to {output_path}...") |
| 114 | + with output_path.open("w", encoding="utf-8") as f: |
| 115 | + # Write the last updated timestamp |
| 116 | + current_time = datetime.now().isoformat() |
| 117 | + f.write('import { Event } from "@/hooks/useEvents";\n\n') |
| 118 | + f.write(f'export const LAST_UPDATED = "{current_time}";\n\n') |
| 119 | + |
| 120 | + # Write static events data |
| 121 | + f.write("export const staticEventsData = new Map<string, Event>([\n") |
| 122 | + for i, event in enumerate(events): |
| 123 | + event_id = str(event["id"]) |
| 124 | + f.write(f" [{format_value(event_id)}, {{\n") |
| 125 | + f.write(f" id: {format_value(event_id)},\n") |
| 126 | + f.write(f' club_handle: {format_value(event["club_handle"])},\n') |
| 127 | + f.write(f' url: {format_value(event["url"])},\n') |
| 128 | + f.write(f' name: {format_value(event["name"])},\n') |
| 129 | + f.write(f' date: {format_value(event["date"])},\n') |
| 130 | + f.write(f' start_time: {format_value(event["start_time"])},\n') |
| 131 | + f.write(f' end_time: {format_value(event["end_time"])},\n') |
| 132 | + f.write(f' location: {format_value(event["location"])},\n') |
| 133 | + f.write(f' price: {format_value(event["price"])},\n') |
| 134 | + f.write(f' food: {format_value(event["food"])},\n') |
| 135 | + f.write(f' registration: {format_value(event["registration"])},\n') |
| 136 | + f.write(f' image_url: {format_value(event["image_url"])},\n') |
| 137 | + f.write(f' club_type: {format_value(event["club_type"])},\n') |
| 138 | + f.write(f' added_at: {format_value(event["added_at"])},\n') |
| 139 | + f.write(" }]") |
| 140 | + if i < len(events) - 1: |
| 141 | + f.write(",") |
| 142 | + f.write("\n") |
| 143 | + f.write("]);\n\n") |
| 144 | + |
| 145 | + # Write recommended filters |
| 146 | + if recommended_filters: |
| 147 | + f.write("export const RECOMMENDED_FILTERS: string[] = [\n") |
| 148 | + for i, filter_keyword in enumerate(recommended_filters): |
| 149 | + escaped = filter_keyword.replace('"', '\\"') |
| 150 | + f.write(f' "{escaped}"') |
| 151 | + if i < len(recommended_filters) - 1: |
| 152 | + f.write(",") |
| 153 | + f.write("\n") |
| 154 | + f.write("];\n") |
| 155 | + else: |
| 156 | + f.write("export const RECOMMENDED_FILTERS: string[] = [];\n") |
| 157 | + |
| 158 | + logging.info("Successfully updated staticData.ts with events and recommended filters") |
| 159 | + except Exception: |
| 160 | + logging.exception("An error occurred") |
| 161 | + sys.exit(1) |
| 162 | + |
| 163 | + |
| 164 | +if __name__ == "__main__": |
| 165 | + main() |
0 commit comments