Spaces:
Running
Running
Update app2.py
Browse files
app2.py
CHANGED
@@ -27,6 +27,8 @@ import numpy as np
|
|
27 |
import tarfile
|
28 |
import gzip
|
29 |
import math
|
|
|
|
|
30 |
|
31 |
# Setup enhanced logging with more detailed formatting
|
32 |
logging.basicConfig(
|
@@ -1132,6 +1134,281 @@ def generate_qr_codes(data: Union[str, Dict, List], combined: bool = True) -> Li
|
|
1132 |
# process_inputs function already handles calling them and getting
|
1133 |
# the combined list of results.
|
1134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1135 |
def create_modern_interface():
|
1136 |
"""Create a modern and visually appealing Gradio interface"""
|
1137 |
|
@@ -1268,6 +1545,7 @@ def create_modern_interface():
|
|
1268 |
)
|
1269 |
|
1270 |
qr_code_paths = gr.State([])
|
|
|
1271 |
gr.Markdown("""
|
1272 |
# π Advanced Data Processing & QR Code Generator
|
1273 |
Transform your data into beautifully designed, sequenced QR codes with our cutting-edge processor.
|
@@ -1322,6 +1600,15 @@ def create_modern_interface():
|
|
1322 |
viewport_output = gr.HTML(label="QR Code Sequence Viewport")
|
1323 |
enabled_qr_codes = gr.State([]) # To store the enabled/disabled state
|
1324 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1325 |
# Load example data
|
1326 |
def load_example():
|
1327 |
example = {
|
@@ -1353,7 +1640,7 @@ def create_modern_interface():
|
|
1353 |
return json.dumps(example, indent=2)
|
1354 |
|
1355 |
def clear_input():
|
1356 |
-
return "", None, "" # Clear url, files, text
|
1357 |
|
1358 |
def update_viewport(paths, enabled_states):
|
1359 |
if not paths:
|
@@ -1478,10 +1765,12 @@ def create_modern_interface():
|
|
1478 |
logger.error(f"Overall processing error in process_inputs: {e}")
|
1479 |
processing_status_messages.append(f"β An unexpected error occurred during processing: {str(e)}")
|
1480 |
|
|
|
1481 |
return (
|
1482 |
final_json_output,
|
1483 |
[str(path) for path in qr_paths], # Gradio Gallery expects list of paths (strings)
|
1484 |
-
"\n".join(processing_status_messages) # Join status messages
|
|
|
1485 |
)
|
1486 |
|
1487 |
def on_qr_generation(qr_paths_list):
|
@@ -1497,12 +1786,12 @@ def create_modern_interface():
|
|
1497 |
|
1498 |
# Link events
|
1499 |
example_btn.click(load_example, inputs=[], outputs=text_input)
|
1500 |
-
clear_btn.click(clear_input, inputs=[], outputs=[url_input, file_input, text_input]) # Clear all inputs
|
1501 |
|
1502 |
process_btn.click(
|
1503 |
process_inputs,
|
1504 |
inputs=[url_input, file_input, text_input, combine_data, crawl_depth_slider], # Pass crawl_depth_slider value
|
1505 |
-
outputs=[output_json, output_gallery, output_text]
|
1506 |
).then( # Chain a .then() to update the QR paths state and trigger viewport update
|
1507 |
on_qr_generation,
|
1508 |
inputs=[output_gallery], # Get the list of paths from the gallery output
|
@@ -1512,6 +1801,34 @@ def create_modern_interface():
|
|
1512 |
# The viewport tab's select event will trigger update_viewport to render the grid
|
1513 |
viewport_tab.select(update_viewport, inputs=[qr_code_paths, enabled_qr_codes], outputs=[viewport_output])
|
1514 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1515 |
# Add helpful documentation
|
1516 |
gr.Markdown("""
|
1517 |
### π Features
|
@@ -1524,6 +1841,7 @@ def create_modern_interface():
|
|
1524 |
- **Sequential QR Codes**: Maintains data integrity across multiple codes by chunking the combined/individual processed data.
|
1525 |
- **QR Code Viewport**: Visualize generated QR codes in a sequenced square grid with options to enable/disable individual codes for selective scanning/sharing.
|
1526 |
- **Modern Design**: Clean, responsive interface with visual feedback.
|
|
|
1527 |
### π‘ Tips
|
1528 |
1. **URLs**: Enter multiple URLs separated by commas or newlines. The processor will attempt to fetch and structure the content based on its type, following links up to the specified **Crawl Depth**.
|
1529 |
2. **Files**: Upload any type of file. The processor will attempt to handle supported text-based files, archives (.zip, .tar, .gz), and specific document/structured formats.
|
@@ -1532,6 +1850,7 @@ def create_modern_interface():
|
|
1532 |
5. **QR Codes**: Choose whether to "Combine all data into sequence" or generate separate sequences for each input item.
|
1533 |
6. **Processing**: Monitor the "Processing Status" box for real-time updates and notes about errors or processing steps.
|
1534 |
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.
|
|
|
1535 |
### βοΈ QR Code Viewport Instructions
|
1536 |
1. Navigate to the **QR Code Viewport** tab after generating QR codes.
|
1537 |
2. The generated QR codes will be displayed in a grid based on their total count.
|
|
|
27 |
import tarfile
|
28 |
import gzip
|
29 |
import math
|
30 |
+
import random # Added for dummy chatbot response variation
|
31 |
+
import pandas as pd # Added for potential data analysis
|
32 |
|
33 |
# Setup enhanced logging with more detailed formatting
|
34 |
logging.basicConfig(
|
|
|
1134 |
# process_inputs function already handles calling them and getting
|
1135 |
# the combined list of results.
|
1136 |
|
1137 |
+
# --- Chatbot Logic ---
|
1138 |
+
def respond_to_chat(message: str, chat_history: List[Tuple[str, str]], chatbot_data: Optional[List[Dict]]) -> Tuple[List[Tuple[str, str]], List[Dict]]:
|
1139 |
+
"""Responds to user chat messages based on the loaded JSON data."""
|
1140 |
+
if chatbot_data is None or not chatbot_data:
|
1141 |
+
chat_history.append((message, "Please process some data first using the other tabs before chatting."))
|
1142 |
+
return chat_history, chatbot_data
|
1143 |
+
|
1144 |
+
# Add user message to history
|
1145 |
+
chat_history.append((message, "")) # Add empty string for bot response initially
|
1146 |
+
|
1147 |
+
response = ""
|
1148 |
+
lower_message = message.lower().strip()
|
1149 |
+
|
1150 |
+
try:
|
1151 |
+
# Attempt to flatten the data structure for easier querying if it's nested
|
1152 |
+
# This is a simplified approach; a real implementation might need a more
|
1153 |
+
# sophisticated data traversal/query engine.
|
1154 |
+
flat_data = []
|
1155 |
+
for item in chatbot_data:
|
1156 |
+
# Recursively flatten dictionaries and lists within the item
|
1157 |
+
def flatten_item(d, parent_key='', sep='_'):
|
1158 |
+
items = []
|
1159 |
+
for k, v in d.items():
|
1160 |
+
new_key = parent_key + sep + k if parent_key else k
|
1161 |
+
if isinstance(v, dict):
|
1162 |
+
items.extend(flatten_item(v, new_key, sep=sep).items())
|
1163 |
+
elif isinstance(v, list):
|
1164 |
+
# Flatten lists by processing each element
|
1165 |
+
for i, elem in enumerate(v):
|
1166 |
+
if isinstance(elem, (dict, list)):
|
1167 |
+
items.extend(flatten_item({f'{new_key}_{i}': elem}, sep=sep).items())
|
1168 |
+
else:
|
1169 |
+
items.append((f'{new_key}_{i}', elem))
|
1170 |
+
else:
|
1171 |
+
items.append((new_key, v))
|
1172 |
+
return dict(items)
|
1173 |
+
|
1174 |
+
if isinstance(item, dict):
|
1175 |
+
flat_data.append(flatten_item(item))
|
1176 |
+
elif isinstance(item, list):
|
1177 |
+
# If a top-level item is a list, try flattening its elements
|
1178 |
+
for sub_item in item:
|
1179 |
+
if isinstance(sub_item, dict):
|
1180 |
+
flat_data.append(flatten_item(sub_item))
|
1181 |
+
else:
|
1182 |
+
# Handle non-dict items in the top-level list if necessary
|
1183 |
+
flat_data.append({'value': sub_item}) # Wrap in dict
|
1184 |
+
|
1185 |
+
|
1186 |
+
# Use pandas DataFrame for easier querying and analysis if flat_data is suitable
|
1187 |
+
df = None
|
1188 |
+
if flat_data:
|
1189 |
+
try:
|
1190 |
+
# Attempt to create a DataFrame. This might fail if structures are highly inconsistent.
|
1191 |
+
df = pd.DataFrame(flat_data)
|
1192 |
+
logger.debug(f"Created DataFrame with shape: {df.shape}")
|
1193 |
+
logger.debug(f"DataFrame columns: {list(df.columns)}")
|
1194 |
+
except Exception as e:
|
1195 |
+
logger.warning(f"Could not create pandas DataFrame from processed data: {e}. Falling back to manual processing.")
|
1196 |
+
df = None # Ensure df is None if creation failed
|
1197 |
+
|
1198 |
+
|
1199 |
+
# --- Complex Queries and Analysis ---
|
1200 |
+
|
1201 |
+
if df is not None:
|
1202 |
+
# Example: How many unique values in a column?
|
1203 |
+
match = re.search(r'how many unique values in (\w+)', lower_message)
|
1204 |
+
if match:
|
1205 |
+
column_name = match.group(1)
|
1206 |
+
if column_name in df.columns:
|
1207 |
+
unique_count = df[column_name].nunique()
|
1208 |
+
response = f"There are {unique_count} unique values in the '{column_name}' column."
|
1209 |
+
else:
|
1210 |
+
response = f"I couldn't find a column named '{column_name}' in the data. Available columns are: {', '.join(df.columns)}"
|
1211 |
+
|
1212 |
+
# Example: What is the average/sum/min/max of a numeric column?
|
1213 |
+
match = re.search(r'what is the (average|sum|min|max) of (\w+)', lower_message)
|
1214 |
+
if match:
|
1215 |
+
operation, column_name = match.groups()
|
1216 |
+
if column_name in df.columns:
|
1217 |
+
try:
|
1218 |
+
# Attempt to convert column to numeric, coercing errors
|
1219 |
+
numeric_col = pd.to_numeric(df[column_name], errors='coerce')
|
1220 |
+
# Drop NaNs that resulted from coercion
|
1221 |
+
numeric_col = numeric_col.dropna()
|
1222 |
+
|
1223 |
+
if not numeric_col.empty:
|
1224 |
+
if operation == 'average':
|
1225 |
+
result = numeric_col.mean()
|
1226 |
+
response = f"The average of '{column_name}' is {result:.2f}."
|
1227 |
+
elif operation == 'sum':
|
1228 |
+
result = numeric_col.sum()
|
1229 |
+
response = f"The sum of '{column_name}' is {result:.2f}."
|
1230 |
+
elif operation == 'min':
|
1231 |
+
result = numeric_col.min()
|
1232 |
+
response = f"The minimum of '{column_name}' is {result}."
|
1233 |
+
elif operation == 'max':
|
1234 |
+
result = numeric_col.max()
|
1235 |
+
response = f"The maximum of '{column_name}' is {result}."
|
1236 |
+
else:
|
1237 |
+
response = "I can calculate average, sum, min, or max."
|
1238 |
+
else:
|
1239 |
+
response = f"The column '{column_name}' does not contain numeric values that I can analyze."
|
1240 |
+
except Exception as e:
|
1241 |
+
response = f"An error occurred while calculating the {operation} of '{column_name}': {e}"
|
1242 |
+
logger.error(f"Error calculating {operation} for column '{column_name}': {e}")
|
1243 |
+
else:
|
1244 |
+
response = f"I couldn't find a column named '{column_name}'. Available columns are: {', '.join(df.columns)}"
|
1245 |
+
|
1246 |
+
# Example: Filter data based on a simple condition (e.g., price > 100)
|
1247 |
+
match = re.search(r'show me items where (\w+)\s*([<>=!]+)\s*(\w+)', lower_message)
|
1248 |
+
if match:
|
1249 |
+
column_name, operator, value_str = match.groups()
|
1250 |
+
try:
|
1251 |
+
# Attempt to infer value type (numeric, string)
|
1252 |
+
try:
|
1253 |
+
value = float(value_str)
|
1254 |
+
is_numeric_comparison = True
|
1255 |
+
except ValueError:
|
1256 |
+
value = value_str.strip("'\"") # Remove quotes for string comparison
|
1257 |
+
is_numeric_comparison = False
|
1258 |
+
|
1259 |
+
if column_name in df.columns:
|
1260 |
+
if is_numeric_comparison:
|
1261 |
+
# Ensure the column is numeric for comparison
|
1262 |
+
numeric_col = pd.to_numeric(df[column_name], errors='coerce')
|
1263 |
+
filtered_df = df.loc[pd.notna(numeric_col)] # Filter out rows where conversion failed
|
1264 |
+
|
1265 |
+
if operator == '>':
|
1266 |
+
filtered_results = filtered_df[numeric_col > value]
|
1267 |
+
elif operator == '<':
|
1268 |
+
filtered_results = filtered_df[numeric_col < value]
|
1269 |
+
elif operator == '>=':
|
1270 |
+
filtered_results = filtered_df[numeric_col >= value]
|
1271 |
+
elif operator == '<=':
|
1272 |
+
filtered_results = filtered_df[numeric_col <= value]
|
1273 |
+
elif operator == '==':
|
1274 |
+
filtered_results = filtered_df[numeric_col == value]
|
1275 |
+
elif operator == '!=':
|
1276 |
+
filtered_results = filtered_df[numeric_col != value]
|
1277 |
+
else:
|
1278 |
+
filtered_results = pd.DataFrame() # Empty if operator is not recognized
|
1279 |
+
response = f"Unsupported numeric operator: {operator}. Try >, <, >=, <=, ==, !=."
|
1280 |
+
|
1281 |
+
if not filtered_results.empty:
|
1282 |
+
preview = filtered_results.to_json(orient='records', indent=2)[:500] + "..." if len(filtered_results.to_json()) > 500 else filtered_results.to_json(orient='records', indent=2)
|
1283 |
+
response = f"Here are the items where '{column_name}' {operator} {value_str}:\n```json\n{preview}\n```"
|
1284 |
+
elif 'response' not in locals(): # Only set if not already set by unsupported operator
|
1285 |
+
response = f"No items found where '{column_name}' {operator} {value_str}."
|
1286 |
+
|
1287 |
+
elif operator == '==': # Simple string equality
|
1288 |
+
filtered_results = df[df[column_name] == value]
|
1289 |
+
if not filtered_results.empty:
|
1290 |
+
preview = filtered_results.to_json(orient='records', indent=2)[:500] + "..." if len(filtered_results.to_json()) > 500 else filtered_results.to_json(orient='records', indent=2)
|
1291 |
+
response = f"Here are the items where '{column_name}' is '{value}':\n```json\n{preview}\n```"
|
1292 |
+
else:
|
1293 |
+
response = f"No items found where '{column_name}' is '{value}'."
|
1294 |
+
elif operator == '!=': # Simple string inequality
|
1295 |
+
filtered_results = df[df[column_name] != value]
|
1296 |
+
if not filtered_results.empty:
|
1297 |
+
preview = filtered_results.to_json(orient='records', indent=2)[:500] + "..." if len(filtered_results.to_json()) > 500 else filtered_results.to_json(orient='records', indent=2)
|
1298 |
+
response = f"Here are the items where '{column_name}' is not '{value}':\n```json\n{preview}\n```"
|
1299 |
+
else:
|
1300 |
+
response = f"All items have '{column_name}' as '{value}' or the column doesn't exist."
|
1301 |
+
else:
|
1302 |
+
response = f"Unsupported operator for string comparison: {operator}. Try == or !=."
|
1303 |
+
|
1304 |
+
|
1305 |
+
else:
|
1306 |
+
response = f"I couldn't find a column named '{column_name}'. Available columns are: {', '.join(df.columns)}"
|
1307 |
+
|
1308 |
+
except Exception as e:
|
1309 |
+
response = f"An error occurred while filtering data: {e}"
|
1310 |
+
logger.error(f"Error filtering data based on condition: {e}")
|
1311 |
+
|
1312 |
+
|
1313 |
+
# --- General Queries ---
|
1314 |
+
|
1315 |
+
if not response: # If no specific analysis/query matched yet
|
1316 |
+
if "how many items" in lower_message or "number of items" in lower_message:
|
1317 |
+
if isinstance(chatbot_data, list):
|
1318 |
+
response = f"There are {len(chatbot_data)} top-level items in the processed data."
|
1319 |
+
elif isinstance(chatbot_data, dict):
|
1320 |
+
response = "The processed data is a single dictionary, not a list of items."
|
1321 |
+
else:
|
1322 |
+
response = "The processed data is not a standard list or dictionary structure."
|
1323 |
+
|
1324 |
+
elif "what is the structure" in lower_message or "tell me about the data" in lower_message:
|
1325 |
+
if isinstance(chatbot_data, list) and chatbot_data:
|
1326 |
+
sample_item = chatbot_data[0]
|
1327 |
+
response = f"The data is a list containing {len(chatbot_data)} items. The first item has the following top-level keys: {list(sample_item.keys())}. I can try to tell you more about specific keys if you like."
|
1328 |
+
elif isinstance(chatbot_data, dict):
|
1329 |
+
response = f"The data is a dictionary with the following top-level keys: {list(chatbot_data.keys())}."
|
1330 |
+
else:
|
1331 |
+
response = "The processed data is not a standard list or dictionary structure that I can easily describe."
|
1332 |
+
|
1333 |
+
elif "show me" in lower_message or "get me" in lower_message or "extract" in lower_message:
|
1334 |
+
# Basic extraction if DataFrame wasn't created or query wasn't complex
|
1335 |
+
parts = lower_message.split("show me")
|
1336 |
+
if len(parts) > 1:
|
1337 |
+
key_request = parts[1].strip().split(" ")[0]
|
1338 |
+
extracted_values = []
|
1339 |
+
if isinstance(chatbot_data, list):
|
1340 |
+
for item in chatbot_data:
|
1341 |
+
if isinstance(item, dict) and key_request in item:
|
1342 |
+
extracted_values.append(item[key_request])
|
1343 |
+
elif isinstance(chatbot_data, dict) and key_request in chatbot_data:
|
1344 |
+
extracted_values.append(chatbot_data[key_request])
|
1345 |
+
|
1346 |
+
if extracted_values:
|
1347 |
+
preview = json.dumps(extracted_values, indent=2)[:500] + "..." if len(json.dumps(extracted_values)) > 500 else json.dumps(extracted_values, indent=2)
|
1348 |
+
response = f"Here are the values for '{key_request}':\n```json\n{preview}\n```"
|
1349 |
+
else:
|
1350 |
+
response = f"I couldn't find a key named '{key_request}' in the top level of the data items."
|
1351 |
+
else:
|
1352 |
+
response = "What specifically would you like me to show or extract?"
|
1353 |
+
|
1354 |
+
# --- Speculation about Modifications ---
|
1355 |
+
elif "how can i modify" in lower_message or "how to change" in lower_message or "can i add" in lower_message or "can i remove" in lower_message:
|
1356 |
+
response = "I cannot directly modify the data here, but I can tell you how you *could* modify it. What kind of change are you considering (e.g., adding an item, changing a value, removing a field)?"
|
1357 |
+
|
1358 |
+
elif "add a field" in lower_message or "add a column" in lower_message:
|
1359 |
+
response = "To add a field (or column if the data is tabular), you would typically iterate through each item (or row) in the data and add the new key-value pair. For example, adding a 'status' field with a default value."
|
1360 |
+
|
1361 |
+
elif "change a value" in lower_message or "update a field" in lower_message:
|
1362 |
+
response = "To change a value, you would need to identify the specific item(s) and the field you want to update. You could use a condition (like filtering) to find the right items and then assign a new value to the field."
|
1363 |
+
|
1364 |
+
elif "remove a field" in lower_message or "delete a column" in lower_message:
|
1365 |
+
response = "To remove a field, you would iterate through each item and delete the specified key. Be careful, as this is irreversible."
|
1366 |
+
|
1367 |
+
elif "restructure" in lower_message or "change the format" in lower_message:
|
1368 |
+
response = "Restructuring data involves transforming it into a different shape. This could mean flattening nested objects, grouping items, or pivoting data. This often requires writing custom code to map the old structure to the new one."
|
1369 |
+
|
1370 |
+
elif "what if i" in lower_message or "if i changed" in lower_message:
|
1371 |
+
response = "Tell me what specific change you're contemplating, and I can speculate on the potential impact or how you might approach it programmatically."
|
1372 |
+
|
1373 |
+
|
1374 |
+
# --- General Conversation / Fallback ---
|
1375 |
+
elif "hello" in lower_message or "hi" in lower_message:
|
1376 |
+
response = random.choice(["Hello! How can I help you understand the processed data?", "Hi there! What's on your mind about this data?", "Hey! Ask me anything about the data you've loaded."])
|
1377 |
+
|
1378 |
+
elif "thank you" in lower_message or "thanks" in lower_message:
|
1379 |
+
response = random.choice(["You're welcome!", "Glad I could help.", "No problem! Let me know if you have more questions about the data."])
|
1380 |
+
|
1381 |
+
elif "clear chat" in lower_message:
|
1382 |
+
chat_history = [] # Clear history
|
1383 |
+
response = "Chat history cleared."
|
1384 |
+
|
1385 |
+
elif not response: # If still no specific response matched
|
1386 |
+
response = random.choice([
|
1387 |
+
"I can analyze the data you've processed. What would you like to know?",
|
1388 |
+
"Ask me about the number of items, the structure, or values of specific fields.",
|
1389 |
+
"I can perform basic analysis like counting unique values or calculating sums/averages if the data is suitable.",
|
1390 |
+
"Tell me what you want to extract or filter from the data.",
|
1391 |
+
"I'm still learning, but I can try to answer questions about the data structure and content."
|
1392 |
+
])
|
1393 |
+
|
1394 |
+
except Exception as e:
|
1395 |
+
logger.error(f"Chatbot runtime error: {e}")
|
1396 |
+
response = f"An internal error occurred while processing your request: {e}"
|
1397 |
+
response += "\nPlease try rephrasing your question or clear the chat history."
|
1398 |
+
|
1399 |
+
|
1400 |
+
# Update the last message in history with the bot's response
|
1401 |
+
if chat_history and chat_history[-1][1] == "":
|
1402 |
+
chat_history[-1] = (chat_history[-1][0], response)
|
1403 |
+
else:
|
1404 |
+
# This case should ideally not happen if the initial append was done correctly
|
1405 |
+
chat_history.append(("", response)) # Append bot response if something went wrong with initial append
|
1406 |
+
|
1407 |
+
|
1408 |
+
return chat_history, chatbot_data # Return updated history and data state
|
1409 |
+
|
1410 |
+
|
1411 |
+
# --- Gradio Interface Definition ---
|
1412 |
def create_modern_interface():
|
1413 |
"""Create a modern and visually appealing Gradio interface"""
|
1414 |
|
|
|
1545 |
)
|
1546 |
|
1547 |
qr_code_paths = gr.State([])
|
1548 |
+
chatbot_data = gr.State(None) # State to hold the processed JSON data for the chatbot
|
1549 |
gr.Markdown("""
|
1550 |
# π Advanced Data Processing & QR Code Generator
|
1551 |
Transform your data into beautifully designed, sequenced QR codes with our cutting-edge processor.
|
|
|
1600 |
viewport_output = gr.HTML(label="QR Code Sequence Viewport")
|
1601 |
enabled_qr_codes = gr.State([]) # To store the enabled/disabled state
|
1602 |
|
1603 |
+
with gr.Tab("π€ Chat with Data") as chat_tab:
|
1604 |
+
chat_history = gr.State([]) # State to hold chat history
|
1605 |
+
chatbot = gr.Chatbot(label="Data Chatbot")
|
1606 |
+
with gr.Row():
|
1607 |
+
chat_input = gr.Textbox(label="Your Message", placeholder="Ask me about the processed data...")
|
1608 |
+
send_msg_btn = gr.Button("Send")
|
1609 |
+
clear_chat_btn = gr.Button("Clear Chat History")
|
1610 |
+
|
1611 |
+
|
1612 |
# Load example data
|
1613 |
def load_example():
|
1614 |
example = {
|
|
|
1640 |
return json.dumps(example, indent=2)
|
1641 |
|
1642 |
def clear_input():
|
1643 |
+
return "", None, "", None # Clear url, files, text, and chatbot data state
|
1644 |
|
1645 |
def update_viewport(paths, enabled_states):
|
1646 |
if not paths:
|
|
|
1765 |
logger.error(f"Overall processing error in process_inputs: {e}")
|
1766 |
processing_status_messages.append(f"β An unexpected error occurred during processing: {str(e)}")
|
1767 |
|
1768 |
+
# Return processed data for the chatbot state
|
1769 |
return (
|
1770 |
final_json_output,
|
1771 |
[str(path) for path in qr_paths], # Gradio Gallery expects list of paths (strings)
|
1772 |
+
"\n".join(processing_status_messages), # Join status messages
|
1773 |
+
final_json_output # Pass the processed data to the chatbot_data state
|
1774 |
)
|
1775 |
|
1776 |
def on_qr_generation(qr_paths_list):
|
|
|
1786 |
|
1787 |
# Link events
|
1788 |
example_btn.click(load_example, inputs=[], outputs=text_input)
|
1789 |
+
clear_btn.click(clear_input, inputs=[], outputs=[url_input, file_input, text_input, chatbot_data]) # Clear all inputs including chatbot data state
|
1790 |
|
1791 |
process_btn.click(
|
1792 |
process_inputs,
|
1793 |
inputs=[url_input, file_input, text_input, combine_data, crawl_depth_slider], # Pass crawl_depth_slider value
|
1794 |
+
outputs=[output_json, output_gallery, output_text, chatbot_data] # Output to chatbot_data state
|
1795 |
).then( # Chain a .then() to update the QR paths state and trigger viewport update
|
1796 |
on_qr_generation,
|
1797 |
inputs=[output_gallery], # Get the list of paths from the gallery output
|
|
|
1801 |
# The viewport tab's select event will trigger update_viewport to render the grid
|
1802 |
viewport_tab.select(update_viewport, inputs=[qr_code_paths, enabled_qr_codes], outputs=[viewport_output])
|
1803 |
|
1804 |
+
# Chatbot interactions
|
1805 |
+
send_msg_btn.click(
|
1806 |
+
respond_to_chat,
|
1807 |
+
inputs=[chat_input, chat_history, chatbot_data], # Pass chatbot_data state
|
1808 |
+
outputs=[chatbot, chatbot_data] # Update chatbot display and chatbot_data state
|
1809 |
+
).then(
|
1810 |
+
lambda: "", # Clear the input box after sending
|
1811 |
+
inputs=None,
|
1812 |
+
outputs=chat_input
|
1813 |
+
)
|
1814 |
+
|
1815 |
+
chat_input.submit( # Allow sending message by pressing Enter
|
1816 |
+
respond_to_chat,
|
1817 |
+
inputs=[chat_input, chat_history, chatbot_data], # Pass chatbot_data state
|
1818 |
+
outputs=[chatbot, chatbot_data] # Update chatbot display and chatbot_data state
|
1819 |
+
).then(
|
1820 |
+
lambda: "", # Clear the input box after sending
|
1821 |
+
inputs=None,
|
1822 |
+
outputs=chat_input
|
1823 |
+
)
|
1824 |
+
|
1825 |
+
clear_chat_btn.click(
|
1826 |
+
lambda: [], # Clear chat history
|
1827 |
+
inputs=None,
|
1828 |
+
outputs=chatbot
|
1829 |
+
)
|
1830 |
+
|
1831 |
+
|
1832 |
# Add helpful documentation
|
1833 |
gr.Markdown("""
|
1834 |
### π Features
|
|
|
1841 |
- **Sequential QR Codes**: Maintains data integrity across multiple codes by chunking the combined/individual processed data.
|
1842 |
- **QR Code Viewport**: Visualize generated QR codes in a sequenced square grid with options to enable/disable individual codes for selective scanning/sharing.
|
1843 |
- **Modern Design**: Clean, responsive interface with visual feedback.
|
1844 |
+
- **Data Chatbot**: Interact conversationally with the processed JSON data to ask questions about its structure, content, or request specific information.
|
1845 |
### π‘ Tips
|
1846 |
1. **URLs**: Enter multiple URLs separated by commas or newlines. The processor will attempt to fetch and structure the content based on its type, following links up to the specified **Crawl Depth**.
|
1847 |
2. **Files**: Upload any type of file. The processor will attempt to handle supported text-based files, archives (.zip, .tar, .gz), and specific document/structured formats.
|
|
|
1850 |
5. **QR Codes**: Choose whether to "Combine all data into sequence" or generate separate sequences for each input item.
|
1851 |
6. **Processing**: Monitor the "Processing Status" box for real-time updates and notes about errors or processing steps.
|
1852 |
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.
|
1853 |
+
8. **Chatbot**: After processing data, go to the "Chat with Data" tab to ask questions about the JSON output.
|
1854 |
### βοΈ QR Code Viewport Instructions
|
1855 |
1. Navigate to the **QR Code Viewport** tab after generating QR codes.
|
1856 |
2. The generated QR codes will be displayed in a grid based on their total count.
|