CoolFace
Apppublic

marcellobeer/usage-abc

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
app.py421 linesDownload Raw Back to root
1import streamlit as st2import pandas as pd3import altair as alt4from typing import Tuple5 6# ----------------------------------------------------------------------------7# Session-state keys for each dataset8# ----------------------------------------------------------------------------9if "df_tokens" not in st.session_state:10    st.session_state["df_tokens"] = None11 12if "df_images" not in st.session_state:13    st.session_state["df_images"] = None14 15# NEW: store the Meetings dataset16if "df_meetings" not in st.session_state:17    st.session_state["df_meetings"] = None18 19# ----------------------------------------------------------------------------20# Classification function (6 thresholds)21# ----------------------------------------------------------------------------22def classify_new(cum_dist: float) -> str:23    if cum_dist <= 0.20:24        return "<= 20%"25    elif cum_dist <= 0.40:26        return "<= 40%"27    elif cum_dist <= 0.60:28        return "<= 60%"29    elif cum_dist <= 0.80:30        return "A (<= 80%)"31    elif cum_dist <= 0.95:32        return "B (<= 95%)"33    else:34        return "C (<= 100%)"35 36# ----------------------------------------------------------------------------37# Generic ABC chart generator38# ----------------------------------------------------------------------------39def generate_abc_chart(df: pd.DataFrame, usage_col: str, title: str) -> Tuple[alt.Chart, pd.DataFrame]:40    """41    Expects a DataFrame with columns: [email, <usage_col>].42    usage_col: 'total_tokens', 'total_images', 'total_minutes', 'num_recordings', etc.43    44    Returns:45      - alt.Chart object46      - processed DataFrame with columns: cumulative_distribution, category, row_rank, ...47    """48    usage_per_user = df.groupby("email", as_index=False)[usage_col].sum()49 50    # If there's no data, return an empty chart and DataFrame51    if usage_per_user.empty:52        return alt.Chart(pd.DataFrame()).mark_line(), usage_per_user53 54    # Sort descending55    usage_per_user = usage_per_user.sort_values(usage_col, ascending=False).reset_index(drop=True)56    usage_per_user["overall_sum"] = usage_per_user[usage_col].sum()57    usage_per_user["running_sum"] = usage_per_user[usage_col].cumsum()58    usage_per_user["cumulative_distribution"] = usage_per_user["running_sum"] / usage_per_user["overall_sum"]59    usage_per_user["category"] = usage_per_user["cumulative_distribution"].apply(classify_new)60    usage_per_user["row_rank"] = usage_per_user.index + 161 62    # Base line chart63    base_chart = (64        alt.Chart(usage_per_user)65        .mark_line(point=True)66        .encode(67            x=alt.X("row_rank:Q", title="User Rank"),68            y=alt.Y("cumulative_distribution:Q", title=f"Cumulative % of {usage_col}"),69            tooltip=[70                alt.Tooltip("email", title="User Email"),71                alt.Tooltip(usage_col, title=f"Total {usage_col}"),72                alt.Tooltip("row_rank", title="Rank"),73                alt.Tooltip("cumulative_distribution", title="Cumulative %", format=".2%"),74                alt.Tooltip("category", title="Category"),75            ],76        )77    )78 79    # Vertical lines for thresholds80    threshold_values = [81        (0.20, "20%"),82        (0.40, "40%"),83        (0.60, "60%"),84        (0.80, "80% (A)"),85        (0.95, "95% (B)"),86    ]87    thresholds_data = []88    for val, label in threshold_values:89        # Find the rank at which cumulative_distribution crosses `val`90        rank = usage_per_user.loc[usage_per_user["cumulative_distribution"] > val, "row_rank"].min()91        if pd.isnull(rank):92            rank = usage_per_user["row_rank"].max()93        thresholds_data.append({"x": rank, "label": label})94 95    thresholds_df = pd.DataFrame(thresholds_data)96    rules_layer = alt.Chart(thresholds_df).mark_rule(color="red", strokeDash=[5, 3]).encode(97        x="x:Q", tooltip=[alt.Tooltip("label", title="Boundary")]98    )99 100    final_chart = (base_chart + rules_layer).properties(101        width=700, height=400, title=title102    )103 104    return final_chart, usage_per_user105 106# ----------------------------------------------------------------------------107# Helper to subset by model_type (used in tokens/images pages)108# ----------------------------------------------------------------------------109def subset_by_model(df: pd.DataFrame, model_type: str):110    # case-insensitive filter111    return df[df["model_type"].str.lower() == model_type.lower()]112 113# ----------------------------------------------------------------------------114# Page: Token Usage Analysis (with Deep)115# ----------------------------------------------------------------------------116def token_usage_page():117    st.header("Chat Token Usage")118 119    # Upload a CSV for token usage120    uploaded_file = st.file_uploader(121        "Upload CSV:",122        type=["csv"],123        key="tokens_file_uploader"124    )125 126    if uploaded_file is not None:127        df_new = pd.read_csv(uploaded_file)128        st.session_state["df_tokens"] = df_new  # store in session state129 130    # Retrieve from session state131    df_tokens = st.session_state["df_tokens"]132 133    if df_tokens is None:134        st.info("Please upload a CSV to analyze token usage.")135        return136 137    # Process data138    df_tokens["date"] = pd.to_datetime(df_tokens["date"], errors="coerce")139    df_tokens["month_str"] = df_tokens["date"].dt.strftime("%Y-%m")140    df_tokens["total_tokens"] = df_tokens["input_tokens"] + df_tokens["output_tokens"]141 142    all_months = sorted(df_tokens["month_str"].dropna().unique())143 144    def show_chart_and_summary(dataframe, month_label, model_filter=None):145        if model_filter:146            filtered_df = subset_by_model(dataframe, model_filter)147            title = f"{month_label} – {model_filter.capitalize()} Model"148        else:149            filtered_df = dataframe150            title = f"{month_label} – All Models"151 152        chart, processed_df = generate_abc_chart(filtered_df[["email","total_tokens"]], "total_tokens", title)153        if processed_df.empty:154            st.info(f"No data for {title}.")155            return156 157        st.altair_chart(chart, use_container_width=True)158 159        # Summarize160        category_groups = (161            processed_df162            .groupby("category", as_index=False)163            .agg(num_users=("email","count"), sum_tokens=("total_tokens","sum"))164        )165 166        total_users = processed_df["email"].nunique()167        total_tokens_sum = processed_df["total_tokens"].sum()168        category_groups["pct_users"] = category_groups["num_users"] / total_users * 100169 170        # Reorder & rename columns171        category_groups = category_groups[["category","num_users","pct_users","sum_tokens"]]172        category_groups.columns = ["Category","Total Users","% of Users","Total Tokens"]173        category_groups["% of Users"] = category_groups["% of Users"].round(2)174        category_groups["Total Tokens"] = category_groups["Total Tokens"].apply(lambda x: f"{x:,}")175 176        summary_row = pd.DataFrame([{177            "Category":"Total",178            "Total Users": total_users,179            "% of Users": "-",180            "Total Tokens": f"{total_tokens_sum:,}"181        }])182        category_groups = pd.concat([category_groups, summary_row], ignore_index=True)183 184        st.markdown("###### Summary")185        st.table(category_groups)186 187    # Loop through each month188    for month_str in all_months:189        st.subheader(f"{month_str}")190 191        df_month = df_tokens[df_tokens["month_str"] == month_str]192 193        # 1) All Models194        show_chart_and_summary(df_month, month_str)195        # 2) Fast196        show_chart_and_summary(df_month, month_str, "Fast")197        # 3) Advanced198        show_chart_and_summary(df_month, month_str, "Advanced")199        # 4) Deep200        show_chart_and_summary(df_month, month_str, "Deep")201 202# ----------------------------------------------------------------------------203# Page: Image Usage Analysis (Fast / Advanced only)204# ----------------------------------------------------------------------------205def image_usage_page():206    st.header("Image Generation Usage")207 208    # Upload a CSV for image usage209    uploaded_file = st.file_uploader(210        "Upload CSV:",211        type=["csv"],212        key="images_file_uploader"213    )214 215    if uploaded_file is not None:216        df_new = pd.read_csv(uploaded_file)217        st.session_state["df_images"] = df_new218 219    df_images = st.session_state["df_images"]220    if df_images is None:221        st.info("Please upload a CSV to analyze image usage.")222        return223 224    # Process data225    df_images["date"] = pd.to_datetime(df_images["date"], errors="coerce")226    df_images["month_str"] = df_images["date"].dt.strftime("%Y-%m")227 228    all_months = sorted(df_images["month_str"].dropna().unique())229 230    def show_chart_and_summary(dataframe, month_label, model_filter=None):231        if model_filter:232            filtered_df = subset_by_model(dataframe, model_filter)233            title = f"{month_label} – {model_filter.capitalize()} Model"234        else:235            filtered_df = dataframe236            title = f"{month_label} – All Models"237 238        chart, processed_df = generate_abc_chart(filtered_df[["email","total_images"]], "total_images", title)239        if processed_df.empty:240            st.info(f"No data for {title}.")241            return242 243        st.altair_chart(chart, use_container_width=True)244 245        category_groups = (246            processed_df247            .groupby("category", as_index=False)248            .agg(num_users=("email","count"), sum_images=("total_images","sum"))249        )250 251        total_users = processed_df["email"].nunique()252        total_images_sum = processed_df["total_images"].sum()253        category_groups["pct_users"] = category_groups["num_users"] / total_users * 100254 255        category_groups = category_groups[["category","num_users","pct_users","sum_images"]]256        category_groups.columns = ["Category","Total Users","% of Users","Total Images"]257        category_groups["% of Users"] = category_groups["% of Users"].round(2)258        category_groups["Total Images"] = category_groups["Total Images"].apply(lambda x: f"{x:,}")259 260        summary_row = pd.DataFrame([{261            "Category":"Total",262            "Total Users": total_users,263            "% of Users":"-",264            "Total Images":f"{total_images_sum:,}"265        }])266        category_groups = pd.concat([category_groups, summary_row], ignore_index=True)267 268        st.markdown("###### Summary")269        st.table(category_groups)270 271    # Loop months272    for month_str in all_months:273        st.subheader(f"{month_str}")274 275        df_month = df_images[df_images["month_str"] == month_str]276 277        # 1) All Models278        show_chart_and_summary(df_month, month_str)279        # 2) Fast280        show_chart_and_summary(df_month, month_str, "Fast")281        # 3) Advanced282        show_chart_and_summary(df_month, month_str, "Advanced")283 284# ----------------------------------------------------------------------------285# NEW PAGE: Meetings Usage286# ----------------------------------------------------------------------------287def meetings_usage_page():288    st.header("Meetings Usage")289 290    # Upload a CSV specifically for meetings usage291    uploaded_file = st.file_uploader(292        "Upload CSV (Meetings Data):",293        type=["csv"],294        key="meetings_file_uploader"295    )296 297    if uploaded_file is not None:298        df_new = pd.read_csv(uploaded_file)299        st.session_state["df_meetings"] = df_new300 301    df_meetings = st.session_state["df_meetings"]302    if df_meetings is None:303        st.info("Please upload a CSV to analyze meetings usage.")304        return305 306    # Parse start_time as date, create "month_str"307    df_meetings["start_time"] = pd.to_datetime(df_meetings["start_time"], errors="coerce")308    df_meetings["month_str"] = df_meetings["start_time"].dt.strftime("%Y-%m")309 310    # Group by email + month311    # total_minutes = sum(duration_minutes)312    # num_recordings = distinct count of id313    grouped = (314        df_meetings315        .groupby(["email", "month_str"], as_index=False)316        .agg(317            total_minutes=("duration_minutes", "sum"),318            num_recordings=("id", "nunique")319        )320    )321 322    all_months = sorted(grouped["month_str"].dropna().unique())323    324    def show_chart_and_summary(dataframe, month_label, usage_col, usage_label):325        """326        usage_col: 'total_minutes' or 'num_recordings'327        usage_label: String for chart title, e.g. "Total Minutes" or "Number of Recordings"328        """329        subset = dataframe[["email", usage_col]]330        chart, processed_df = generate_abc_chart(subset, usage_col, f"{month_label} – {usage_label}")331        if processed_df.empty:332            st.info(f"No data for {usage_label} in {month_label}.")333            return334 335        st.altair_chart(chart, use_container_width=True)336 337        # Summarize338        cat_groups = (339            processed_df340            .groupby("category", as_index=False)341            .agg(num_users=("email","count"), sum_usage=(usage_col,"sum"))342        )343        total_users = processed_df["email"].nunique()344        total_usage_sum = processed_df[usage_col].sum()345        cat_groups["pct_users"] = cat_groups["num_users"] / total_users * 100346 347        # We'll rename columns after building everything348        cat_groups["% of Users"] = cat_groups["pct_users"].round(2)349 350        # For the detail column name351        usage_col_label = f"Total {usage_col}"352        cat_groups = cat_groups.rename(columns={353            "category": "Category",354            "num_users": "Total Users",355            "sum_usage": usage_col_label356        })357 358        # If we are looking at num_recordings, add an extra column359        if usage_col == "num_recordings":360            # Compute average recordings per user in each ABC category361            cat_groups["Avg. Recordings per User"] = cat_groups[usage_col_label] / cat_groups["Total Users"]362            # Round to 2 decimals363            cat_groups["Avg. Recordings per User"] = cat_groups["Avg. Recordings per User"].round().astype(int).astype(str).str.replace('.0', '')364 365        # Convert numeric usage to a comma-separated string366        cat_groups[usage_col_label] = cat_groups[usage_col_label].apply(lambda x: f"{x:,}")367 368        # Build a summary row369        summary_data = {370            "Category": "Total",371            "Total Users": total_users,372            "% of Users": "-",373            usage_col_label: f"{total_usage_sum:,}"374        }375        # For the summary row, if we're dealing with num_recordings, add an average column376        if usage_col == "num_recordings":377            avg_recs = 0378            if total_users > 0:379                avg_recs = total_usage_sum / total_users380            summary_data["Avg. Recordings per User"] = round(avg_recs, 2)381 382        summary_row = pd.DataFrame([summary_data])383        cat_groups = pd.concat([cat_groups, summary_row], ignore_index=True)384 385        st.markdown("###### Summary")386        st.table(cat_groups)387 388    # For each month, we show 2 ABC charts:389    # 1) total_minutes390    # 2) num_recordings391    for month_str in all_months:392        st.subheader(f"{month_str}")393        df_month = grouped[grouped["month_str"] == month_str]394 395        # Chart for total_minutes396        show_chart_and_summary(df_month, month_str, "total_minutes", "Total Minutes")397 398        # Chart for num_recordings (includes Avg. Recordings per User)399        show_chart_and_summary(df_month, month_str, "num_recordings", "Number of Recordings")400 401# ----------------------------------------------------------------------------402# Main: choose which "page" to show403# ----------------------------------------------------------------------------404def main():405    st.title("ABC Analysis")406 407    page = st.sidebar.radio(408        "Select an analysis page:",409        ("Token Usage", "Image Usage", "Meetings Usage")410    )411 412    if page == "Token Usage":413        token_usage_page()414    elif page == "Image Usage":415        image_usage_page()416    else:417        meetings_usage_page()418 419if __name__ == "__main__":420    main()421