def main()

in project/nanoeval/nanoeval/monitor.py [0:0]


def main() -> None:
    import matplotlib.pyplot as plt
    import streamlit as st

    st.set_page_config(layout="wide")

    st.title("Monitor")

    folder = database_dir()
    db_files = sorted(
        [f for f in os.listdir(folder) if f.endswith(".db")],
        reverse=True,
    )
    db_options = [f"{f}" for f in db_files]  # List all .db files in the folder with creation date

    selected_option = st.selectbox("Select Database (by pid of main process)", db_options)

    if not selected_option:
        return
    selected_db = folder / db_files[db_options.index(selected_option)]
    st.write(f"Open with: `sqlite3 {selected_db}`")
    if not selected_db:
        return

    st.subheader("Evals")
    with _monitor_conn(folder / selected_db) as conn:
        eval_data = _fetch_table_data("eval", conn)
        # task_data = _fetch_table_data("task", conn)
        executor_data = _fetch_table_data("executor", conn)

    # Calculate percent_complete for each eval
    st.dataframe(eval_data)

    st.subheader("Running executors")
    executor_data["aiomonitor_cmd"] = executor_data["aiomonitor_host"].apply(
        lambda x: f"python3 -m aiomonitor.cli -H {x.split(':')[0]} -p {x.split(':')[1]}"
    )
    executor_data["py_spy"] = executor_data["pid"].apply(lambda x: f"py-spy dump --pid {x}")

    st.dataframe(executor_data)

    st.subheader("Tasks")

    eval_name_filter = st.selectbox(
        "Filter by run id", options=["All"] + list(eval_data["run_id"].unique())
    )
    eval_constraint = " AND eval_id = '{eval_name_filter}'" if eval_name_filter != "All" else ""

    # Add a filter for status
    status_filter = (
        st.selectbox(
            "Filter by status", options=["All", "initialized", "running", "errored", "completed"]
        )
        or "All"
    )

    # Add filter for the state column in the dataframe
    with _monitor_conn(folder / selected_db) as conn:
        task_data = _load_task_data(conn, eval_data, eval_name_filter, status_filter)

    st.dataframe(task_data.head(100))
    st.write("(first 100 rows)")

    with _monitor_conn(folder / selected_db) as conn:
        num_init = conn.execute(
            f"SELECT COUNT(*) FROM task WHERE executor_pid IS NULL AND result IS NULL {eval_constraint}"
        ).fetchone()[0]
        num_running = conn.execute(
            f"SELECT COUNT(*) FROM task WHERE executor_pid IS NOT NULL AND result IS NULL {eval_constraint}"
        ).fetchone()[0]
        num_completed = conn.execute(
            f"SELECT COUNT(*) FROM task WHERE result IS NOT NULL {eval_constraint}"
        ).fetchone()[0]
        num_total = conn.execute(
            f"SELECT COUNT(*) FROM task WHERE 1=1 {eval_constraint}"
        ).fetchone()[0]
        assert num_init + num_running + num_completed == num_total

        # Compute histogram of task completion rate per minute
        cursor = conn.execute(
            """
            select strftime("%s", end_time) as end_time, eval_id, eval.name as eval_name from task join eval on task.eval_id = eval.run_id where end_time is not null
            """
            + (f" AND eval_id = '{eval_name_filter}'" if eval_name_filter != "All" else "")
        )
        completion_rate_data = cursor.fetchall()

    st.subheader("Graphs")
    st.write(
        f"num_init: {num_init}, num_running: {num_running}, num_completed: {num_completed}, num_total: {num_total}"
    )

    completion_rate_df = pd.DataFrame(
        completion_rate_data, columns=["end_time", "eval_id", "eval_name"]
    )
    col1, col2 = st.columns(2)
    with col1:
        # Convert end_time to datetime and localize to Pacific Time
        completion_rate_df["end_time"] = pd.to_datetime(
            completion_rate_df["end_time"], unit="s", origin="unix"
        )

        # Plot the histogram of task completion rate per minute
        fig, ax = plt.subplots(figsize=(8, 4))
        # Convert end_time to Pacific Time
        completion_rate_df["end_time_pacific"] = (
            completion_rate_df["end_time"].dt.tz_localize("UTC").dt.tz_convert("US/Pacific")
        )

        # Plot histogram colored by eval_id
        for _eval_id, group in completion_rate_df.groupby("eval_id"):
            ax.hist(
                group["end_time_pacific"],
                bins=pd.date_range(  # type: ignore
                    start=completion_rate_df["end_time_pacific"].min(),
                    end=completion_rate_df["end_time_pacific"].max(),
                    freq="T",
                ),
                histtype="step",
                alpha=0.5,
                label=f"{group['eval_name'].iloc[0]}",
            )

        ax.set_xlabel("Time (Pacific Time)")
        ax.set_ylabel("Tasks per minute")
        ax.set_title("Task completion rate")
        ax.grid(True)
        plt.xticks(rotation=45)
        ax.legend(title="Eval name")
        st.pyplot(fig, use_container_width=True)

    with col2:
        status_counts = [
            ("initialized", num_init),
            ("running", num_running),
            ("completed", num_completed),
        ]
        status_labels = [row[0] for row in status_counts]
        status_sizes = [row[1] for row in status_counts]
        fig, ax = plt.subplots(figsize=(2, 2))  # Set the figure size to 300px by 300px
        ax.pie(
            status_sizes,
            labels=status_labels,
            # TODO(kevinliu) why does this round weirdly?
            autopct=lambda p: f"{p:.1f}%\n({int(p * sum(status_sizes) / 100)})",
        )
        ax.axis("equal")  # Equal aspect ratio ensures that pie is drawn as a circle.
        st.pyplot(fig, use_container_width=False)