from fastapi import FastAPI, HTTPException, APIRouter, Request from fastapi.responses import HTMLResponse, RedirectResponse import uvicorn from dataApi import router as dataAPI_router from TrendAnalysis import router as trend_analysis_process from datacite import router as citation_analysis_process from fastapi.middleware.cors import CORSMiddleware from dbconnect import db_Connect from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles from venuAnalysis import router as venue_analysis_process from venuedata import router as venuedata_router import os from starlette.middleware.wsgi import WSGIMiddleware import dash_bootstrap_components as dbc import dash from dash import html import webbrowser import threading # Initialize FastAPI app app = FastAPI() # Global dash app instances dash_app = None venue_dash_app = None # Define a function to get or create the trend analysis dash app def get_or_create_dash_app(): global dash_app if dash_app is None: dash_app = dash.Dash(__name__, external_stylesheets=[dbc.themes.DARKLY], requests_pathname_prefix='/dash/') dash_app.layout = html.Div("Dashboard is loading...") return dash_app # Define a function to get or create the venue dash app def get_or_create_venue_dash_app(): global venue_dash_app if venue_dash_app is None: venue_dash_app = dash.Dash(__name__, external_stylesheets=[dbc.themes.DARKLY], requests_pathname_prefix='/venues/') venue_dash_app.layout = html.Div("Venue Dashboard is loading. Please start an analysis first.") return venue_dash_app # Initialize and mount dash apps dash_app = get_or_create_dash_app() app.mount("/dash", WSGIMiddleware(dash_app.server)) venue_dash_app = get_or_create_venue_dash_app() app.mount("/venues", WSGIMiddleware(venue_dash_app.server)) # Function to safely open browser def open_browser_safely(url, delay=1.5): def _open(): try: webbrowser.open(url, new=2) except Exception as e: print(f"Error opening browser: {e}") # Start a thread to open the browser after a delay threading.Timer(delay, _open).start() @app.get("/update-dashboard/{userId}/{topic}/{year}") @app.get("/update-dashboard/{userId}/{topic}") async def update_dashboard(request: Request, userId: str, topic: str, year: str = None): # Import necessary components from TrendAnalysis import fetch_papers_with_pagination, perform_trend_analysis, build_dashboard # Convert to a TrendAnalysisRequest-like structure class TempRequest: def __init__(self, userId, topic, year=None, page=0): self.userId = userId self.topic = topic self.year = year self.page = page data_request = TempRequest(userId, topic, year) # Fetch and process data df, current_page, total_pages, papers_count, total_papers = await fetch_papers_with_pagination( request, data_request.userId, data_request.topic, data_request.year, data_request.page ) if df.empty: return {"error": "No data found"} # Perform the trend analysis df, topic_labels = perform_trend_analysis(df) if df.empty: return {"error": "Failed to process embeddings"} # Update the global dash app global dash_app dash_app = build_dashboard(df, data_request.topic, data_request.year if data_request.year else "", existing_app=dash_app) # Get the base URL from the request base_url = str(request.base_url) dashboard_url = f"{base_url}dash" # Open the dashboard in a new browser tab open_browser_safely(dashboard_url) # Return a redirect to the dash app return RedirectResponse(url="/dash") # Fix for venue_redirect endpoint @app.get("/venue_redirect/{userId}/{topic}/{year}") @app.get("/venue_redirect/{userId}/{topic}") async def venue_redirect(request: Request, userId: str, topic: str, year: int = None): """ A centralized endpoint that redirects to the venue dashboard and opens it in a browser """ try: # Reset the global venue_dash_app to ensure a fresh instance global venue_dash_app venue_dash_app = None # Re-initialize and mount the Dash app venue_dash_app = get_or_create_venue_dash_app() app.mount("/venues", WSGIMiddleware(venue_dash_app.server)) base_url = str(request.base_url).rstrip('/') dashboard_url = f"{base_url}/venues/" open_browser_safely(dashboard_url) print(f"Redirecting to {dashboard_url}") return RedirectResponse(url="/venues/") except Exception as e: print(f"Error in venue_redirect: {e}") raise HTTPException(status_code=500, detail=f"Failed to redirect to venue dashboard: {str(e)}") app.mount("/static", StaticFiles(directory="static"), name="static") app.mount("/assets", StaticFiles(directory="assets"), name="assets") templates = Jinja2Templates(directory="templates") templates.env.auto_reload = True app.add_middleware( CORSMiddleware, allow_origins=["*"], # For development - restrict this in production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include routers app.include_router(dataAPI_router) app.include_router(trend_analysis_process) app.include_router(citation_analysis_process) app.include_router(venue_analysis_process) app.include_router(venuedata_router) collection, collection1, collection2 = db_Connect() app.state.collection = collection app.state.collection1 = collection1 app.state.collection2 = collection2 # Root endpoint @app.get("/") async def root(): return {"message": "Welcome to the Science Mapping Tool!"} @app.get("/home", response_class=HTMLResponse) async def home(request: Request): return templates.TemplateResponse("homepage.html", {"request": request}) @app.get("/login", response_class=HTMLResponse) async def login(request: Request): return templates.TemplateResponse("loginpage.html", {"request": request}) @app.get("/contact", response_class=HTMLResponse) async def login(request: Request): return templates.TemplateResponse("contactBoard.html", {"request": request}) @app.get("/feedback", response_class=HTMLResponse) async def login(request: Request): return templates.TemplateResponse("feedback.html", {"request": request}) # uvicorn app:app --host 0.0.0.0 --port 7000 --reload