from __future__ import annotations import os import gradio as gr from fastapi import Request from fastapi.responses import RedirectResponse from src.attending import COLS, LOCAL_DISPLAY_TZ from src.config import assets_dir, events_path, load_env from src.events_schema import normalize_results from src.hf_sync import hf_enabled, pull_events from src.models import load_payload from src.teams_calendar import ( SESSION_COOKIE, auth_url, begin_auth_flow, consume_auth_flow, ensure_user_on_event, exchange_code_for_user, session_id, ) from src.teams_session_store import get_user, set_user from src.ui.view import ( facet_choices, filter_button_label, render_filter_summary, render_hero, render_spotlight, reset_filters, route_url, toggle_filter_popover, view_updates, ) from src.ui.events_df import events_df load_env() ASSETS_DIR = assets_dir() EVENTS_PATH = events_path() CSS = (ASSETS_DIR / "app.css").read_text(encoding="utf-8") SHARE_JS = (ASSETS_DIR / "share.js").read_text(encoding="utf-8") SCROLL_AFTER_RENDER_JS = (ASSETS_DIR / "scroll_after_render.js").read_text(encoding="utf-8") def render(): if hf_enabled(): pull_events(EVENTS_PATH) payload = load_payload(EVENTS_PATH) results = normalize_results(payload["results"]) df_all = events_df(results) category_choices, tag_choices, org_choices = facet_choices(df_all) summary, spotlight, table, button_label, clear_button = view_updates( results, categories=category_choices, tags=tag_choices, orgs=org_choices, ) sources = "\n".join(f"- [{result['org_name']}]({result['source_url']})" for result in results) return ( render_hero(payload, results), summary, spotlight, table, results, sources, gr.update(choices=category_choices, value=category_choices), gr.update(choices=tag_choices, value=tag_choices), gr.update(choices=org_choices, value=org_choices), button_label, clear_button, ) with gr.Blocks( theme=gr.themes.Soft( primary_hue=gr.themes.colors.orange, secondary_hue=gr.themes.colors.stone, neutral_hue=gr.themes.colors.stone, radius_size="lg", ), css=CSS, js=SHARE_JS, fill_width=True, title="Robotic seminars", ) as demo: hero = gr.Markdown(elem_id="hero") spotlight = gr.Markdown(elem_id="spotlight") with gr.Column(elem_id="toolbar-shell"): with gr.Row(elem_classes="toolbar-row", equal_height=True): search = gr.Textbox( placeholder="Search titles, speakers, or keywords", show_label=False, container=False, elem_id="search", scale=1, ) filter_button = gr.Button( "Filters", variant="secondary", size="sm", elem_id="filter-toggle", min_width=124, scale=0, ) clear_button = gr.Button( "Reset", variant="secondary", size="sm", elem_id="clear-filters", min_width=104, scale=0, visible=False, ) filter_summary = gr.Markdown(elem_id="filter-summary") with gr.Column(elem_id="filter-popover", visible=False) as filter_popover: gr.Markdown("Refine results", elem_classes="popover-title") with gr.Row(elem_classes="filter-grid", equal_height=True): category_filter = gr.Dropdown( [], value=[], multiselect=True, filterable=True, label="Categories", elem_classes="filter-field", min_width=170, ) tag_filter = gr.Dropdown( [], value=[], multiselect=True, filterable=True, label="Tags", elem_classes="filter-field", min_width=210, scale=2, ) university_filter = gr.Dropdown( [], value=[], multiselect=True, filterable=True, label="Universities", elem_classes="filter-field", min_width=250, scale=2, ) filter_open = gr.State(False) table = gr.Dataframe( headers=COLS, datatype=["markdown", "markdown", "markdown", "markdown", "markdown", "markdown"], column_widths=[145, 320, 200, 135, 170, 145], max_height=100000, interactive=False, wrap=True, elem_id="events-table", ) results_state = gr.State([]) with gr.Accordion("Sources", open=False): sources_box = gr.Markdown() filter_inputs = [results_state, search, category_filter, tag_filter, university_filter] load_event = demo.load( render, outputs=[ hero, filter_summary, spotlight, table, results_state, sources_box, category_filter, tag_filter, university_filter, filter_button, clear_button, ], js=SHARE_JS, show_progress="hidden", ) load_event.then( fn=None, js=SCROLL_AFTER_RENDER_JS, queue=False, show_progress="hidden", ) search.input( view_updates, inputs=filter_inputs, outputs=[filter_summary, spotlight, table, filter_button, clear_button], queue=False, show_progress="hidden", trigger_mode="always_last", ) category_filter.change( view_updates, inputs=filter_inputs, outputs=[filter_summary, spotlight, table, filter_button, clear_button], queue=False, show_progress="hidden", ) tag_filter.change( view_updates, inputs=filter_inputs, outputs=[filter_summary, spotlight, table, filter_button, clear_button], queue=False, show_progress="hidden", ) university_filter.change( view_updates, inputs=filter_inputs, outputs=[filter_summary, spotlight, table, filter_button, clear_button], queue=False, show_progress="hidden", ) filter_button.click( toggle_filter_popover, inputs=[filter_open], outputs=[filter_open, filter_popover], queue=False, show_progress="hidden", ) clear_button.click( reset_filters, inputs=[results_state], outputs=[ search, category_filter, tag_filter, university_filter, filter_summary, spotlight, table, filter_button, clear_button, filter_open, filter_popover, ], queue=False, show_progress="hidden", ) @demo.app.get("/calendar/action", name="calendar_action") def calendar_action(request: Request, event_key: str): user = get_user(request.cookies.get(SESSION_COOKIE, "")) if user is None: return RedirectResponse(route_url(request, "microsoft_start", event_key=event_key), status_code=302) return RedirectResponse(ensure_user_on_event(event_key, user, events_path=EVENTS_PATH), status_code=302) @demo.app.get("/auth/microsoft/start", name="microsoft_start") def microsoft_start(request: Request, event_key: str): current_session_id = request.cookies.get(SESSION_COOKIE) or session_id() state = begin_auth_flow(current_session_id, event_key) response = RedirectResponse( auth_url(redirect_uri=str(request.url_for("microsoft_callback")), state=state), status_code=302, ) response.set_cookie(SESSION_COOKIE, current_session_id, httponly=True, samesite="lax") return response @demo.app.get("/auth/microsoft/callback", name="microsoft_callback") def microsoft_callback(request: Request, code: str, state: str): expected_session_id = request.cookies.get(SESSION_COOKIE, "") pending = consume_auth_flow(state, expected_session_id=expected_session_id) user = exchange_code_for_user(code, redirect_uri=str(request.url_for("microsoft_callback"))) set_user(session_id=pending["session_id"], user=user) response = RedirectResponse( ensure_user_on_event(pending["event_key"], user, events_path=EVENTS_PATH), status_code=302, ) response.set_cookie(SESSION_COOKIE, pending["session_id"], httponly=True, samesite="lax") return response if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", "7860")))