Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -669,6 +669,14 @@ def create_ui() -> gr.Blocks:
|
|
| 669 |
wrap=True,
|
| 670 |
interactive=False
|
| 671 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
|
| 673 |
gr.Markdown("### π All Analysis Results")
|
| 674 |
gr.Markdown("π‘ **Click repository names to visit them on Hugging Face**")
|
|
@@ -678,6 +686,29 @@ def create_ui() -> gr.Blocks:
|
|
| 678 |
wrap=True,
|
| 679 |
interactive=False
|
| 680 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 681 |
|
| 682 |
# --- Chatbot Tab ---
|
| 683 |
with gr.TabItem("π€ AI Assistant", id="chatbot_tab"):
|
|
@@ -839,37 +870,102 @@ def create_ui() -> gr.Blocks:
|
|
| 839 |
|
| 840 |
return history, "π¬ Conversation continuing...", "", "", [], 0, pd.DataFrame(), gr.update()
|
| 841 |
|
| 842 |
-
def
|
| 843 |
-
"""Handle
|
|
|
|
|
|
|
|
|
|
|
|
|
| 844 |
if evt is None:
|
| 845 |
-
return ""
|
| 846 |
|
| 847 |
try:
|
|
|
|
| 848 |
row_idx = evt.index[0]
|
| 849 |
col_idx = evt.index[1]
|
|
|
|
| 850 |
|
| 851 |
-
#
|
| 852 |
-
if
|
| 853 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 854 |
|
| 855 |
-
|
| 856 |
-
|
| 857 |
-
|
| 858 |
-
return
|
|
|
|
|
|
|
|
|
|
| 859 |
except Exception as e:
|
| 860 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 861 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 862 |
return ""
|
| 863 |
|
| 864 |
-
def handle_extract_and_analyze(history: List[Dict[str, str]]) -> Tuple[str, str, str, List[str], int, pd.DataFrame, Any, pd.DataFrame, str, Any]:
|
| 865 |
"""Extract keywords from chat, search repositories, and immediately start analysis."""
|
| 866 |
if not history:
|
| 867 |
-
return "β No conversation to extract from.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
|
| 868 |
|
| 869 |
# Convert the full, valid history for the extraction logic
|
| 870 |
tuple_history = convert_messages_to_tuples(history)
|
| 871 |
if not tuple_history:
|
| 872 |
-
return "β No completed conversations to analyze.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
|
| 873 |
|
| 874 |
# Get raw keywords string from the LLM
|
| 875 |
raw_keywords_str = extract_keywords_from_conversation(tuple_history)
|
|
@@ -879,7 +975,7 @@ def create_ui() -> gr.Blocks:
|
|
| 879 |
cleaned_keywords = [kw.strip() for kw in cleaned_keywords if kw.strip()]
|
| 880 |
|
| 881 |
if not cleaned_keywords:
|
| 882 |
-
return f"β Could not extract valid keywords. Raw output: '{raw_keywords_str}'", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
|
| 883 |
|
| 884 |
# Join them into a clean, comma-separated string
|
| 885 |
final_keywords_str = ", ".join(cleaned_keywords)
|
|
@@ -895,23 +991,23 @@ def create_ui() -> gr.Blocks:
|
|
| 895 |
unique_repo_ids = list(dict.fromkeys(repo_ids))
|
| 896 |
|
| 897 |
if not unique_repo_ids:
|
| 898 |
-
return f"β No repositories found for keywords: {final_keywords_str}", final_keywords_str, user_requirements, [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False)
|
| 899 |
|
| 900 |
write_repos_to_csv(unique_repo_ids)
|
| 901 |
df = format_dataframe_for_display(read_csv_to_dataframe())
|
| 902 |
|
| 903 |
# Immediately start analysis
|
| 904 |
try:
|
| 905 |
-
analyzed_df, analysis_status, top_repos, top_section_update = handle_analyze_all_repos(unique_repo_ids, user_requirements)
|
| 906 |
|
| 907 |
chat_status = f"π Extracted keywords β Found {len(unique_repo_ids)} repositories β Analysis complete!"
|
| 908 |
|
| 909 |
-
return chat_status, final_keywords_str, user_requirements, unique_repo_ids, 0, analyzed_df, gr.update(selected="analysis_tab"), top_repos, analysis_status, top_section_update
|
| 910 |
|
| 911 |
except Exception as e:
|
| 912 |
logger.error(f"Error during extract and analyze: {e}")
|
| 913 |
error_status = f"β
Found {len(unique_repo_ids)} repositories, but analysis failed: {e}"
|
| 914 |
-
return error_status, final_keywords_str, user_requirements, unique_repo_ids, 0, df, gr.update(selected="analysis_tab"), pd.DataFrame(), "", gr.update(visible=False)
|
| 915 |
|
| 916 |
def extract_user_requirements_from_chat(history: List[Dict[str, str]]) -> str:
|
| 917 |
"""Extract user requirements from chatbot conversation."""
|
|
@@ -930,10 +1026,10 @@ def create_ui() -> gr.Blocks:
|
|
| 930 |
requirements = "\n".join([f"- {msg}" for msg in user_messages if msg.strip()])
|
| 931 |
return requirements
|
| 932 |
|
| 933 |
-
def handle_analyze_all_repos(repo_ids: List[str], user_requirements: str, progress=gr.Progress()) -> Tuple[pd.DataFrame, str, pd.DataFrame, Any]:
|
| 934 |
"""Analyzes all repositories in the CSV file with progress tracking."""
|
| 935 |
if not repo_ids:
|
| 936 |
-
return pd.DataFrame(), "Status: No repositories to analyze. Please submit repo IDs first.", pd.DataFrame(), gr.update(visible=False)
|
| 937 |
|
| 938 |
total_repos = len(repo_ids)
|
| 939 |
|
|
@@ -1030,6 +1126,10 @@ def create_ui() -> gr.Blocks:
|
|
| 1030 |
# Get top 3 most relevant repositories using full data
|
| 1031 |
top_repos = get_top_relevant_repos(updated_df, user_requirements, top_n=3)
|
| 1032 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1033 |
# Final status with detailed breakdown
|
| 1034 |
final_status = f"π Batch Analysis Complete!\nβ
Successful: {successful_analyses}/{total_repos}\nβ Failed: {failed_analyses}/{total_repos}"
|
| 1035 |
if csv_update_failures > 0:
|
|
@@ -1043,12 +1143,12 @@ def create_ui() -> gr.Blocks:
|
|
| 1043 |
show_top_section = gr.update(visible=not top_repos.empty)
|
| 1044 |
|
| 1045 |
logger.info(f"Batch analysis completed: {successful_analyses} successful, {failed_analyses} failed, {csv_update_failures} CSV update issues")
|
| 1046 |
-
return format_dataframe_for_display(analyzed_df), final_status, format_dataframe_for_display(top_repos), show_top_section
|
| 1047 |
|
| 1048 |
except Exception as e:
|
| 1049 |
logger.error(f"Error in batch analysis: {e}")
|
| 1050 |
error_status = f"β Batch analysis failed: {e}"
|
| 1051 |
-
return format_dataframe_for_display(read_csv_to_dataframe()), error_status, pd.DataFrame(), gr.update(visible=False)
|
| 1052 |
|
| 1053 |
def handle_reset_everything() -> Tuple[List[str], int, str, pd.DataFrame, pd.DataFrame, Any, List[Dict[str, str]], str, str, str]:
|
| 1054 |
"""Reset everything to initial state - clear all data, CSV, and UI components."""
|
|
@@ -1122,9 +1222,9 @@ def create_ui() -> gr.Blocks:
|
|
| 1122 |
outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
|
| 1123 |
).then(
|
| 1124 |
# If auto_analyze is enabled and we got repos, start analysis automatically
|
| 1125 |
-
fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False)),
|
| 1126 |
inputs=[repo_ids_state, user_requirements_state, status_box_input],
|
| 1127 |
-
outputs=[df_output, status_box_input, top_repos_df, top_repos_section]
|
| 1128 |
)
|
| 1129 |
|
| 1130 |
# Smart Submit Button (same behavior as enter)
|
|
@@ -1134,9 +1234,9 @@ def create_ui() -> gr.Blocks:
|
|
| 1134 |
outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
|
| 1135 |
).then(
|
| 1136 |
# If auto_analyze is enabled and we got repos, start analysis automatically
|
| 1137 |
-
fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False)),
|
| 1138 |
inputs=[repo_ids_state, user_requirements_state, status_box_input],
|
| 1139 |
-
outputs=[df_output, status_box_input, top_repos_df, top_repos_section]
|
| 1140 |
)
|
| 1141 |
|
| 1142 |
# Auto-analyze checkbox toggle
|
|
@@ -1150,7 +1250,7 @@ def create_ui() -> gr.Blocks:
|
|
| 1150 |
analyze_all_btn.click(
|
| 1151 |
fn=handle_analyze_all_repos,
|
| 1152 |
inputs=[repo_ids_state, user_requirements_state],
|
| 1153 |
-
outputs=[df_output, status_box_analysis, top_repos_df, top_repos_section]
|
| 1154 |
)
|
| 1155 |
|
| 1156 |
# Chatbot with Auto-extraction and Auto-search
|
|
@@ -1169,9 +1269,9 @@ def create_ui() -> gr.Blocks:
|
|
| 1169 |
outputs=[current_requirements_display]
|
| 1170 |
).then(
|
| 1171 |
# If we got repos from chatbot, auto-analyze them
|
| 1172 |
-
fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False)),
|
| 1173 |
inputs=[repo_ids_state, user_requirements_state],
|
| 1174 |
-
outputs=[df_output, chat_status, top_repos_df, top_repos_section]
|
| 1175 |
)
|
| 1176 |
|
| 1177 |
send_btn.click(
|
|
@@ -1189,16 +1289,16 @@ def create_ui() -> gr.Blocks:
|
|
| 1189 |
outputs=[current_requirements_display]
|
| 1190 |
).then(
|
| 1191 |
# If we got repos from chatbot, auto-analyze them
|
| 1192 |
-
fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False)),
|
| 1193 |
inputs=[repo_ids_state, user_requirements_state],
|
| 1194 |
-
outputs=[df_output, chat_status, top_repos_df, top_repos_section]
|
| 1195 |
)
|
| 1196 |
|
| 1197 |
# Extract and Analyze Button (one-click solution for chatbot)
|
| 1198 |
extract_analyze_btn.click(
|
| 1199 |
fn=handle_extract_and_analyze,
|
| 1200 |
inputs=[chatbot],
|
| 1201 |
-
outputs=[chat_status, extracted_keywords_output, user_requirements_state, repo_ids_state, current_repo_idx_state, df_output, tabs, top_repos_df, status_box_analysis, top_repos_section]
|
| 1202 |
).then(
|
| 1203 |
# Update requirements display when they change
|
| 1204 |
fn=lambda req: req if req.strip() else "No specific requirements extracted from conversation.",
|
|
@@ -1209,19 +1309,44 @@ def create_ui() -> gr.Blocks:
|
|
| 1209 |
# Repo Explorer Tab
|
| 1210 |
setup_repo_explorer_events(repo_components, repo_states)
|
| 1211 |
|
| 1212 |
-
# Direct Repository Clicks -
|
| 1213 |
df_output.select(
|
| 1214 |
-
fn=
|
| 1215 |
inputs=[df_output],
|
| 1216 |
-
outputs=[
|
| 1217 |
-
js="(url) => { if(url && url.trim()) { window.open(url, '_blank'); } }"
|
| 1218 |
)
|
| 1219 |
|
| 1220 |
top_repos_df.select(
|
| 1221 |
-
fn=
|
| 1222 |
inputs=[top_repos_df],
|
| 1223 |
-
outputs=[
|
| 1224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1225 |
)
|
| 1226 |
|
| 1227 |
# Reset button event
|
|
|
|
| 669 |
wrap=True,
|
| 670 |
interactive=False
|
| 671 |
)
|
| 672 |
+
|
| 673 |
+
# Quick links for top repositories
|
| 674 |
+
with gr.Row():
|
| 675 |
+
top_repo_links = gr.HTML(
|
| 676 |
+
value="",
|
| 677 |
+
label="π Quick Links",
|
| 678 |
+
visible=False
|
| 679 |
+
)
|
| 680 |
|
| 681 |
gr.Markdown("### π All Analysis Results")
|
| 682 |
gr.Markdown("π‘ **Click repository names to visit them on Hugging Face**")
|
|
|
|
| 686 |
wrap=True,
|
| 687 |
interactive=False
|
| 688 |
)
|
| 689 |
+
|
| 690 |
+
# Modal popup for repository action selection (like in old_app2.py)
|
| 691 |
+
with gr.Row():
|
| 692 |
+
with gr.Column():
|
| 693 |
+
repo_action_modal = gr.Column(visible=False)
|
| 694 |
+
with repo_action_modal:
|
| 695 |
+
gr.Markdown("### π Repository Actions")
|
| 696 |
+
selected_repo_display = gr.Textbox(
|
| 697 |
+
label="Selected Repository",
|
| 698 |
+
interactive=False,
|
| 699 |
+
info="Choose what you'd like to do with this repository"
|
| 700 |
+
)
|
| 701 |
+
with gr.Row():
|
| 702 |
+
visit_repo_btn = gr.Button("π Visit Hugging Face Space", variant="primary", size="lg")
|
| 703 |
+
explore_repo_btn = gr.Button("π Open in Repo Explorer", variant="secondary", size="lg")
|
| 704 |
+
cancel_modal_btn = gr.Button("β Cancel", size="lg")
|
| 705 |
+
|
| 706 |
+
# Quick links section for all repositories
|
| 707 |
+
with gr.Row():
|
| 708 |
+
all_repo_links = gr.HTML(
|
| 709 |
+
value="",
|
| 710 |
+
label="π Repository Quick Links"
|
| 711 |
+
)
|
| 712 |
|
| 713 |
# --- Chatbot Tab ---
|
| 714 |
with gr.TabItem("π€ AI Assistant", id="chatbot_tab"):
|
|
|
|
| 870 |
|
| 871 |
return history, "π¬ Conversation continuing...", "", "", [], 0, pd.DataFrame(), gr.update()
|
| 872 |
|
| 873 |
+
def handle_dataframe_select(evt: gr.SelectData, df_data) -> Tuple[str, Any, str]:
|
| 874 |
+
"""Handle dataframe row selection - show modal for repo ID (column 0) clicks."""
|
| 875 |
+
print(f"DEBUG: Selection event triggered!")
|
| 876 |
+
print(f"DEBUG: evt = {evt}")
|
| 877 |
+
print(f"DEBUG: df_data type = {type(df_data)}")
|
| 878 |
+
|
| 879 |
if evt is None:
|
| 880 |
+
return "", gr.update(visible=False), ""
|
| 881 |
|
| 882 |
try:
|
| 883 |
+
# Get the selected row and column from the event
|
| 884 |
row_idx = evt.index[0]
|
| 885 |
col_idx = evt.index[1]
|
| 886 |
+
print(f"DEBUG: Selected row {row_idx}, column {col_idx}")
|
| 887 |
|
| 888 |
+
# Handle pandas DataFrame
|
| 889 |
+
if isinstance(df_data, pd.DataFrame) and not df_data.empty and row_idx < len(df_data):
|
| 890 |
+
|
| 891 |
+
if col_idx == 0: # Repository name column - show action modal
|
| 892 |
+
repo_id = df_data.iloc[row_idx, 0]
|
| 893 |
+
print(f"DEBUG: Extracted repo_id = '{repo_id}'")
|
| 894 |
+
|
| 895 |
+
if repo_id and str(repo_id).strip() and str(repo_id).strip() != 'nan':
|
| 896 |
+
clean_repo_id = str(repo_id).strip()
|
| 897 |
+
logger.info(f"Showing modal for repository: {clean_repo_id}")
|
| 898 |
+
return clean_repo_id, gr.update(visible=True), clean_repo_id
|
| 899 |
|
| 900 |
+
# For content columns (1,2,3) and relevance (4), do nothing since full text is shown directly
|
| 901 |
+
else:
|
| 902 |
+
print(f"DEBUG: Clicked on column {col_idx}, full text already shown in table")
|
| 903 |
+
return "", gr.update(visible=False), ""
|
| 904 |
+
else:
|
| 905 |
+
print(f"DEBUG: df_data is not a DataFrame or row_idx {row_idx} out of range")
|
| 906 |
+
|
| 907 |
except Exception as e:
|
| 908 |
+
print(f"DEBUG: Exception occurred: {e}")
|
| 909 |
+
logger.error(f"Error handling dataframe selection: {e}")
|
| 910 |
+
|
| 911 |
+
return "", gr.update(visible=False), ""
|
| 912 |
+
|
| 913 |
+
def handle_visit_repo(repo_id: str) -> Tuple[Any, str]:
|
| 914 |
+
"""Handle visiting the Hugging Face Space for the repository."""
|
| 915 |
+
if repo_id and repo_id.strip():
|
| 916 |
+
hf_url = f"https://huggingface.co/spaces/{repo_id.strip()}"
|
| 917 |
+
logger.info(f"User chose to visit: {hf_url}")
|
| 918 |
+
return gr.update(visible=False), hf_url
|
| 919 |
+
return gr.update(visible=False), ""
|
| 920 |
+
|
| 921 |
+
def handle_explore_repo(selected_repo_id: str) -> Tuple[Any, Any, Any]:
|
| 922 |
+
"""Handle navigating to the repo explorer and populate the repo ID."""
|
| 923 |
+
logger.info(f"DEBUG: handle_explore_repo called with selected_repo_id: '{selected_repo_id}'")
|
| 924 |
+
|
| 925 |
+
if selected_repo_id and selected_repo_id.strip() and selected_repo_id.strip() != 'nan':
|
| 926 |
+
clean_repo_id = selected_repo_id.strip()
|
| 927 |
+
return (
|
| 928 |
+
gr.update(visible=False), # close modal
|
| 929 |
+
gr.update(selected="repo_explorer_tab"), # switch tab
|
| 930 |
+
gr.update(value=clean_repo_id) # populate repo explorer input
|
| 931 |
+
)
|
| 932 |
+
else:
|
| 933 |
+
return (
|
| 934 |
+
gr.update(visible=False), # close modal
|
| 935 |
+
gr.update(selected="repo_explorer_tab"), # switch tab
|
| 936 |
+
gr.update() # don't change repo explorer input
|
| 937 |
+
)
|
| 938 |
+
|
| 939 |
+
def handle_cancel_modal() -> Any:
|
| 940 |
+
"""Handle closing the modal."""
|
| 941 |
+
return gr.update(visible=False)
|
| 942 |
+
|
| 943 |
+
def generate_repo_links_html(df: pd.DataFrame) -> str:
|
| 944 |
+
"""Generate HTML with clickable links for repositories."""
|
| 945 |
+
if df.empty:
|
| 946 |
+
return ""
|
| 947 |
|
| 948 |
+
html_links = []
|
| 949 |
+
for idx, row in df.iterrows():
|
| 950 |
+
repo_id = row.get('repo id', '') if hasattr(row, 'get') else row[0]
|
| 951 |
+
if repo_id and str(repo_id).strip() and str(repo_id).strip() != 'nan':
|
| 952 |
+
clean_repo_id = str(repo_id).strip()
|
| 953 |
+
hf_url = f"https://huggingface.co/spaces/{clean_repo_id}"
|
| 954 |
+
html_links.append(f'<a href="{hf_url}" target="_blank" style="display: inline-block; margin: 5px 10px; padding: 8px 16px; background: linear-gradient(45deg, #667eea, #764ba2); color: white; text-decoration: none; border-radius: 8px; font-weight: 600; transition: all 0.3s ease;">{clean_repo_id}</a>')
|
| 955 |
+
|
| 956 |
+
if html_links:
|
| 957 |
+
return f'<div style="margin: 10px 0; padding: 15px; background: rgba(255, 255, 255, 0.1); border-radius: 12px; backdrop-filter: blur(10px);">{"".join(html_links)}</div>'
|
| 958 |
return ""
|
| 959 |
|
| 960 |
+
def handle_extract_and_analyze(history: List[Dict[str, str]]) -> Tuple[str, str, str, List[str], int, pd.DataFrame, Any, pd.DataFrame, str, Any, str, str]:
|
| 961 |
"""Extract keywords from chat, search repositories, and immediately start analysis."""
|
| 962 |
if not history:
|
| 963 |
+
return "β No conversation to extract from.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
|
| 964 |
|
| 965 |
# Convert the full, valid history for the extraction logic
|
| 966 |
tuple_history = convert_messages_to_tuples(history)
|
| 967 |
if not tuple_history:
|
| 968 |
+
return "β No completed conversations to analyze.", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
|
| 969 |
|
| 970 |
# Get raw keywords string from the LLM
|
| 971 |
raw_keywords_str = extract_keywords_from_conversation(tuple_history)
|
|
|
|
| 975 |
cleaned_keywords = [kw.strip() for kw in cleaned_keywords if kw.strip()]
|
| 976 |
|
| 977 |
if not cleaned_keywords:
|
| 978 |
+
return f"β Could not extract valid keywords. Raw output: '{raw_keywords_str}'", "", "", [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
|
| 979 |
|
| 980 |
# Join them into a clean, comma-separated string
|
| 981 |
final_keywords_str = ", ".join(cleaned_keywords)
|
|
|
|
| 991 |
unique_repo_ids = list(dict.fromkeys(repo_ids))
|
| 992 |
|
| 993 |
if not unique_repo_ids:
|
| 994 |
+
return f"β No repositories found for keywords: {final_keywords_str}", final_keywords_str, user_requirements, [], 0, pd.DataFrame(), gr.update(), pd.DataFrame(), "", gr.update(visible=False), "", ""
|
| 995 |
|
| 996 |
write_repos_to_csv(unique_repo_ids)
|
| 997 |
df = format_dataframe_for_display(read_csv_to_dataframe())
|
| 998 |
|
| 999 |
# Immediately start analysis
|
| 1000 |
try:
|
| 1001 |
+
analyzed_df, analysis_status, top_repos, top_section_update, all_links, top_links = handle_analyze_all_repos(unique_repo_ids, user_requirements)
|
| 1002 |
|
| 1003 |
chat_status = f"π Extracted keywords β Found {len(unique_repo_ids)} repositories β Analysis complete!"
|
| 1004 |
|
| 1005 |
+
return chat_status, final_keywords_str, user_requirements, unique_repo_ids, 0, analyzed_df, gr.update(selected="analysis_tab"), top_repos, analysis_status, top_section_update, all_links, top_links
|
| 1006 |
|
| 1007 |
except Exception as e:
|
| 1008 |
logger.error(f"Error during extract and analyze: {e}")
|
| 1009 |
error_status = f"β
Found {len(unique_repo_ids)} repositories, but analysis failed: {e}"
|
| 1010 |
+
return error_status, final_keywords_str, user_requirements, unique_repo_ids, 0, df, gr.update(selected="analysis_tab"), pd.DataFrame(), "", gr.update(visible=False), "", ""
|
| 1011 |
|
| 1012 |
def extract_user_requirements_from_chat(history: List[Dict[str, str]]) -> str:
|
| 1013 |
"""Extract user requirements from chatbot conversation."""
|
|
|
|
| 1026 |
requirements = "\n".join([f"- {msg}" for msg in user_messages if msg.strip()])
|
| 1027 |
return requirements
|
| 1028 |
|
| 1029 |
+
def handle_analyze_all_repos(repo_ids: List[str], user_requirements: str, progress=gr.Progress()) -> Tuple[pd.DataFrame, str, pd.DataFrame, Any, str, str]:
|
| 1030 |
"""Analyzes all repositories in the CSV file with progress tracking."""
|
| 1031 |
if not repo_ids:
|
| 1032 |
+
return pd.DataFrame(), "Status: No repositories to analyze. Please submit repo IDs first.", pd.DataFrame(), gr.update(visible=False), "", ""
|
| 1033 |
|
| 1034 |
total_repos = len(repo_ids)
|
| 1035 |
|
|
|
|
| 1126 |
# Get top 3 most relevant repositories using full data
|
| 1127 |
top_repos = get_top_relevant_repos(updated_df, user_requirements, top_n=3)
|
| 1128 |
|
| 1129 |
+
# Generate HTML links for repositories
|
| 1130 |
+
all_links_html = generate_repo_links_html(analyzed_df)
|
| 1131 |
+
top_links_html = generate_repo_links_html(top_repos) if not top_repos.empty else ""
|
| 1132 |
+
|
| 1133 |
# Final status with detailed breakdown
|
| 1134 |
final_status = f"π Batch Analysis Complete!\nβ
Successful: {successful_analyses}/{total_repos}\nβ Failed: {failed_analyses}/{total_repos}"
|
| 1135 |
if csv_update_failures > 0:
|
|
|
|
| 1143 |
show_top_section = gr.update(visible=not top_repos.empty)
|
| 1144 |
|
| 1145 |
logger.info(f"Batch analysis completed: {successful_analyses} successful, {failed_analyses} failed, {csv_update_failures} CSV update issues")
|
| 1146 |
+
return format_dataframe_for_display(analyzed_df), final_status, format_dataframe_for_display(top_repos), show_top_section, all_links_html, top_links_html
|
| 1147 |
|
| 1148 |
except Exception as e:
|
| 1149 |
logger.error(f"Error in batch analysis: {e}")
|
| 1150 |
error_status = f"β Batch analysis failed: {e}"
|
| 1151 |
+
return format_dataframe_for_display(read_csv_to_dataframe()), error_status, pd.DataFrame(), gr.update(visible=False), "", ""
|
| 1152 |
|
| 1153 |
def handle_reset_everything() -> Tuple[List[str], int, str, pd.DataFrame, pd.DataFrame, Any, List[Dict[str, str]], str, str, str]:
|
| 1154 |
"""Reset everything to initial state - clear all data, CSV, and UI components."""
|
|
|
|
| 1222 |
outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
|
| 1223 |
).then(
|
| 1224 |
# If auto_analyze is enabled and we got repos, start analysis automatically
|
| 1225 |
+
fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False), "", ""),
|
| 1226 |
inputs=[repo_ids_state, user_requirements_state, status_box_input],
|
| 1227 |
+
outputs=[df_output, status_box_input, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
|
| 1228 |
)
|
| 1229 |
|
| 1230 |
# Smart Submit Button (same behavior as enter)
|
|
|
|
| 1234 |
outputs=[repo_ids_state, current_repo_idx_state, df_output, status_box_input, tabs, status_box_input]
|
| 1235 |
).then(
|
| 1236 |
# If auto_analyze is enabled and we got repos, start analysis automatically
|
| 1237 |
+
fn=lambda repo_ids, user_reqs, trigger: handle_analyze_all_repos(repo_ids, user_reqs) if trigger == "auto_analyze" and repo_ids else (pd.DataFrame(), "Ready for analysis.", pd.DataFrame(), gr.update(visible=False), "", ""),
|
| 1238 |
inputs=[repo_ids_state, user_requirements_state, status_box_input],
|
| 1239 |
+
outputs=[df_output, status_box_input, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
|
| 1240 |
)
|
| 1241 |
|
| 1242 |
# Auto-analyze checkbox toggle
|
|
|
|
| 1250 |
analyze_all_btn.click(
|
| 1251 |
fn=handle_analyze_all_repos,
|
| 1252 |
inputs=[repo_ids_state, user_requirements_state],
|
| 1253 |
+
outputs=[df_output, status_box_analysis, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
|
| 1254 |
)
|
| 1255 |
|
| 1256 |
# Chatbot with Auto-extraction and Auto-search
|
|
|
|
| 1269 |
outputs=[current_requirements_display]
|
| 1270 |
).then(
|
| 1271 |
# If we got repos from chatbot, auto-analyze them
|
| 1272 |
+
fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False), "", ""),
|
| 1273 |
inputs=[repo_ids_state, user_requirements_state],
|
| 1274 |
+
outputs=[df_output, chat_status, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
|
| 1275 |
)
|
| 1276 |
|
| 1277 |
send_btn.click(
|
|
|
|
| 1289 |
outputs=[current_requirements_display]
|
| 1290 |
).then(
|
| 1291 |
# If we got repos from chatbot, auto-analyze them
|
| 1292 |
+
fn=lambda repo_ids, user_reqs: handle_analyze_all_repos(repo_ids, user_reqs) if repo_ids else (pd.DataFrame(), "", pd.DataFrame(), gr.update(visible=False), "", ""),
|
| 1293 |
inputs=[repo_ids_state, user_requirements_state],
|
| 1294 |
+
outputs=[df_output, chat_status, top_repos_df, top_repos_section, all_repo_links, top_repo_links]
|
| 1295 |
)
|
| 1296 |
|
| 1297 |
# Extract and Analyze Button (one-click solution for chatbot)
|
| 1298 |
extract_analyze_btn.click(
|
| 1299 |
fn=handle_extract_and_analyze,
|
| 1300 |
inputs=[chatbot],
|
| 1301 |
+
outputs=[chat_status, extracted_keywords_output, user_requirements_state, repo_ids_state, current_repo_idx_state, df_output, tabs, top_repos_df, status_box_analysis, top_repos_section, all_repo_links, top_repo_links]
|
| 1302 |
).then(
|
| 1303 |
# Update requirements display when they change
|
| 1304 |
fn=lambda req: req if req.strip() else "No specific requirements extracted from conversation.",
|
|
|
|
| 1309 |
# Repo Explorer Tab
|
| 1310 |
setup_repo_explorer_events(repo_components, repo_states)
|
| 1311 |
|
| 1312 |
+
# Direct Repository Clicks - Show Modal (like old_app2.py)
|
| 1313 |
df_output.select(
|
| 1314 |
+
fn=handle_dataframe_select,
|
| 1315 |
inputs=[df_output],
|
| 1316 |
+
outputs=[selected_repo_display, repo_action_modal, selected_repo_id_state]
|
|
|
|
| 1317 |
)
|
| 1318 |
|
| 1319 |
top_repos_df.select(
|
| 1320 |
+
fn=handle_dataframe_select,
|
| 1321 |
inputs=[top_repos_df],
|
| 1322 |
+
outputs=[selected_repo_display, repo_action_modal, selected_repo_id_state]
|
| 1323 |
+
)
|
| 1324 |
+
|
| 1325 |
+
# Modal button events (like old_app2.py)
|
| 1326 |
+
visit_repo_btn.click(
|
| 1327 |
+
fn=handle_visit_repo,
|
| 1328 |
+
inputs=[selected_repo_display],
|
| 1329 |
+
outputs=[repo_action_modal, selected_repo_display],
|
| 1330 |
+
js="(repo_id) => { if(repo_id && repo_id.trim()) { window.open('https://huggingface.co/spaces/' + repo_id.trim(), '_blank'); } }"
|
| 1331 |
+
)
|
| 1332 |
+
explore_repo_btn.click(
|
| 1333 |
+
fn=handle_explore_repo,
|
| 1334 |
+
inputs=[selected_repo_id_state],
|
| 1335 |
+
outputs=[
|
| 1336 |
+
repo_action_modal,
|
| 1337 |
+
tabs,
|
| 1338 |
+
repo_components["repo_explorer_input"]
|
| 1339 |
+
],
|
| 1340 |
+
js="""(repo_id) => {
|
| 1341 |
+
console.log('DEBUG: Navigate to repo explorer for:', repo_id);
|
| 1342 |
+
setTimeout(() => {
|
| 1343 |
+
window.scrollTo({top: 0, behavior: 'smooth'});
|
| 1344 |
+
}, 200);
|
| 1345 |
+
}"""
|
| 1346 |
+
)
|
| 1347 |
+
cancel_modal_btn.click(
|
| 1348 |
+
fn=handle_cancel_modal,
|
| 1349 |
+
outputs=[repo_action_modal]
|
| 1350 |
)
|
| 1351 |
|
| 1352 |
# Reset button event
|