Spaces:
				
			
			
	
			
			
					
		Running
		
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
	Update app2.py
Browse files
    	
        app2.py
    CHANGED
    
    | @@ -14,6 +14,7 @@ from datetime import datetime | |
| 14 | 
             
            from typing import List, Dict, Optional, Union, Tuple, Any
         | 
| 15 | 
             
            from pathlib import Path
         | 
| 16 | 
             
            from urllib.parse import urlparse, urljoin
         | 
|  | |
| 17 | 
             
            import requests
         | 
| 18 | 
             
            import validators
         | 
| 19 | 
             
            import gradio as gr
         | 
| @@ -928,7 +929,6 @@ def generate_qr_codes(data: Union[str, Dict, List], combined: bool = True) -> Li | |
| 928 | 
             
                    return []
         | 
| 929 |  | 
| 930 |  | 
| 931 | 
            -
            # --- Chatbot Logic ---
         | 
| 932 | 
             
            def respond_to_chat(
         | 
| 933 | 
             
                    message: str,
         | 
| 934 | 
             
                    chat_history: List[Tuple[str, str]],
         | 
| @@ -1219,6 +1219,31 @@ def respond_to_chat( | |
| 1219 | 
             
                return chat_history, chatbot_data, new_filtered_df_state
         | 
| 1220 |  | 
| 1221 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1222 | 
             
            # --- Gradio Interface Definition ---
         | 
| 1223 | 
             
            def create_modern_interface():
         | 
| 1224 | 
             
                """Create a modern and visually appealing Gradio interface"""
         | 
| @@ -1233,15 +1258,6 @@ def create_modern_interface(): | |
| 1233 | 
             
                    --error-color: #f56565;
         | 
| 1234 | 
             
                    --warning-color: #ed8936;
         | 
| 1235 | 
             
                }
         | 
| 1236 | 
            -
                /* Container styling */
         | 
| 1237 | 
            -
                .container {
         | 
| 1238 | 
            -
                    max-width: 1200px;
         | 
| 1239 | 
            -
                    margin: auto;
         | 
| 1240 | 
            -
                    padding: 2rem;
         | 
| 1241 | 
            -
                    background-color: var(--background-color);
         | 
| 1242 | 
            -
                    border-radius: 1rem;
         | 
| 1243 | 
            -
                    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
         | 
| 1244 | 
            -
                }
         | 
| 1245 | 
             
                /* Component styling */
         | 
| 1246 | 
             
                .input-container {
         | 
| 1247 | 
             
                    background-color: white;
         | 
| @@ -1264,19 +1280,10 @@ def create_modern_interface(): | |
| 1264 | 
             
                    background-color: var(--accent-color);
         | 
| 1265 | 
             
                    transform: translateY(-1px);
         | 
| 1266 | 
             
                }
         | 
| 1267 | 
            -
                /* Status messages */
         | 
| 1268 | 
            -
                .status {
         | 
| 1269 | 
            -
                    padding: 1rem;
         | 
| 1270 | 
            -
                    border-radius: 0.375rem;
         | 
| 1271 | 
            -
                    margin: 1rem 0;
         | 
| 1272 | 
            -
                }
         | 
| 1273 | 
            -
                .status.success { background-color: #f0fff4; color: var(--success-color); }
         | 
| 1274 | 
            -
                .status.error { background-color: #fff5f5; color: var(--error-color); }
         | 
| 1275 | 
            -
                .status.warning { background-color: #fffaf0; color: var(--warning-color); }
         | 
| 1276 | 
             
                /* Gallery styling */
         | 
| 1277 | 
             
                .gallery {
         | 
| 1278 | 
             
                    display: grid;
         | 
| 1279 | 
            -
                    grid-template-columns: repeat(auto-fit, minmax( | 
| 1280 | 
             
                    gap: 1rem;
         | 
| 1281 | 
             
                    padding: 1rem;
         | 
| 1282 | 
             
                    background-color: white;
         | 
| @@ -1315,6 +1322,19 @@ def create_modern_interface(): | |
| 1315 | 
             
                    max-width: 150px;
         | 
| 1316 | 
             
                    max-height: 150px;
         | 
| 1317 | 
             
                }
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1318 | 
             
                """
         | 
| 1319 | 
             
                with gr.Blocks(css=css, title="Advanced Data Processor & QR Generator") as interface:
         | 
| 1320 | 
             
                    interface.head += """
         | 
| @@ -1336,8 +1356,27 @@ def create_modern_interface(): | |
| 1336 | 
             
                        }
         | 
| 1337 | 
             
                        console.log("Enabled QR Code Indices:", enabledStates);
         | 
| 1338 | 
             
                    }
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1339 | 
             
                    </script>
         | 
| 1340 | 
             
                    """
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1341 | 
             
                    with gr.Row():
         | 
| 1342 | 
             
                        crawl_depth_slider = gr.Slider(
         | 
| 1343 | 
             
                            label="Crawl Depth",
         | 
| @@ -1348,12 +1387,6 @@ def create_modern_interface(): | |
| 1348 | 
             
                            interactive=True,
         | 
| 1349 | 
             
                            info="Select the maximum depth for crawling links (0-10)."
         | 
| 1350 | 
             
                        )
         | 
| 1351 | 
            -
                    qr_code_paths = gr.State([])
         | 
| 1352 | 
            -
                    chatbot_data = gr.State(None)
         | 
| 1353 | 
            -
                    gr.Markdown("""
         | 
| 1354 | 
            -
                    # 🌐 Advanced Data Processing & QR Code Generator
         | 
| 1355 | 
            -
                    Transform your data into beautifully designed, sequenced QR codes with our cutting-edge processor.
         | 
| 1356 | 
            -
                    """)
         | 
| 1357 | 
             
                    with gr.Tab("📝 URL Processing"):
         | 
| 1358 | 
             
                        url_input = gr.Textbox(
         | 
| 1359 | 
             
                            label="Enter URLs (comma or newline separated)",
         | 
| @@ -1385,30 +1418,42 @@ def create_modern_interface(): | |
| 1385 | 
             
                        )
         | 
| 1386 | 
             
                        generate_qr_toggle = gr.Checkbox(
         | 
| 1387 | 
             
                            label="Generate QR Codes",
         | 
| 1388 | 
            -
                            value=False, | 
| 1389 | 
             
                            info="Enable to generate QR codes for the processed data."
         | 
| 1390 | 
             
                        )
         | 
| 1391 | 
             
                        process_btn = gr.Button(
         | 
| 1392 | 
             
                            "🔄 Process & Generate QR",
         | 
| 1393 | 
             
                            variant="primary"
         | 
| 1394 | 
             
                        )
         | 
| 1395 | 
            -
             | 
| 1396 | 
            -
                     | 
| 1397 | 
            -
             | 
| 1398 | 
            -
                         | 
| 1399 | 
            -
             | 
| 1400 | 
            -
                         | 
| 1401 | 
            -
             | 
| 1402 | 
            -
             | 
| 1403 | 
            -
             | 
| 1404 | 
            -
             | 
| 1405 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1406 | 
             
                    with gr.Tab("🖼️ QR Code Viewport") as viewport_tab:
         | 
| 1407 | 
             
                        viewport_output = gr.HTML(label="QR Code Sequence Viewport")
         | 
| 1408 | 
             
                        enabled_qr_codes = gr.State([])
         | 
|  | |
| 1409 | 
             
                    with gr.Tab("🤖 Chat with Data") as chat_tab:
         | 
| 1410 | 
             
                        chat_history = gr.State([])
         | 
| 1411 | 
            -
                        chatbot = gr.Chatbot(label="Data Chatbot")
         | 
| 1412 | 
             
                        filtered_chatbot_df_state = gr.State(None)  # To store the filtered DataFrame
         | 
| 1413 | 
             
                        with gr.Row():
         | 
| 1414 | 
             
                            chat_input = gr.Textbox(label="Your Message", placeholder="Ask me about the processed data...")
         | 
| @@ -1416,7 +1461,7 @@ def create_modern_interface(): | |
| 1416 | 
             
                        with gr.Row():
         | 
| 1417 | 
             
                            download_full_json_btn = gr.Button("Download Full JSON")
         | 
| 1418 | 
             
                            download_filtered_json_btn = gr.Button("Download Filtered JSON")
         | 
| 1419 | 
            -
                        download_file_output = gr.File(label="Download Data", interactive=False) | 
| 1420 | 
             
                        clear_chat_btn = gr.Button("Clear Chat History")
         | 
| 1421 |  | 
| 1422 | 
             
                    def load_example():
         | 
| @@ -1454,22 +1499,35 @@ def create_modern_interface(): | |
| 1454 | 
             
                    def update_viewport(paths, enabled_states):
         | 
| 1455 | 
             
                        if not paths:
         | 
| 1456 | 
             
                            return "<p>No QR codes generated yet.</p>"
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1457 | 
             
                        num_qr_codes = len(paths)
         | 
| 1458 | 
             
                        cols = math.ceil(math.sqrt(num_qr_codes))
         | 
| 1459 | 
            -
                        cols = max(1, min(cols,  | 
| 1460 | 
            -
             | 
|  | |
|  | |
| 1461 | 
             
                        if enabled_states is None or len(enabled_states) != num_qr_codes or not enabled_states:
         | 
| 1462 | 
             
                            enabled_states = list(range(num_qr_codes))
         | 
|  | |
| 1463 | 
             
                        for i, path in enumerate(paths):
         | 
| 1464 | 
             
                            is_enabled = i in enabled_states
         | 
| 1465 | 
            -
                            border = "border: 2px solid  | 
| 1466 | 
             
                            opacity = "opacity: 1.0;" if is_enabled else "opacity: 0.5;"
         | 
| 1467 | 
            -
                             | 
| 1468 | 
            -
                             | 
| 1469 | 
            -
                             | 
| 1470 | 
            -
                             | 
| 1471 | 
            -
             | 
| 1472 | 
            -
                         | 
|  | |
|  | |
|  | |
| 1473 |  | 
| 1474 | 
             
                    def process_inputs(urls, files, text, combine, crawl_depth, generate_qr_enabled):
         | 
| 1475 | 
             
                        """Process all inputs and generate QR codes based on toggle"""
         | 
| @@ -1557,7 +1615,8 @@ def create_modern_interface(): | |
| 1557 | 
             
                            final_json_output,
         | 
| 1558 | 
             
                            [str(path) for path in qr_paths],
         | 
| 1559 | 
             
                            "\n".join(processing_status_messages),
         | 
| 1560 | 
            -
                            final_json_output
         | 
|  | |
| 1561 | 
             
                        )
         | 
| 1562 |  | 
| 1563 | 
             
                    def on_qr_generation(qr_paths_list):
         | 
| @@ -1565,18 +1624,28 @@ def create_modern_interface(): | |
| 1565 | 
             
                        initial_enabled_states = list(range(num_qrs))
         | 
| 1566 | 
             
                        return qr_paths_list, initial_enabled_states
         | 
| 1567 |  | 
|  | |
| 1568 | 
             
                    example_btn.click(load_example, inputs=[], outputs=text_input)
         | 
| 1569 | 
             
                    clear_btn.click(clear_input, inputs=[], outputs=[url_input, file_input, text_input, chatbot_data])
         | 
|  | |
| 1570 | 
             
                    process_btn.click(
         | 
| 1571 | 
             
                        process_inputs,
         | 
| 1572 | 
             
                        inputs=[url_input, file_input, text_input, combine_data, crawl_depth_slider, generate_qr_toggle],
         | 
| 1573 | 
            -
                        outputs=[output_json, output_gallery, output_text, chatbot_data]
         | 
| 1574 | 
             
                    ).then(
         | 
| 1575 | 
             
                        on_qr_generation,
         | 
| 1576 | 
             
                        inputs=[output_gallery],
         | 
| 1577 | 
             
                        outputs=[qr_code_paths, enabled_qr_codes]
         | 
| 1578 | 
             
                    )
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1579 | 
             
                    viewport_tab.select(update_viewport, inputs=[qr_code_paths, enabled_qr_codes], outputs=[viewport_output])
         | 
|  | |
| 1580 | 
             
                    send_msg_btn.click(
         | 
| 1581 | 
             
                        respond_to_chat,
         | 
| 1582 | 
             
                        inputs=[chat_input, chat_history, chatbot_data, filtered_chatbot_df_state],
         | 
| @@ -1586,6 +1655,7 @@ def create_modern_interface(): | |
| 1586 | 
             
                        inputs=None,
         | 
| 1587 | 
             
                        outputs=chat_input
         | 
| 1588 | 
             
                    )
         | 
|  | |
| 1589 | 
             
                    chat_input.submit(
         | 
| 1590 | 
             
                        respond_to_chat,
         | 
| 1591 | 
             
                        inputs=[chat_input, chat_history, chatbot_data, filtered_chatbot_df_state],
         | 
| @@ -1595,19 +1665,35 @@ def create_modern_interface(): | |
| 1595 | 
             
                        inputs=None,
         | 
| 1596 | 
             
                        outputs=chat_input
         | 
| 1597 | 
             
                    )
         | 
|  | |
| 1598 | 
             
                    clear_chat_btn.click(
         | 
| 1599 | 
            -
                        lambda: ([], None),
         | 
| 1600 | 
             
                        inputs=None,
         | 
| 1601 | 
            -
                        outputs=[chatbot, filtered_chatbot_df_state]
         | 
| 1602 | 
             
                    )
         | 
| 1603 |  | 
| 1604 | 
            -
                    def download_json_data( | 
| 1605 | 
            -
                        if  | 
| 1606 | 
             
                            logger.info(f"No data provided for download with prefix '{filename_prefix}'.")
         | 
| 1607 | 
             
                            return None
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1608 | 
             
                        try:
         | 
| 1609 | 
            -
                             | 
| 1610 | 
            -
                            json_str = json.dumps(data_list, indent=2, ensure_ascii=False)
         | 
| 1611 | 
             
                            timestamp = int(time.time())
         | 
| 1612 | 
             
                            filename = f"{filename_prefix}_{timestamp}.json"
         | 
| 1613 | 
             
                            file_path = TEMP_DIR / filename
         | 
| @@ -1619,23 +1705,18 @@ def create_modern_interface(): | |
| 1619 | 
             
                            logger.error(f"Error creating JSON file for {filename_prefix}: {e}")
         | 
| 1620 | 
             
                            return None
         | 
| 1621 |  | 
|  | |
| 1622 | 
             
                    def handle_download_full_json(current_chatbot_data_state: Optional[List[Dict]]) -> Optional[str]:
         | 
| 1623 | 
             
                        if not current_chatbot_data_state:
         | 
| 1624 | 
             
                            logger.info("No full data available to download.")
         | 
|  | |
| 1625 | 
             
                            return None
         | 
| 1626 | 
            -
                         | 
| 1627 | 
            -
                            df_to_download = pd.DataFrame(current_chatbot_data_state)
         | 
| 1628 | 
            -
                            if df_to_download.empty:
         | 
| 1629 | 
            -
                                logger.info("Full data resulted in an empty DataFrame. Nothing to download.")
         | 
| 1630 | 
            -
                                return None
         | 
| 1631 | 
            -
                        except Exception as e:
         | 
| 1632 | 
            -
                            logger.error(f"Error converting full chatbot_data to DataFrame for download: {e}")
         | 
| 1633 | 
            -
                            return None
         | 
| 1634 | 
            -
                        return download_json_data(df_to_download, "full_data")
         | 
| 1635 |  | 
| 1636 | 
             
                    def handle_download_filtered_json(current_filtered_df_state: Optional[pd.DataFrame]) -> Optional[str]:
         | 
| 1637 | 
             
                        if current_filtered_df_state is None or current_filtered_df_state.empty:
         | 
| 1638 | 
             
                            logger.info("No filtered data available to download.")
         | 
|  | |
| 1639 | 
             
                            return None
         | 
| 1640 | 
             
                        return download_json_data(current_filtered_df_state, "filtered_data")
         | 
| 1641 |  | 
| @@ -1649,33 +1730,22 @@ def create_modern_interface(): | |
| 1649 | 
             
                        inputs=[filtered_chatbot_df_state],
         | 
| 1650 | 
             
                        outputs=[download_file_output]
         | 
| 1651 | 
             
                    )
         | 
|  | |
| 1652 | 
             
                    gr.Markdown("""
         | 
| 1653 | 
             
                    ### 🚀 Features
         | 
| 1654 | 
            -
                    - **Enhanced URL Scraping**: Extracts HTML text, title, meta description, links, and attempts parsing JSON/XML from URLs based on content type. Supports crawling links up to a specified depth. | 
| 1655 | 
            -
                    - **Advanced File Processing**: Reads various text-based files | 
| 1656 | 
            -
                    - ** | 
| 1657 | 
            -
                    - ** | 
| 1658 | 
            -
                    - ** | 
| 1659 | 
            -
                    - ** | 
| 1660 | 
            -
                    - ** | 
| 1661 | 
            -
                    - **QR Code Viewport**: Visualize generated QR codes in a sequenced square grid with options to enable/disable individual codes for selective scanning/sharing.
         | 
| 1662 | 
            -
                    - **Modern Design**: Clean, responsive interface with visual feedback.
         | 
| 1663 | 
            -
                    - **Data Chatbot**: Interact conversationally with the processed JSON data to ask questions about its structure, content, or request specific information.
         | 
| 1664 | 
             
                    ### 💡 Tips
         | 
| 1665 | 
            -
                    1.  ** | 
| 1666 | 
            -
                    2.  ** | 
| 1667 | 
            -
                    3.  ** | 
| 1668 | 
            -
                    4.  **Dependencies**: Processing PDF, DOCX, RTF, and ODT files requires installing optional Python libraries (`PyPDF2`, `python-docx`, `pyth`, `odfpy`). Check the console logs for warnings if a library is missing.
         | 
| 1669 | 
            -
                    5.  **QR Codes**: Choose whether to "Combine all data into sequence" or generate separate sequences for each input item. **Remember to check the "Generate QR Codes" checkbox!**
         | 
| 1670 | 
            -
                    6.  **Processing**: Monitor the "Processing Status" box for real-time updates and notes about errors or processing steps.
         | 
| 1671 | 
            -
                    7.  **Output**: The "Processed Data" JSON box shows the structured data extracted from your inputs. The "Generated QR Codes" gallery shows the QR code images.
         | 
| 1672 | 
            -
                    8.  **Chatbot**: After processing data, go to the "Chat with Data" tab to ask questions about the JSON output.
         | 
| 1673 | 
            -
                    ### ⚙️ QR Code Viewport Instructions
         | 
| 1674 | 
            -
                    1.  Navigate to the **QR Code Viewport** tab after generating QR codes.
         | 
| 1675 | 
            -
                    2.  The generated QR codes will be displayed in a grid based on their total count.
         | 
| 1676 | 
            -
                    3.  Use the checkboxes below each QR code to enable or disable it for visual selection. Enabled codes have a green border and full opacity.
         | 
| 1677 | 
            -
                    4.  This viewport is currently for visualization and selection *within the UI*; it doesn't change the generated files themselves. You would manually select which physical QR codes to scan based on this view.
         | 
| 1678 | 
             
                    """)
         | 
|  | |
| 1679 | 
             
                return interface
         | 
| 1680 |  | 
| 1681 |  | 
|  | |
| 14 | 
             
            from typing import List, Dict, Optional, Union, Tuple, Any
         | 
| 15 | 
             
            from pathlib import Path
         | 
| 16 | 
             
            from urllib.parse import urlparse, urljoin
         | 
| 17 | 
            +
             | 
| 18 | 
             
            import requests
         | 
| 19 | 
             
            import validators
         | 
| 20 | 
             
            import gradio as gr
         | 
|  | |
| 929 | 
             
                    return []
         | 
| 930 |  | 
| 931 |  | 
|  | |
| 932 | 
             
            def respond_to_chat(
         | 
| 933 | 
             
                    message: str,
         | 
| 934 | 
             
                    chat_history: List[Tuple[str, str]],
         | 
|  | |
| 1219 | 
             
                return chat_history, chatbot_data, new_filtered_df_state
         | 
| 1220 |  | 
| 1221 |  | 
| 1222 | 
            +
            def create_qr_zip(qr_paths: List[str]) -> Optional[str]:
         | 
| 1223 | 
            +
                """Creates a zip archive from a list of QR code image paths."""
         | 
| 1224 | 
            +
                if not qr_paths:
         | 
| 1225 | 
            +
                    logger.warning("Attempted to create a zip archive, but no QR code paths were provided.")
         | 
| 1226 | 
            +
                    return None  # Return None to prevent Gradio from attempting a download
         | 
| 1227 | 
            +
                try:
         | 
| 1228 | 
            +
                    timestamp = int(time.time())
         | 
| 1229 | 
            +
                    zip_filename = f"qr_code_collection_{timestamp}.zip"
         | 
| 1230 | 
            +
                    zip_filepath = TEMP_DIR / zip_filename
         | 
| 1231 | 
            +
             | 
| 1232 | 
            +
                    with zipfile.ZipFile(zip_filepath, 'w') as zipf:
         | 
| 1233 | 
            +
                        for path_str in qr_paths:
         | 
| 1234 | 
            +
                            path = Path(path_str)
         | 
| 1235 | 
            +
                            if path.exists():
         | 
| 1236 | 
            +
                                # Use path.name to avoid storing the full directory structure in the zip
         | 
| 1237 | 
            +
                                zipf.write(path, arcname=path.name)
         | 
| 1238 | 
            +
                            else:
         | 
| 1239 | 
            +
                                logger.warning(f"QR code file not found, skipping: {path_str}")
         | 
| 1240 | 
            +
             | 
| 1241 | 
            +
                    logger.info(f"Successfully created QR code zip archive: {zip_filepath}")
         | 
| 1242 | 
            +
                    return str(zip_filepath)
         | 
| 1243 | 
            +
                except Exception as e:
         | 
| 1244 | 
            +
                    logger.error(f"Failed to create QR code zip archive: {e}")
         | 
| 1245 | 
            +
                    return None
         | 
| 1246 | 
            +
             | 
| 1247 | 
             
            # --- Gradio Interface Definition ---
         | 
| 1248 | 
             
            def create_modern_interface():
         | 
| 1249 | 
             
                """Create a modern and visually appealing Gradio interface"""
         | 
|  | |
| 1258 | 
             
                    --error-color: #f56565;
         | 
| 1259 | 
             
                    --warning-color: #ed8936;
         | 
| 1260 | 
             
                }
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1261 | 
             
                /* Component styling */
         | 
| 1262 | 
             
                .input-container {
         | 
| 1263 | 
             
                    background-color: white;
         | 
|  | |
| 1280 | 
             
                    background-color: var(--accent-color);
         | 
| 1281 | 
             
                    transform: translateY(-1px);
         | 
| 1282 | 
             
                }
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1283 | 
             
                /* Gallery styling */
         | 
| 1284 | 
             
                .gallery {
         | 
| 1285 | 
             
                    display: grid;
         | 
| 1286 | 
            +
                    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
         | 
| 1287 | 
             
                    gap: 1rem;
         | 
| 1288 | 
             
                    padding: 1rem;
         | 
| 1289 | 
             
                    background-color: white;
         | 
|  | |
| 1322 | 
             
                    max-width: 150px;
         | 
| 1323 | 
             
                    max-height: 150px;
         | 
| 1324 | 
             
                }
         | 
| 1325 | 
            +
                /* --- NEW: Fullscreen Enhancements --- */
         | 
| 1326 | 
            +
                #fullscreen-viewport-wrapper:fullscreen {
         | 
| 1327 | 
            +
                    background-color: var(--background-color) !important;
         | 
| 1328 | 
            +
                    overflow-y: auto;
         | 
| 1329 | 
            +
                    padding: 2rem;
         | 
| 1330 | 
            +
                }
         | 
| 1331 | 
            +
                #fullscreen-viewport-wrapper:fullscreen .viewport-container {
         | 
| 1332 | 
            +
                    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
         | 
| 1333 | 
            +
                }
         | 
| 1334 | 
            +
                #fullscreen-viewport-wrapper:fullscreen .viewport-item img {
         | 
| 1335 | 
            +
                    max-width: none;
         | 
| 1336 | 
            +
                    max-height: none;
         | 
| 1337 | 
            +
                }
         | 
| 1338 | 
             
                """
         | 
| 1339 | 
             
                with gr.Blocks(css=css, title="Advanced Data Processor & QR Generator") as interface:
         | 
| 1340 | 
             
                    interface.head += """
         | 
|  | |
| 1356 | 
             
                        }
         | 
| 1357 | 
             
                        console.log("Enabled QR Code Indices:", enabledStates);
         | 
| 1358 | 
             
                    }
         | 
| 1359 | 
            +
                    function goFullscreen(elementId) {
         | 
| 1360 | 
            +
                        const elem = document.getElementById(elementId);
         | 
| 1361 | 
            +
                        if (!elem) return;
         | 
| 1362 | 
            +
                        if (elem.requestFullscreen) {
         | 
| 1363 | 
            +
                            elem.requestFullscreen();
         | 
| 1364 | 
            +
                        } else if (elem.webkitRequestFullscreen) { /* Safari */
         | 
| 1365 | 
            +
                            elem.webkitRequestFullscreen();
         | 
| 1366 | 
            +
                        } else if (elem.msRequestFullscreen) { /* IE11 */
         | 
| 1367 | 
            +
                            elem.msRequestFullscreen();
         | 
| 1368 | 
            +
                        }
         | 
| 1369 | 
            +
                    }
         | 
| 1370 | 
             
                    </script>
         | 
| 1371 | 
             
                    """
         | 
| 1372 | 
            +
                    qr_code_paths = gr.State([])
         | 
| 1373 | 
            +
                    chatbot_data = gr.State(None)
         | 
| 1374 | 
            +
             | 
| 1375 | 
            +
                    gr.Markdown("""
         | 
| 1376 | 
            +
                    # 🌐 Advanced Data Processing & QR Code Generator
         | 
| 1377 | 
            +
                    Transform your data into beautifully designed, sequenced QR codes with our cutting-edge processor.
         | 
| 1378 | 
            +
                    """)
         | 
| 1379 | 
            +
                    
         | 
| 1380 | 
             
                    with gr.Row():
         | 
| 1381 | 
             
                        crawl_depth_slider = gr.Slider(
         | 
| 1382 | 
             
                            label="Crawl Depth",
         | 
|  | |
| 1387 | 
             
                            interactive=True,
         | 
| 1388 | 
             
                            info="Select the maximum depth for crawling links (0-10)."
         | 
| 1389 | 
             
                        )
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1390 | 
             
                    with gr.Tab("📝 URL Processing"):
         | 
| 1391 | 
             
                        url_input = gr.Textbox(
         | 
| 1392 | 
             
                            label="Enter URLs (comma or newline separated)",
         | 
|  | |
| 1418 | 
             
                        )
         | 
| 1419 | 
             
                        generate_qr_toggle = gr.Checkbox(
         | 
| 1420 | 
             
                            label="Generate QR Codes",
         | 
| 1421 | 
            +
                            value=False,
         | 
| 1422 | 
             
                            info="Enable to generate QR codes for the processed data."
         | 
| 1423 | 
             
                        )
         | 
| 1424 | 
             
                        process_btn = gr.Button(
         | 
| 1425 | 
             
                            "🔄 Process & Generate QR",
         | 
| 1426 | 
             
                            variant="primary"
         | 
| 1427 | 
             
                        )
         | 
| 1428 | 
            +
             | 
| 1429 | 
            +
                    # --- NEW: Two-Column Output Layout ---
         | 
| 1430 | 
            +
                    with gr.Row():
         | 
| 1431 | 
            +
                        with gr.Column(scale=1):
         | 
| 1432 | 
            +
                            output_json = gr.JSON(label="Processed Data")
         | 
| 1433 | 
            +
                        with gr.Column(scale=1):
         | 
| 1434 | 
            +
                            output_gallery = gr.Gallery(
         | 
| 1435 | 
            +
                                label="Generated QR Codes",
         | 
| 1436 | 
            +
                                columns=None,
         | 
| 1437 | 
            +
                                height="auto",
         | 
| 1438 | 
            +
                                show_label=True,
         | 
| 1439 | 
            +
                                elem_classes=["gallery"]
         | 
| 1440 | 
            +
                            )
         | 
| 1441 | 
            +
                            download_qrs_btn = gr.Button("⬇️ Download All QR Codes as ZIP")
         | 
| 1442 | 
            +
                            qr_zip_output = gr.File(label="Download QR Code ZIP", interactive=False)
         | 
| 1443 | 
            +
                            output_text = gr.Textbox(
         | 
| 1444 | 
            +
                                label="Processing Status",
         | 
| 1445 | 
            +
                                interactive=False,
         | 
| 1446 | 
            +
                                lines=8 
         | 
| 1447 | 
            +
                            )
         | 
| 1448 | 
            +
                    # --- End of New Layout ---
         | 
| 1449 | 
            +
             | 
| 1450 | 
             
                    with gr.Tab("🖼️ QR Code Viewport") as viewport_tab:
         | 
| 1451 | 
             
                        viewport_output = gr.HTML(label="QR Code Sequence Viewport")
         | 
| 1452 | 
             
                        enabled_qr_codes = gr.State([])
         | 
| 1453 | 
            +
             | 
| 1454 | 
             
                    with gr.Tab("🤖 Chat with Data") as chat_tab:
         | 
| 1455 | 
             
                        chat_history = gr.State([])
         | 
| 1456 | 
            +
                        chatbot = gr.Chatbot(label="Data Chatbot", height=500)
         | 
| 1457 | 
             
                        filtered_chatbot_df_state = gr.State(None)  # To store the filtered DataFrame
         | 
| 1458 | 
             
                        with gr.Row():
         | 
| 1459 | 
             
                            chat_input = gr.Textbox(label="Your Message", placeholder="Ask me about the processed data...")
         | 
|  | |
| 1461 | 
             
                        with gr.Row():
         | 
| 1462 | 
             
                            download_full_json_btn = gr.Button("Download Full JSON")
         | 
| 1463 | 
             
                            download_filtered_json_btn = gr.Button("Download Filtered JSON")
         | 
| 1464 | 
            +
                        download_file_output = gr.File(label="Download Data", interactive=False)
         | 
| 1465 | 
             
                        clear_chat_btn = gr.Button("Clear Chat History")
         | 
| 1466 |  | 
| 1467 | 
             
                    def load_example():
         | 
|  | |
| 1499 | 
             
                    def update_viewport(paths, enabled_states):
         | 
| 1500 | 
             
                        if not paths:
         | 
| 1501 | 
             
                            return "<p>No QR codes generated yet.</p>"
         | 
| 1502 | 
            +
             | 
| 1503 | 
            +
                        # Wrapper div with an ID for fullscreen targeting
         | 
| 1504 | 
            +
                        html_content = '<div id="fullscreen-viewport-wrapper" style="padding:1rem; border: 1px solid #ddd; border-radius: 0.5rem;">'
         | 
| 1505 | 
            +
             | 
| 1506 | 
            +
                        # Fullscreen button
         | 
| 1507 | 
            +
                        html_content += '<button onclick="goFullscreen(\'fullscreen-viewport-wrapper\')" class="primary-button" style="margin-bottom: 1rem;">View Fullscreen</button>'
         | 
| 1508 | 
            +
             | 
| 1509 | 
             
                        num_qr_codes = len(paths)
         | 
| 1510 | 
             
                        cols = math.ceil(math.sqrt(num_qr_codes))
         | 
| 1511 | 
            +
                        cols = max(1, min(cols, 8)) 
         | 
| 1512 | 
            +
             | 
| 1513 | 
            +
                        html_content += f'<div class="viewport-container" style="grid-template-columns: repeat({cols}, 1fr);">'
         | 
| 1514 | 
            +
             | 
| 1515 | 
             
                        if enabled_states is None or len(enabled_states) != num_qr_codes or not enabled_states:
         | 
| 1516 | 
             
                            enabled_states = list(range(num_qr_codes))
         | 
| 1517 | 
            +
             | 
| 1518 | 
             
                        for i, path in enumerate(paths):
         | 
| 1519 | 
             
                            is_enabled = i in enabled_states
         | 
| 1520 | 
            +
                            border = "border: 2px solid var(--success-color);" if is_enabled else "border: 2px solid #ccc;"
         | 
| 1521 | 
             
                            opacity = "opacity: 1.0;" if is_enabled else "opacity: 0.5;"
         | 
| 1522 | 
            +
                            html_content += f'<div class="viewport-item" id="qr_item_{i}">'
         | 
| 1523 | 
            +
                            html_content += f'<img src="/file={path}" style="{border} {opacity}" alt="QR Code {i + 1}">'
         | 
| 1524 | 
            +
                            html_content += f'<label style="font-size: 0.8em; margin-top: 4px;"><input type="checkbox" data-index="{i}" {"checked" if is_enabled else ""} onchange="updateEnabledStates(this)"> Enable</label>'
         | 
| 1525 | 
            +
                            html_content += '</div>'
         | 
| 1526 | 
            +
             | 
| 1527 | 
            +
                        html_content += '</div>'
         | 
| 1528 | 
            +
                        html_content += '</div>'
         | 
| 1529 | 
            +
             | 
| 1530 | 
            +
                        return html_content
         | 
| 1531 |  | 
| 1532 | 
             
                    def process_inputs(urls, files, text, combine, crawl_depth, generate_qr_enabled):
         | 
| 1533 | 
             
                        """Process all inputs and generate QR codes based on toggle"""
         | 
|  | |
| 1615 | 
             
                            final_json_output,
         | 
| 1616 | 
             
                            [str(path) for path in qr_paths],
         | 
| 1617 | 
             
                            "\n".join(processing_status_messages),
         | 
| 1618 | 
            +
                            final_json_output,
         | 
| 1619 | 
            +
                            None 
         | 
| 1620 | 
             
                        )
         | 
| 1621 |  | 
| 1622 | 
             
                    def on_qr_generation(qr_paths_list):
         | 
|  | |
| 1624 | 
             
                        initial_enabled_states = list(range(num_qrs))
         | 
| 1625 | 
             
                        return qr_paths_list, initial_enabled_states
         | 
| 1626 |  | 
| 1627 | 
            +
                    # Event Handlers
         | 
| 1628 | 
             
                    example_btn.click(load_example, inputs=[], outputs=text_input)
         | 
| 1629 | 
             
                    clear_btn.click(clear_input, inputs=[], outputs=[url_input, file_input, text_input, chatbot_data])
         | 
| 1630 | 
            +
             | 
| 1631 | 
             
                    process_btn.click(
         | 
| 1632 | 
             
                        process_inputs,
         | 
| 1633 | 
             
                        inputs=[url_input, file_input, text_input, combine_data, crawl_depth_slider, generate_qr_toggle],
         | 
| 1634 | 
            +
                        outputs=[output_json, output_gallery, output_text, chatbot_data, qr_zip_output]
         | 
| 1635 | 
             
                    ).then(
         | 
| 1636 | 
             
                        on_qr_generation,
         | 
| 1637 | 
             
                        inputs=[output_gallery],
         | 
| 1638 | 
             
                        outputs=[qr_code_paths, enabled_qr_codes]
         | 
| 1639 | 
             
                    )
         | 
| 1640 | 
            +
             | 
| 1641 | 
            +
                    download_qrs_btn.click(
         | 
| 1642 | 
            +
                        fn=create_qr_zip,
         | 
| 1643 | 
            +
                        inputs=[qr_code_paths],
         | 
| 1644 | 
            +
                        outputs=[qr_zip_output]
         | 
| 1645 | 
            +
                    )
         | 
| 1646 | 
            +
                    
         | 
| 1647 | 
             
                    viewport_tab.select(update_viewport, inputs=[qr_code_paths, enabled_qr_codes], outputs=[viewport_output])
         | 
| 1648 | 
            +
             | 
| 1649 | 
             
                    send_msg_btn.click(
         | 
| 1650 | 
             
                        respond_to_chat,
         | 
| 1651 | 
             
                        inputs=[chat_input, chat_history, chatbot_data, filtered_chatbot_df_state],
         | 
|  | |
| 1655 | 
             
                        inputs=None,
         | 
| 1656 | 
             
                        outputs=chat_input
         | 
| 1657 | 
             
                    )
         | 
| 1658 | 
            +
             | 
| 1659 | 
             
                    chat_input.submit(
         | 
| 1660 | 
             
                        respond_to_chat,
         | 
| 1661 | 
             
                        inputs=[chat_input, chat_history, chatbot_data, filtered_chatbot_df_state],
         | 
|  | |
| 1665 | 
             
                        inputs=None,
         | 
| 1666 | 
             
                        outputs=chat_input
         | 
| 1667 | 
             
                    )
         | 
| 1668 | 
            +
             | 
| 1669 | 
             
                    clear_chat_btn.click(
         | 
| 1670 | 
            +
                        lambda: ([], None, []),
         | 
| 1671 | 
             
                        inputs=None,
         | 
| 1672 | 
            +
                        outputs=[chatbot, filtered_chatbot_df_state, chat_history]
         | 
| 1673 | 
             
                    )
         | 
| 1674 |  | 
| 1675 | 
            +
                    def download_json_data(data: Optional[Union[pd.DataFrame, List[Dict]]], filename_prefix: str) -> Optional[str]:
         | 
| 1676 | 
            +
                        if data is None:
         | 
| 1677 | 
             
                            logger.info(f"No data provided for download with prefix '{filename_prefix}'.")
         | 
| 1678 | 
             
                            return None
         | 
| 1679 | 
            +
                        
         | 
| 1680 | 
            +
                        data_to_dump = None
         | 
| 1681 | 
            +
                        if isinstance(data, pd.DataFrame):
         | 
| 1682 | 
            +
                            if data.empty:
         | 
| 1683 | 
            +
                                logger.info(f"DataFrame for '{filename_prefix}' is empty. Nothing to download.")
         | 
| 1684 | 
            +
                                return None
         | 
| 1685 | 
            +
                            data_to_dump = data.to_dict(orient='records')
         | 
| 1686 | 
            +
                        elif isinstance(data, list):
         | 
| 1687 | 
            +
                            if not data:
         | 
| 1688 | 
            +
                                logger.info(f"List for '{filename_prefix}' is empty. Nothing to download.")
         | 
| 1689 | 
            +
                                return None
         | 
| 1690 | 
            +
                            data_to_dump = data
         | 
| 1691 | 
            +
                        
         | 
| 1692 | 
            +
                        if data_to_dump is None:
         | 
| 1693 | 
            +
                            return None
         | 
| 1694 | 
            +
             | 
| 1695 | 
             
                        try:
         | 
| 1696 | 
            +
                            json_str = json.dumps(data_to_dump, indent=2, ensure_ascii=False)
         | 
|  | |
| 1697 | 
             
                            timestamp = int(time.time())
         | 
| 1698 | 
             
                            filename = f"{filename_prefix}_{timestamp}.json"
         | 
| 1699 | 
             
                            file_path = TEMP_DIR / filename
         | 
|  | |
| 1705 | 
             
                            logger.error(f"Error creating JSON file for {filename_prefix}: {e}")
         | 
| 1706 | 
             
                            return None
         | 
| 1707 |  | 
| 1708 | 
            +
             | 
| 1709 | 
             
                    def handle_download_full_json(current_chatbot_data_state: Optional[List[Dict]]) -> Optional[str]:
         | 
| 1710 | 
             
                        if not current_chatbot_data_state:
         | 
| 1711 | 
             
                            logger.info("No full data available to download.")
         | 
| 1712 | 
            +
                            gr.Warning("No data has been processed yet!")
         | 
| 1713 | 
             
                            return None
         | 
| 1714 | 
            +
                        return download_json_data(current_chatbot_data_state, "full_data_collection")
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1715 |  | 
| 1716 | 
             
                    def handle_download_filtered_json(current_filtered_df_state: Optional[pd.DataFrame]) -> Optional[str]:
         | 
| 1717 | 
             
                        if current_filtered_df_state is None or current_filtered_df_state.empty:
         | 
| 1718 | 
             
                            logger.info("No filtered data available to download.")
         | 
| 1719 | 
            +
                            gr.Warning("No filtered data to download. Please filter data in the chat first.")
         | 
| 1720 | 
             
                            return None
         | 
| 1721 | 
             
                        return download_json_data(current_filtered_df_state, "filtered_data")
         | 
| 1722 |  | 
|  | |
| 1730 | 
             
                        inputs=[filtered_chatbot_df_state],
         | 
| 1731 | 
             
                        outputs=[download_file_output]
         | 
| 1732 | 
             
                    )
         | 
| 1733 | 
            +
                    
         | 
| 1734 | 
             
                    gr.Markdown("""
         | 
| 1735 | 
             
                    ### 🚀 Features
         | 
| 1736 | 
            +
                    - **Enhanced URL Scraping**: Extracts HTML text, title, meta description, links, and attempts parsing JSON/XML from URLs based on content type. Supports crawling links up to a specified depth.
         | 
| 1737 | 
            +
                    - **Advanced File Processing**: Reads various text-based files, HTML, XML, CSV, and attempts text extraction from common documents (.pdf, .docx, .rtf, .odt).
         | 
| 1738 | 
            +
                    - **Archive Support**: Extracts and processes supported files from .zip, .tar, .gz archives.
         | 
| 1739 | 
            +
                    - **Data Chatbot**: Interact conversationally with the processed JSON data to ask questions, filter, and get insights.
         | 
| 1740 | 
            +
                    - **Sequential QR Codes**: Chunks large data and embeds sequencing info for reconstruction.
         | 
| 1741 | 
            +
                    - **QR Code Viewport**: A dedicated tab with a **fullscreen mode** for viewing the entire QR code collection.
         | 
| 1742 | 
            +
                    - **Bulk Download**: Download all generated QR codes as a single ZIP file.
         | 
|  | |
|  | |
|  | |
| 1743 | 
             
                    ### 💡 Tips
         | 
| 1744 | 
            +
                    1.  **Layout**: The output is split into two columns: raw JSON on the left, and the QR Code gallery + status log on the right. This prevents the status log from hiding the QR codes.
         | 
| 1745 | 
            +
                    2.  **Fullscreen**: For the best viewing experience of all QR codes, navigate to the **"QR Code Viewport"** tab and click the **"View Fullscreen"** button.
         | 
| 1746 | 
            +
                    3.  **Download**: Use the **"Download All QR Codes as ZIP"** button located directly under the QR code gallery to save all images at once.
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 1747 | 
             
                    """)
         | 
| 1748 | 
            +
             | 
| 1749 | 
             
                return interface
         | 
| 1750 |  | 
| 1751 |  | 
