EmptyClassroom uses a modern full-stack architecture with a Next.js frontend and FastAPI backend, deployed separately on Railway with Redis for caching.
The application is optimized for low latency and high availability with a 24-hour cache strategy to minimize API calls to BU’s scheduling system.
@app.on_event('startup')async def startup_event(): # Wait for Redis to be ready for i in range(5): try: rd.ping() print('Redis connection established') break except Exception: print(f'Waiting for Redis to be ready... (attempt {i+1}/5)') await asyncio.sleep(1) try: print('App starting up - checking if refresh is needed') # Check if refresh needed if should_refresh_on_wake(): print('App was sleeping or no recent data - fetching fresh data') await update_cache() # Set refresh timestamp now = datetime.now(pytz.timezone('America/New_York')) rd.set('classrooms:last_refresh', now.isoformat(), ex=CACHE_EXPIRY) print('Wake-up refresh completed successfully') else: print('Recent data available, skipping wake-up refresh') except Exception as e: print(f'Failed to handle wake-up refresh: {str(e)}')
The startup event implements wake-up refresh logic - if the app restarts and data wasn’t fetched today, it automatically refreshes the cache. See Caching Strategy for details.
@app.get('/api/open-classrooms')async def get_classroom_availability_by_building(): try: # Check cache first cache = rd.get('classrooms:availability') if cache: print('Cache hit') availability_data = json.loads(cache) else: print('Cache miss - fetching new data') availability_data = await get_classroom_availability() rd.set('classrooms:availability', json.dumps(availability_data), ex=CACHE_EXPIRY) # Update last refresh timestamp when fetching new data now = datetime.now(pytz.timezone('America/New_York')) rd.set('classrooms:last_refresh', now.isoformat(), ex=CACHE_EXPIRY) # Organize response by building res = {} for building_code, building_data in BUILDINGS.items(): res[building_code] = { "code": building_data["code"], "name": building_data["name"], "classrooms": [] } # Add classroom data to buildings for classroom_id, classroom_data in CLASSROOMS.items(): building_code = classroom_data["building_code"] if building_code in res: res[building_code]["classrooms"].append({ "id": classroom_data["id"], "name": classroom_data["name"], "availability": availability_data.get(classroom_id, []) }) return { "buildings": res, "last_updated": last_updated }