import re with open("app/modules/manual/frontend/views.py", "r") as f: text = f.read() # Make sure cache is imported if "manual_cache" not in text: text = text.replace("from app.core.database import", "from app.modules.manual.backend.cache import manual_cache\nfrom app.core.database import") # GET /manual list_patch = """async def manual_index( request: Request, module: Optional[str] = Query(default=None), difficulty: Optional[str] = Query(default=None), tag: Optional[str] = Query(default=None), search: Optional[str] = Query(default=None), ): cache_key = f"views:list:{module}:{difficulty}:{tag}:{search}" cached = manual_cache.get(cache_key) if cached: # FastAPI TemplateResponse returns HTMLResponse which we can't easily cache natively # But we can cache the context dictionary to avoid DB queries rows, modules, unique_tags = cached else:""" text = re.sub( r'async def manual_index\([^)]*\):', list_patch, text ) # Need to indent the whole db fetch logic # Then we save it to cache. # Alternatively, I can just use a simple python caching decorator in `views.py`. Since we share the manual_cache instance, we can just clear it. text = text.replace(' return templates.TemplateResponse(', ' manual_cache.set(cache_key, (rows, modules, unique_tags))\n return templates.TemplateResponse(') # Indenting the execute_query part cleanly using re.sub is hard. Let's do it using Python string replacement lines = text.split('\n') in_else = False new_lines = [] for line in lines: if 'else:' in line and 'manual_index' not in line: # crude check if 'rows, modules, unique_tags = cached' in '\n'.join(new_lines[-5:]): in_else = True if in_else: if line.startswith(' return templates.TemplateResponse('): in_else = False else: if line.startswith(' '): line = ' ' + line new_lines.append(line) with open("app/modules/manual/frontend/views.py", "w") as f: f.write("\n".join(new_lines))