Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -2600,6 +2600,172 @@ def create_ui(manager: IssueManager) -> gr.Blocks:
|
|
2600 |
# Return default updates to clear the UI
|
2601 |
return default_updates
|
2602 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2603 |
try:
|
2604 |
# The first column (index 0) is the Issue ID
|
2605 |
selected_id = int(evt.value[0])
|
|
|
2600 |
# Return default updates to clear the UI
|
2601 |
return default_updates
|
2602 |
|
2603 |
+
try:
|
2604 |
+
# The first column (index 0) is the Issue ID
|
2605 |
+
selected_id = int(evt.value[0])
|
2606 |
+
logger.info(f"Issue selected via Dataframe: ID {selected_id}")
|
2607 |
+
|
2608 |
+
if selected_id not in manager.issues:
|
2609 |
+
logger.error(f"Selected issue ID {selected_id} not found in manager's issue list.")
|
2610 |
+
return {
|
2611 |
+
**default_updates,
|
2612 |
+
"issue_preview_html": gr.update(value=f"<p style='color: red; font-weight: bold;'>Error: Issue {selected_id} not found in the current list. Try re-scanning.</p>"),
|
2613 |
+
"selected_issue_id_hidden": gr.update(value=""), # Ensure hidden state is cleared
|
2614 |
+
}
|
2615 |
+
|
2616 |
+
issue_data = manager.issues[selected_id]
|
2617 |
+
files_content: Dict[str, str] = {}
|
2618 |
+
context_source_msg = "Loading context..."
|
2619 |
+
context_load_start = time.time()
|
2620 |
+
|
2621 |
+
# --- Load or Compute Code Context ---
|
2622 |
+
# Prioritize pre-computed context if available
|
2623 |
+
context_data = manager.precomputed_context.get(selected_id) # Use .get for safety
|
2624 |
+
|
2625 |
+
if context_data:
|
2626 |
+
timestamp = context_data.get('timestamp', 0)
|
2627 |
+
timestamp_str = datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
|
2628 |
+
if context_data.get("error"):
|
2629 |
+
context_source_msg = f"Pre-computed (Failed @ {timestamp_str})"
|
2630 |
+
files_content["error_context.txt"] = f"# Error loading pre-computed context:\n# {context_data['error']}"
|
2631 |
+
elif context_data.get("files"):
|
2632 |
+
context_source_msg = f"Pre-computed ({len(context_data['files'])} files @ {timestamp_str})"
|
2633 |
+
logger.info(f"Loading {len(context_data['files'])} files from pre-computed context for issue {selected_id}: {context_data['files']}")
|
2634 |
+
loaded_count = 0
|
2635 |
+
for file_path_str in context_data["files"]:
|
2636 |
+
full_path = manager.repo_local_path / file_path_str
|
2637 |
+
try:
|
2638 |
+
if full_path.is_file():
|
2639 |
+
# Use resolved path for robust reading
|
2640 |
+
resolved_full_path = full_path.resolve()
|
2641 |
+
# Double-check it's still in the repo path after resolve
|
2642 |
+
if resolved_full_path.is_relative_to(manager.repo_local_path.resolve()):
|
2643 |
+
files_content[file_path_str] = resolved_full_path.read_text(encoding='utf-8', errors='ignore')
|
2644 |
+
loaded_count += 1
|
2645 |
+
else:
|
2646 |
+
logger.warning(f"Pre-computed file path {file_path_str} resolved outside repo during load for issue {selected_id}.")
|
2647 |
+
files_content[file_path_str] = f"# File path resolved outside repository: {file_path_str}"
|
2648 |
+
else:
|
2649 |
+
logger.warning(f"Pre-computed file path {file_path_str} not found or not a file during load for issue {selected_id}.")
|
2650 |
+
files_content[file_path_str] = f"# File not found or not a file: {file_path_str}"
|
2651 |
+
except Exception as e:
|
2652 |
+
logger.warning(f"Error reading pre-computed file {full_path} for issue {selected_id}: {e}")
|
2653 |
+
files_content[file_path_str] = f"# Error reading file: {e}"
|
2654 |
+
# This check should be outside the for loop, indented the same as the for loop
|
2655 |
+
if loaded_count == 0 and context_data.get("files"): # Use .get for safety
|
2656 |
+
files_content["error_reading_files.txt"] = "# Precomputed context found file references, but failed to read any file content."
|
2657 |
+
# This else corresponds to elif context_data.get("files"):
|
2658 |
+
else: # Line 2690 - Corrected indentation
|
2659 |
+
context_source_msg = f"Pre-computed (No files found @ {timestamp_str})"
|
2660 |
+
files_content[f"issue_{selected_id}_context.md"] = context_data.get("content", "# No specific code context found (pre-computed).")
|
2661 |
+
|
2662 |
+
else: # Handle on-demand context computation
|
2663 |
+
logger.info(f"Context not pre-computed for issue {selected_id}, computing on demand for editor.")
|
2664 |
+
context_source_msg = "Computed On-Demand"
|
2665 |
+
context_result = await manager._get_code_context(issue_data)
|
2666 |
+
|
2667 |
+
# Store the newly computed context for future use (e.g., patch generation)
|
2668 |
+
manager.precomputed_context[selected_id] = {
|
2669 |
+
"content": context_result.get("content"),
|
2670 |
+
"files": context_result.get("files", []),
|
2671 |
+
"error": context_result.get("error"),
|
2672 |
+
"timestamp": time.time()
|
2673 |
+
}
|
2674 |
+
|
2675 |
+
if "error" in context_result and context_result["error"]:
|
2676 |
+
context_source_msg += f" (Error: {context_result['error']})"
|
2677 |
+
files_content["error_context.txt"] = f"# Error loading context on demand:\n# {context_result['error']}"
|
2678 |
+
elif context_result.get("files"):
|
2679 |
+
context_source_msg += f" ({len(context_result['files'])} files)"
|
2680 |
+
logger.info(f"Loading {len(context_result['files'])} files computed on-demand for issue {selected_id}: {context_result['files']}")
|
2681 |
+
loaded_count = 0
|
2682 |
+
for file_path_str in context_result["files"]:
|
2683 |
+
full_path = manager.repo_local_path / file_path_str
|
2684 |
+
try:
|
2685 |
+
if full_path.is_file():
|
2686 |
+
resolved_full_path = full_path.resolve()
|
2687 |
+
if resolved_full_path.is_relative_to(manager.repo_local_path.resolve()):
|
2688 |
+
files_content[file_path_str] = resolved_full_path.read_text(encoding='utf-8', errors='ignore')
|
2689 |
+
loaded_count +=1
|
2690 |
+
else:
|
2691 |
+
logger.warning(f"On-demand file path {file_path_str} resolved outside repo during load for issue {selected_id}.")
|
2692 |
+
files_content[file_path_str] = f"# File path resolved outside repository: {file_path_str}"
|
2693 |
+
else:
|
2694 |
+
logger.warning(f"On-demand file path {file_path_str} not found or not a file during load for issue {selected_id}.")
|
2695 |
+
files_content[file_path_str] = f"# File not found or not a file: {file_path_str}"
|
2696 |
+
except Exception as e:
|
2697 |
+
logger.warning(f"Error reading on-demand file {full_path} for issue {selected_id}: {e}")
|
2698 |
+
files_content[file_path_str] = f"# Error reading file: {e}"
|
2699 |
+
# This check should be outside the try/except and for loop
|
2700 |
+
if loaded_count == 0 and context_result.get("files"): # Use .get for safety
|
2701 |
+
files_content["error_reading_files.txt"] = "# Context computation found file references, but failed to read any file content."
|
2702 |
+
# This else corresponds to the inner if/elif within the on-demand block
|
2703 |
+
else:
|
2704 |
+
context_source_msg += " (No files found)"
|
2705 |
+
files_content[f"issue_{selected_id}_context.md"] = context_result.get("content", "# No specific code context found.")
|
2706 |
+
|
2707 |
+
# This line should be outside both the 'if context_data' and its 'else' branch
|
2708 |
+
context_load_duration = time.time() - context_load_start
|
2709 |
+
logger.info(f"Context loading for editor took {context_load_duration:.2f}s. Source: {context_source_msg}")
|
2710 |
+
|
2711 |
+
if not files_content:
|
2712 |
+
files_content["placeholder.txt"] = f"# No relevant files found or context failed to load for issue {selected_id}."
|
2713 |
+
|
2714 |
+
# Initialize or update the OT editor instance for this issue
|
2715 |
+
manager.code_editors[selected_id] = OTCodeEditor(initial_value=files_content)
|
2716 |
+
logger.info(f"Initialized/Updated OT editor state for issue {selected_id} with files: {list(files_content.keys())}")
|
2717 |
+
|
2718 |
+
# Determine the initial language hint for the editor based on the first file extension
|
2719 |
+
initial_language = "text" # Default to plain text
|
2720 |
+
if files_content:
|
2721 |
+
first_file_name = list(files_content.keys())[0]
|
2722 |
+
# Extract extension (part after the last dot)
|
2723 |
+
_, ext = os.path.splitext(first_file_name)
|
2724 |
+
if ext:
|
2725 |
+
# Remove leading dot and convert to lowercase
|
2726 |
+
ext = ext[1:].lower()
|
2727 |
+
# Map common extensions to ACE editor modes (approximate)
|
2728 |
+
# Check available modes in ACE editor documentation if needed
|
2729 |
+
lang_map = {
|
2730 |
+
'py': 'python', 'js': 'javascript', 'jsx': 'javascript', 'ts': 'typescript', 'tsx': 'typescript',
|
2731 |
+
'java': 'java', 'c': 'c_cpp', 'cpp': 'c_cpp', 'h': 'c_cpp', 'hpp': 'c_cpp', 'cs': 'csharp',
|
2732 |
+
'go': 'golang', 'rs': 'rust', 'php': 'php', 'rb': 'ruby', 'html': 'html', 'css': 'css',
|
2733 |
+
'scss': 'scss', 'less': 'less', 'md': 'markdown', 'yaml': 'yaml', 'yml': 'yaml', 'json': 'json',
|
2734 |
+
'toml': 'toml', 'sh': 'sh', 'bash': 'sh', 'zsh': 'sh', 'sql': 'sql', 'xml': 'xml',
|
2735 |
+
'dockerfile': 'dockerfile', 'makefile': 'makefile', 'txt': 'text', 'log': 'text',
|
2736 |
+
'conf': 'text', 'cfg': 'text', 'ini': 'ini'
|
2737 |
+
}
|
2738 |
+
initial_language = lang_map.get(ext, 'text')
|
2739 |
+
|
2740 |
+
|
2741 |
+
# Prepare updates for all relevant UI components
|
2742 |
+
updates = {
|
2743 |
+
# Update the hidden state element with the selected ID (as string)
|
2744 |
+
"selected_issue_id_hidden": gr.update(value=str(selected_id)),
|
2745 |
+
"issue_preview_html": gr.update(value=generate_issue_preview(selected_id)),
|
2746 |
+
"code_edit_component": gr.update(value=files_content, interactive=True, language=initial_language),
|
2747 |
+
"ai_output_display": gr.update(value=f"*Context loaded ({context_source_msg}). Ready for AI actions or editing.*"),
|
2748 |
+
"copy_patch_btn": gr.update(visible=False), # Hide copy button on new selection
|
2749 |
+
}
|
2750 |
+
return updates
|
2751 |
+
|
2752 |
+
except (ValueError, TypeError, IndexError, KeyError, AttributeError) as e:
|
2753 |
+
logger.exception(f"Error processing issue selection event (evt.value: {getattr(evt, 'value', 'N/A')}): {e}")
|
2754 |
+
# Return default state plus an error message
|
2755 |
+
return {
|
2756 |
+
**default_updates,
|
2757 |
+
"issue_preview_html": gr.update(value="<p style='color: red; font-weight: bold;'>Error loading issue details. Please check logs and try again.</p>"),
|
2758 |
+
"code_edit_component": gr.update(value={"error.txt": f"# Error loading code context for selection.\n# Error: {e}"}, interactive=False, language="text"),
|
2759 |
+
"ai_output_display": gr.update(value="*Error processing selection. See logs.*"),
|
2760 |
+
"selected_issue_id_hidden": gr.update(value=""), # Ensure hidden state is cleared
|
2761 |
+
"copy_patch_btn": gr.update(visible=False), # Hide copy button on error
|
2762 |
+
}
|
2763 |
+
# Check if a row was actually selected and has a value
|
2764 |
+
if evt.index is None or not hasattr(evt, 'value') or not evt.value or evt.value[0] is None:
|
2765 |
+
logger.info("Issue deselected or invalid selection event.")
|
2766 |
+
# Return default updates to clear the UI
|
2767 |
+
return default_updates
|
2768 |
+
|
2769 |
try:
|
2770 |
# The first column (index 0) is the Issue ID
|
2771 |
selected_id = int(evt.value[0])
|