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
|