bluenevus commited on
Commit
b7ed401
·
1 Parent(s): 6026a72

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +102 -112
app.py CHANGED
@@ -273,99 +273,54 @@ app.layout = dbc.Container([
273
 
274
  @app.callback(
275
  Output('file-list', 'children'),
276
- Input('upload-files', 'contents'),
277
- State('upload-files', 'filename'),
278
- prevent_initial_call=True
 
279
  )
280
- def update_output(list_of_contents, list_of_names):
281
- session_data, lock = get_session_data()
282
- logger.info("Uploading files...")
283
- if list_of_contents is not None:
284
- with lock:
285
- for content, name in zip(list_of_contents, list_of_names):
286
- content_type, content_string = content.split(',')
287
- decoded = base64.b64decode(content_string)
288
- temp_path = os.path.join(session_data['temp_dir'], name)
289
- with open(temp_path, 'wb') as f:
290
- f.write(decoded)
291
- session_data['uploaded_files'][name] = temp_path
292
- session_data['file_texts'][name] = parse_file_content(temp_path, name)
293
- logger.info(f"Files after upload: {list(session_data['uploaded_files'].keys())}")
294
- return get_file_cards(session_data['uploaded_files'])
295
- raise PreventUpdate
296
-
297
- @app.callback(
298
- Output('file-list', 'children'),
299
- Input({'type': 'remove-file', 'index': ALL}, 'n_clicks'),
300
- State('file-list', 'children'),
301
- prevent_initial_call=True
302
- )
303
- def remove_file(n_clicks, file_list_children):
304
  ctx = callback_context
305
- if not ctx.triggered or not any(n_clicks):
306
- raise PreventUpdate
307
- session_data, lock = get_session_data()
308
- triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
309
- import ast
310
- try:
311
- triggered_id_dict = ast.literal_eval(triggered_id)
312
- removed_file = triggered_id_dict['index']
313
- except Exception:
314
- raise PreventUpdate
315
- with lock:
316
- if removed_file in session_data['uploaded_files']:
317
- try:
318
- os.remove(session_data['uploaded_files'][removed_file])
319
- logger.info(f"Deleted file from disk: {removed_file}")
320
- except Exception as e:
321
- logger.warning(f"Failed to delete temp file {removed_file}: {e}")
322
- session_data['uploaded_files'].pop(removed_file, None)
323
- session_data['file_texts'].pop(removed_file, None)
324
- logger.info(f"Files after deletion: {list(session_data['uploaded_files'].keys())}")
325
- return get_file_cards(session_data['uploaded_files'])
326
-
327
- @app.callback(
328
- Output('file-list', 'children', allow_duplicate=True),
329
- Input('file-list', 'children'),
330
- prevent_initial_call=False
331
- )
332
- def restore_file_list(file_list_children):
333
  session_data, lock = get_session_data()
 
334
  with lock:
335
  restore_session_files(session_data)
336
- return get_file_cards(session_data['uploaded_files'])
337
-
338
- @app.callback(
339
- Output('matrix-preview', 'children'),
340
- Output('loading-output', 'children'),
341
- [Input({'type': 'matrix-btn', 'index': matrix_label}, 'n_clicks') for matrix_label in matrix_types.keys()],
342
- prevent_initial_call=True
343
- )
344
- def generate_matrix(*args):
345
- session_data, lock = get_session_data()
346
- ctx = callback_context
347
- if not ctx.triggered:
348
- raise PreventUpdate
349
- button_id = ctx.triggered[0]['prop_id'].split('.')[0]
350
- import ast
351
- try:
352
- triggered = ast.literal_eval(button_id)
353
- matrix_type = triggered['index']
354
- except Exception:
355
- raise PreventUpdate
356
-
357
- if not session_data['uploaded_files']:
358
- return html.Div("Please upload project artifacts before generating a matrix."), ""
359
- file_contents = list(session_data['file_texts'].values())
360
- with lock:
361
  try:
362
- session_data['matrix_type'] = matrix_type
363
- session_data['current_matrix'] = generate_matrix_with_gpt(matrix_type, file_contents)
364
- logger.info(f"{matrix_type} generated for session.")
365
- return dbc.Table.from_dataframe(session_data['current_matrix'], striped=True, bordered=True, hover=True), f"{matrix_type} generated"
366
- except Exception as e:
367
- logger.exception(f"Error generating matrix: {str(e)}")
368
- return html.Div(f"Error generating matrix: {str(e)}"), "Error"
 
 
 
 
 
 
 
 
 
 
369
 
370
  def generate_matrix_with_gpt(matrix_type, file_contents):
371
  prompt = f"""Generate a {matrix_type} based on the following project artifacts:
@@ -401,19 +356,52 @@ Now, generate the {matrix_type}:
401
  return pd.DataFrame(data, columns=headers)
402
 
403
  @app.callback(
 
 
404
  Output('chat-output', 'children'),
405
- Output('matrix-preview', 'children', allow_duplicate=True),
406
- Input('btn-send-chat', 'n_clicks'),
407
- State('chat-input', 'value'),
408
- prevent_initial_call=True
409
  )
410
- def update_matrix_via_chat(n_clicks, chat_input):
411
  session_data, lock = get_session_data()
412
- if not chat_input or session_data['current_matrix'] is None or session_data['matrix_type'] is None:
 
 
 
 
 
 
413
  raise PreventUpdate
414
- matrix_type = session_data['matrix_type']
415
- with lock:
416
- prompt = f"""Update the following {matrix_type} based on this instruction: {chat_input}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  Current matrix:
418
  {session_data['current_matrix'].to_string(index=False)}
419
  Instructions:
@@ -426,22 +414,24 @@ Instructions:
426
  7. Each subsequent row should represent a single item in the matrix.
427
  Now, provide the updated {matrix_type}:
428
  """
429
- response = openai.ChatCompletion.create(
430
- model="gpt-4-turbo",
431
- messages=[
432
- {"role": "system", "content": "You are a precise matrix updater that outputs only the requested matrix without any additional text. You will make assumptions as a project manager to produce the matrix based on the limited information provided"},
433
- {"role": "user", "content": prompt}
434
- ]
435
- )
436
- updated_matrix_text = response.choices[0].message.content.strip()
437
- logger.info(f"Raw updated matrix text from GPT: {updated_matrix_text[:200]}...")
438
- lines = [line.strip() for line in updated_matrix_text.split('\n') if '|' in line]
439
- data = [line.split('|') for line in lines]
440
- data = [[cell.strip() for cell in row] for row in data]
441
- headers = data[0]
442
- data = data[1:]
443
- session_data['current_matrix'] = pd.DataFrame(data, columns=headers)
444
- return f"Matrix updated based on: {chat_input}", dbc.Table.from_dataframe(session_data['current_matrix'], striped=True, bordered=True, hover=True)
 
 
445
 
446
  @app.callback(
447
  Output("download-matrix", "data"),
 
273
 
274
  @app.callback(
275
  Output('file-list', 'children'),
276
+ [Input('upload-files', 'contents'),
277
+ Input({'type': 'remove-file', 'index': ALL}, 'n_clicks')],
278
+ [State('upload-files', 'filename')],
279
+ prevent_initial_call='initial_duplicate'
280
  )
281
+ def handle_file_upload_and_remove(list_of_contents, n_clicks, list_of_names):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ # If upload triggered
288
+ if ctx.triggered and ctx.triggered[0]['prop_id'].startswith("upload-files.contents"):
289
+ logger.info("Uploading files...")
290
+ if list_of_contents is not None:
291
+ with lock:
292
+ for content, name in zip(list_of_contents, list_of_names):
293
+ content_type, content_string = content.split(',')
294
+ decoded = base64.b64decode(content_string)
295
+ temp_path = os.path.join(session_data['temp_dir'], name)
296
+ with open(temp_path, 'wb') as f:
297
+ f.write(decoded)
298
+ session_data['uploaded_files'][name] = temp_path
299
+ session_data['file_texts'][name] = parse_file_content(temp_path, name)
300
+ logger.info(f"Files after upload: {list(session_data['uploaded_files'].keys())}")
301
+ return get_file_cards(session_data['uploaded_files'])
302
+ # If remove triggered
303
+ elif ctx.triggered and "remove-file" in ctx.triggered[0]['prop_id']:
304
+ triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
305
+ import ast
 
 
 
 
 
 
306
  try:
307
+ triggered_id_dict = ast.literal_eval(triggered_id)
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
+ os.remove(session_data['uploaded_files'][removed_file])
315
+ logger.info(f"Deleted file from disk: {removed_file}")
316
+ except Exception as e:
317
+ logger.warning(f"Failed to delete temp file {removed_file}: {e}")
318
+ session_data['uploaded_files'].pop(removed_file, None)
319
+ session_data['file_texts'].pop(removed_file, None)
320
+ logger.info(f"Files after deletion: {list(session_data['uploaded_files'].keys())}")
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:
 
356
  return pd.DataFrame(data, columns=headers)
357
 
358
  @app.callback(
359
+ Output('matrix-preview', 'children'),
360
+ Output('loading-output', 'children'),
361
  Output('chat-output', 'children'),
362
+ [Input({'type': 'matrix-btn', 'index': matrix_label}, 'n_clicks') for matrix_label in matrix_types.keys()] +
363
+ [Input('btn-send-chat', 'n_clicks')],
364
+ [State('chat-input', 'value')],
365
+ prevent_initial_call='initial_duplicate'
366
  )
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]
374
+ chat_input_value = args[matrix_btns_len + 1]
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:
382
+ triggered = ast.literal_eval(triggered_id)
383
+ matrix_type = triggered['index']
384
+ except Exception:
385
+ raise PreventUpdate
386
+ if not session_data['uploaded_files']:
387
+ return html.Div("Please upload project artifacts before generating a matrix."), "", ""
388
+ file_contents = list(session_data['file_texts'].values())
389
+ with lock:
390
+ try:
391
+ session_data['matrix_type'] = matrix_type
392
+ session_data['current_matrix'] = generate_matrix_with_gpt(matrix_type, file_contents)
393
+ logger.info(f"{matrix_type} generated for session.")
394
+ return dbc.Table.from_dataframe(session_data['current_matrix'], striped=True, bordered=True, hover=True), f"{matrix_type} generated", ""
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
402
+ matrix_type = session_data['matrix_type']
403
+ with lock:
404
+ prompt = f"""Update the following {matrix_type} based on this instruction: {chat_input_value}
405
  Current matrix:
406
  {session_data['current_matrix'].to_string(index=False)}
407
  Instructions:
 
414
  7. Each subsequent row should represent a single item in the matrix.
415
  Now, provide the updated {matrix_type}:
416
  """
417
+ response = openai.ChatCompletion.create(
418
+ model="gpt-4-turbo",
419
+ messages=[
420
+ {"role": "system", "content": "You are a precise matrix updater that outputs only the requested matrix without any additional text. You will make assumptions as a project manager to produce the matrix based on the limited information provided"},
421
+ {"role": "user", "content": prompt}
422
+ ]
423
+ )
424
+ updated_matrix_text = response.choices[0].message.content.strip()
425
+ logger.info(f"Raw updated matrix text from GPT: {updated_matrix_text[:200]}...")
426
+ lines = [line.strip() for line in updated_matrix_text.split('\n') if '|' in line]
427
+ data = [line.split('|') for line in lines]
428
+ data = [[cell.strip() for cell in row] for row in data]
429
+ headers = data[0]
430
+ data = data[1:]
431
+ session_data['current_matrix'] = pd.DataFrame(data, columns=headers)
432
+ return dbc.Table.from_dataframe(session_data['current_matrix'], striped=True, bordered=True, hover=True), "", f"Matrix updated based on: {chat_input_value}"
433
+ else:
434
+ raise PreventUpdate
435
 
436
  @app.callback(
437
  Output("download-matrix", "data"),