import gradio as gr import pandas as pd import plotly.express as px import plotly.graph_objects as go from datasets import load_dataset # --------------------------------------------------------------------------- # Data loading # --------------------------------------------------------------------------- DATASETS = { "EN": "AYI-NEDJIMI/ai-code-multimodal-en", "FR": "AYI-NEDJIMI/ai-code-multimodal-fr", } _cache: dict[str, pd.DataFrame] = {} def get_df(lang: str = "EN") -> pd.DataFrame: if lang not in _cache: ds = load_dataset(DATASETS[lang], split="train") _cache[lang] = ds.to_pandas() return _cache[lang] # Pre-load both for _l in DATASETS: get_df(_l) COLS_TABLE = ["name", "vendor", "category", "subcategory", "pricing"] DETAIL_FIELDS = [ "name", "vendor", "category", "subcategory", "description", "features", "strengths", "weaknesses", "ide_support", "privacy_model", "supported_languages", "pricing", "source_url", ] LABELS = { "EN": { "title": "AI Code & Multimodal Explorer", "tab_explorer": "Tools Explorer", "tab_details": "Details", "tab_compare": "Comparison", "tab_stats": "Statistics", "search": "Search by name or vendor...", "category": "Category", "all_categories": "All categories", "select_tool": "Select a tool", "select_tool_a": "Tool A", "select_tool_b": "Tool B", "compare_btn": "Compare", "no_tool": "No tool selected.", "chart_cat": "Tools by Category", "chart_vendor": "Top 15 Vendors by Number of Tools", "chart_pricing": "Pricing Distribution", "chart_cat_vendor": "Category × Vendor Heatmap", }, "FR": { "title": "AI Code & Multimodal Explorer", "tab_explorer": "Explorateur", "tab_details": "Détails", "tab_compare": "Comparaison", "tab_stats": "Statistiques", "search": "Rechercher par nom ou éditeur...", "category": "Catégorie", "all_categories": "Toutes les catégories", "select_tool": "Sélectionner un outil", "select_tool_a": "Outil A", "select_tool_b": "Outil B", "compare_btn": "Comparer", "no_tool": "Aucun outil sélectionné.", "chart_cat": "Outils par catégorie", "chart_vendor": "Top 15 éditeurs par nombre d'outils", "chart_pricing": "Répartition des tarifs", "chart_cat_vendor": "Carte thermique Catégorie × Éditeur", }, } # --------------------------------------------------------------------------- # Helper functions # --------------------------------------------------------------------------- def filter_table(search: str, category: str, lang: str) -> pd.DataFrame: df = get_df(lang) if category and category != LABELS[lang]["all_categories"]: df = df[df["category"] == category] if search: mask = ( df["name"].str.contains(search, case=False, na=False) | df["vendor"].str.contains(search, case=False, na=False) ) df = df[mask] return df[COLS_TABLE].reset_index(drop=True) def get_tool_names(lang: str) -> list[str]: return sorted(get_df(lang)["name"].dropna().unique().tolist()) def get_categories(lang: str) -> list[str]: cats = sorted(get_df(lang)["category"].dropna().unique().tolist()) return [LABELS[lang]["all_categories"]] + cats def tool_detail_md(name: str | None, lang: str) -> str: if not name: return LABELS[lang]["no_tool"] df = get_df(lang) rows = df[df["name"] == name] if rows.empty: return LABELS[lang]["no_tool"] r = rows.iloc[0] lines = [] for f in DETAIL_FIELDS: val = r.get(f, "") if pd.isna(val) or val == "": val = "—" label = f.replace("_", " ").title() if f == "source_url" and val != "—": lines.append(f"**{label}:** [{val}]({val})") else: lines.append(f"**{label}:** {val}") return "\n\n".join(lines) def compare_tools(name_a: str | None, name_b: str | None, lang: str) -> str: if not name_a or not name_b: return LABELS[lang]["no_tool"] md_a = tool_detail_md(name_a, lang) md_b = tool_detail_md(name_b, lang) sep = "\n\n---\n\n" return f"## {name_a}\n\n{md_a}{sep}## {name_b}\n\n{md_b}" # --------------------------------------------------------------------------- # Charts # --------------------------------------------------------------------------- def chart_category(lang: str): df = get_df(lang) counts = df["category"].value_counts().reset_index() counts.columns = ["category", "count"] fig = px.bar( counts, x="category", y="count", color="category", title=LABELS[lang]["chart_cat"], color_discrete_sequence=px.colors.qualitative.Set2, ) fig.update_layout(showlegend=False, template="plotly_white") return fig def chart_vendor(lang: str): df = get_df(lang) counts = df["vendor"].value_counts().head(15).reset_index() counts.columns = ["vendor", "count"] fig = px.bar( counts, x="count", y="vendor", orientation="h", title=LABELS[lang]["chart_vendor"], color="count", color_continuous_scale="Viridis", ) fig.update_layout(yaxis=dict(autorange="reversed"), template="plotly_white") return fig def chart_pricing(lang: str): df = get_df(lang) counts = df["pricing"].value_counts().reset_index() counts.columns = ["pricing", "count"] fig = px.pie( counts, names="pricing", values="count", title=LABELS[lang]["chart_pricing"], color_discrete_sequence=px.colors.qualitative.Pastel, ) fig.update_layout(template="plotly_white") return fig def chart_heatmap(lang: str): df = get_df(lang) ct = pd.crosstab(df["category"], df["vendor"]) top_vendors = df["vendor"].value_counts().head(12).index.tolist() ct = ct[[v for v in top_vendors if v in ct.columns]] fig = go.Figure(data=go.Heatmap( z=ct.values, x=ct.columns.tolist(), y=ct.index.tolist(), colorscale="YlOrRd", )) fig.update_layout( title=LABELS[lang]["chart_cat_vendor"], template="plotly_white", height=400, ) return fig # --------------------------------------------------------------------------- # Footer # --------------------------------------------------------------------------- FOOTER_HTML = """
Built by AYI-NEDJIMI Consultants
🌐 Website | LinkedIn | GitHub | X / Twitter