Update app.py via AI Editor
Browse files
app.py
CHANGED
|
@@ -167,7 +167,7 @@ def get_file_cards(file_dict):
|
|
| 167 |
dbc.Col(
|
| 168 |
dbc.Button(
|
| 169 |
"Delete",
|
| 170 |
-
id={'type': '
|
| 171 |
color="danger",
|
| 172 |
size="sm",
|
| 173 |
style={'marginLeft': 'auto', 'float': 'right'}
|
|
@@ -273,54 +273,60 @@ app.layout = dbc.Container([
|
|
| 273 |
|
| 274 |
@app.callback(
|
| 275 |
Output('file-list', 'children'),
|
| 276 |
-
[
|
| 277 |
-
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
prevent_initial_call='initial_duplicate'
|
| 280 |
)
|
| 281 |
-
def
|
| 282 |
ctx = callback_context
|
| 283 |
session_data, lock = get_session_data()
|
| 284 |
-
# On first load, restore file list from session temp dir
|
| 285 |
with lock:
|
| 286 |
restore_session_files(session_data)
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
removed_file = triggered_id_dict['index']
|
| 309 |
-
except Exception:
|
| 310 |
-
raise PreventUpdate
|
| 311 |
-
with lock:
|
| 312 |
-
if removed_file in session_data['uploaded_files']:
|
| 313 |
try:
|
| 314 |
-
|
| 315 |
-
|
| 316 |
except Exception as e:
|
| 317 |
-
logger.warning(f"
|
| 318 |
-
|
| 319 |
-
session_data['
|
| 320 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
return get_file_cards(session_data['uploaded_files'])
|
| 322 |
-
# Otherwise just display current files (e.g. on initial load)
|
| 323 |
-
return get_file_cards(session_data['uploaded_files'])
|
| 324 |
|
| 325 |
def generate_matrix_with_gpt(matrix_type, file_contents):
|
| 326 |
prompt = f"""Generate a {matrix_type} based on the following project artifacts:
|
|
@@ -367,7 +373,6 @@ Now, generate the {matrix_type}:
|
|
| 367 |
def handle_matrix_and_chat(*args):
|
| 368 |
session_data, lock = get_session_data()
|
| 369 |
ctx = callback_context
|
| 370 |
-
# Matrix generation button pressed
|
| 371 |
matrix_btns_len = len(matrix_types)
|
| 372 |
matrix_btn_inputs = args[:matrix_btns_len]
|
| 373 |
chat_n_clicks = args[matrix_btns_len]
|
|
@@ -375,7 +380,6 @@ def handle_matrix_and_chat(*args):
|
|
| 375 |
if not ctx.triggered:
|
| 376 |
raise PreventUpdate
|
| 377 |
triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
|
| 378 |
-
# If matrix button triggered
|
| 379 |
if "matrix-btn" in triggered_id:
|
| 380 |
import ast
|
| 381 |
try:
|
|
@@ -395,7 +399,6 @@ def handle_matrix_and_chat(*args):
|
|
| 395 |
except Exception as e:
|
| 396 |
logger.exception(f"Error generating matrix: {str(e)}")
|
| 397 |
return html.Div(f"Error generating matrix: {str(e)}"), "Error", ""
|
| 398 |
-
# If chat send button triggered
|
| 399 |
elif "btn-send-chat" in triggered_id:
|
| 400 |
if not chat_input_value or session_data['current_matrix'] is None or session_data['matrix_type'] is None:
|
| 401 |
raise PreventUpdate
|
|
|
|
| 167 |
dbc.Col(
|
| 168 |
dbc.Button(
|
| 169 |
"Delete",
|
| 170 |
+
id={'type': 'delete-file-btn', 'index': name},
|
| 171 |
color="danger",
|
| 172 |
size="sm",
|
| 173 |
style={'marginLeft': 'auto', 'float': 'right'}
|
|
|
|
| 273 |
|
| 274 |
@app.callback(
|
| 275 |
Output('file-list', 'children'),
|
| 276 |
+
[
|
| 277 |
+
Input('upload-files', 'contents'),
|
| 278 |
+
Input({'type': 'delete-file-btn', 'index': ALL}, 'n_clicks')
|
| 279 |
+
],
|
| 280 |
+
[
|
| 281 |
+
State('upload-files', 'filename'),
|
| 282 |
+
],
|
| 283 |
prevent_initial_call='initial_duplicate'
|
| 284 |
)
|
| 285 |
+
def handle_file_upload_and_delete(list_of_contents, delete_clicks, list_of_names):
|
| 286 |
ctx = callback_context
|
| 287 |
session_data, lock = get_session_data()
|
|
|
|
| 288 |
with lock:
|
| 289 |
restore_session_files(session_data)
|
| 290 |
+
triggered = ctx.triggered
|
| 291 |
+
if triggered:
|
| 292 |
+
prop_id = triggered[0]['prop_id']
|
| 293 |
+
# Handle file upload
|
| 294 |
+
if prop_id.startswith("upload-files.contents"):
|
| 295 |
+
logger.info("Uploading files...")
|
| 296 |
+
if list_of_contents is not None and list_of_names is not None:
|
| 297 |
+
for content, name in zip(list_of_contents, list_of_names):
|
| 298 |
+
content_type, content_string = content.split(',')
|
| 299 |
+
decoded = base64.b64decode(content_string)
|
| 300 |
+
temp_path = os.path.join(session_data['temp_dir'], name)
|
| 301 |
+
with open(temp_path, 'wb') as f:
|
| 302 |
+
f.write(decoded)
|
| 303 |
+
session_data['uploaded_files'][name] = temp_path
|
| 304 |
+
session_data['file_texts'][name] = parse_file_content(temp_path, name)
|
| 305 |
+
logger.info(f"Files after upload: {list(session_data['uploaded_files'].keys())}")
|
| 306 |
+
return get_file_cards(session_data['uploaded_files'])
|
| 307 |
+
# Handle delete button click
|
| 308 |
+
elif "delete-file-btn" in prop_id:
|
| 309 |
+
import ast
|
| 310 |
+
btn_id = prop_id.split('.')[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
try:
|
| 312 |
+
btn_id_dict = ast.literal_eval(btn_id)
|
| 313 |
+
filename = btn_id_dict['index']
|
| 314 |
except Exception as e:
|
| 315 |
+
logger.warning(f"Could not extract filename from delete prop_id: {prop_id} error: {e}")
|
| 316 |
+
raise PreventUpdate
|
| 317 |
+
if filename in session_data['uploaded_files']:
|
| 318 |
+
filepath = session_data['uploaded_files'][filename]
|
| 319 |
+
try:
|
| 320 |
+
os.remove(filepath)
|
| 321 |
+
logger.info(f"Deleted file from disk: {filename}")
|
| 322 |
+
except Exception as e:
|
| 323 |
+
logger.warning(f"Failed to delete temp file {filename}: {e}")
|
| 324 |
+
session_data['uploaded_files'].pop(filename, None)
|
| 325 |
+
session_data['file_texts'].pop(filename, None)
|
| 326 |
+
logger.info(f"Files after deletion: {list(session_data['uploaded_files'].keys())}")
|
| 327 |
+
return get_file_cards(session_data['uploaded_files'])
|
| 328 |
+
# On initial load or no trigger, show files from session
|
| 329 |
return get_file_cards(session_data['uploaded_files'])
|
|
|
|
|
|
|
| 330 |
|
| 331 |
def generate_matrix_with_gpt(matrix_type, file_contents):
|
| 332 |
prompt = f"""Generate a {matrix_type} based on the following project artifacts:
|
|
|
|
| 373 |
def handle_matrix_and_chat(*args):
|
| 374 |
session_data, lock = get_session_data()
|
| 375 |
ctx = callback_context
|
|
|
|
| 376 |
matrix_btns_len = len(matrix_types)
|
| 377 |
matrix_btn_inputs = args[:matrix_btns_len]
|
| 378 |
chat_n_clicks = args[matrix_btns_len]
|
|
|
|
| 380 |
if not ctx.triggered:
|
| 381 |
raise PreventUpdate
|
| 382 |
triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
|
|
|
|
| 383 |
if "matrix-btn" in triggered_id:
|
| 384 |
import ast
|
| 385 |
try:
|
|
|
|
| 399 |
except Exception as e:
|
| 400 |
logger.exception(f"Error generating matrix: {str(e)}")
|
| 401 |
return html.Div(f"Error generating matrix: {str(e)}"), "Error", ""
|
|
|
|
| 402 |
elif "btn-send-chat" in triggered_id:
|
| 403 |
if not chat_input_value or session_data['current_matrix'] is None or session_data['matrix_type'] is None:
|
| 404 |
raise PreventUpdate
|