bluenevus commited on
Commit
27430be
·
1 Parent(s): d4e44f6

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +238 -105
app.py CHANGED
@@ -6,7 +6,7 @@ from docx import Document
6
  from io import BytesIO
7
  import dash
8
  import dash_bootstrap_components as dbc
9
- from dash import html, dcc, Input, Output, State, callback_context, ALL
10
  from dash.dash_table import DataTable
11
  from PyPDF2 import PdfReader
12
  import logging
@@ -229,7 +229,7 @@ def get_left_col_content():
229
  chat_card
230
  ]
231
 
232
- def get_right_col_content(selected_type, shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc):
233
  controls = []
234
  controls.append(html.Div([
235
  html.Div(className="blinking-dot", style={'margin':'0 auto','width':'16px','height':'16px'}),
@@ -239,11 +239,13 @@ def get_right_col_content(selected_type, shred_doc, pink_doc, pink_review_doc, r
239
  type="dot",
240
  children=[html.Div(id="loading-output")]
241
  ))
 
 
242
  if selected_type == "Shred":
243
  controls.append(
244
  html.Div([
245
  dcc.Upload(
246
- id='upload-document',
247
  children=html.Div([
248
  'Drag and Drop or ',
249
  html.A('Select Files')
@@ -263,40 +265,35 @@ def get_right_col_content(selected_type, shred_doc, pink_doc, pink_review_doc, r
263
  file_list_component()
264
  ])
265
  )
 
266
  elif selected_type in doc_dependencies:
267
  sources = doc_dependencies[selected_type]["source"]
268
  for src in sources:
269
  label = ""
270
  loaded_preview = None
271
- store_var = None
272
  if src == "shred":
273
  label = "Shred (Requirements)"
274
- store_var = shred_doc
275
- loaded_preview = markdown_table_preview(shred_doc)
276
  elif src == "pink":
277
  label = "Pink Document"
278
- store_var = pink_doc
279
- loaded_preview = markdown_narrative_preview(pink_doc)
280
  elif src == "pink_review":
281
  label = "Pink Review"
282
- store_var = pink_review_doc
283
- loaded_preview = markdown_table_preview(pink_review_doc)
284
  elif src == "red":
285
  label = "Red Document"
286
- store_var = red_doc
287
- loaded_preview = markdown_narrative_preview(red_doc)
288
  elif src == "red_review":
289
  label = "Red Review"
290
- store_var = red_review_doc
291
- loaded_preview = markdown_table_preview(red_review_doc)
292
  elif src == "gold":
293
  label = "Gold Document"
294
- store_var = gold_doc
295
- loaded_preview = markdown_narrative_preview(gold_doc)
296
  controls.append(html.Div([
297
  html.Label(f"Upload {label}"),
298
  dcc.Upload(
299
- id={'type': f'upload-doc-type-{src}', 'index': selected_type},
300
  children=html.Div(['Drag and Drop or ', html.A('Select File')]),
301
  style={
302
  'width': '100%',
@@ -310,14 +307,13 @@ def get_right_col_content(selected_type, shred_doc, pink_doc, pink_review_doc, r
310
  },
311
  multiple=False
312
  ),
313
- html.Div(id={'type': f'uploaded-doc-name-{src}', 'index': selected_type}),
314
  dbc.RadioItems(
315
- id={'type': f'radio-doc-source-{src}', 'index': selected_type},
316
  options=[
317
- {'label': 'Loaded Document', 'value': 'loaded'},
318
- {'label': 'Uploaded Document', 'value': 'uploaded'}
319
- ] if store_var else [{'label': 'Uploaded Document', 'value': 'uploaded'}],
320
- value='loaded' if store_var else 'uploaded',
321
  inline=True,
322
  className="mb-2"
323
  ),
@@ -438,75 +434,143 @@ def update_selected_doc_type(n_clicks_list, btn_ids):
438
  State('store-red', 'data'),
439
  State('store-red-review', 'data'),
440
  State('store-gold', 'data'),
441
- )
442
- def update_right_col(selected_type, shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc):
443
- return get_right_col_content(selected_type, shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc)
444
-
445
- @app.callback(
446
- Output('file-list', 'children'),
447
- Output('store-shred', 'data'),
448
- Output('document-preview', 'children'),
449
- Output('loading-output', 'children'),
450
- Output('store-generated-doc', 'data'),
451
- Input('upload-document', 'contents'),
452
- State('upload-document', 'filename'),
453
- State('file-list', 'children'),
454
- State('store-shred', 'data'),
455
- Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
456
- State('selected-doc-type', 'data'),
457
- State('store-shred', 'data'),
458
- State('store-pink', 'data'),
459
- State('store-pink-review', 'data'),
460
- State('store-red', 'data'),
461
- State('store-red-review', 'data'),
462
- State('store-gold', 'data'),
463
  State('store-gold-review', 'data'),
464
  State('store-loe', 'data'),
465
  State('store-virtual-board', 'data'),
466
- prevent_initial_call=True
467
  )
468
- def combined_callback(
469
- upload_contents, upload_filenames, existing_files, current_shred,
470
- n_clicks, selected_type, shred, pink, pink_review, red, red_review, gold, gold_review, loe, virtual_board
471
- ):
472
- ctx = callback_context
473
- trigger = ctx.triggered[0]['prop_id'].split('.')[0] if ctx.triggered else ""
474
- files_display = existing_files if existing_files else []
475
- new_shred = current_shred
476
- preview = dash.no_update
477
- loading_msg = ""
478
- generated_doc = dash.no_update
 
 
479
 
480
- # Only handle upload if upload-document is present in layout
481
- if trigger == "upload-document" and upload_contents is not None and upload_filenames is not None:
482
- for content, name in zip(upload_contents, upload_filenames):
483
- file_content = process_document(content, name)
484
- uploaded_files[name] = file_content
485
- new_shred = file_content
486
- files_display.append(
487
- dbc.Row([
488
- dbc.Col(
489
- html.Button('×', id={'type': 'remove-file', 'index': name}, style={'marginRight': '5px', 'fontSize': '10px'}),
490
- width=1
491
- ),
492
- dbc.Col(
493
- html.Span(name, style={'wordBreak': 'break-all'}),
494
- width=11
495
- )
496
- ], id={'type': 'file-row', 'index': name}, align="center", className="mb-1")
497
- )
498
- logging.info("Shred document uploaded and stored.")
499
- preview = markdown_table_preview(new_shred)
500
- generated_doc = dash.no_update
501
- return files_display, new_shred, preview, loading_msg, generated_doc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
 
503
- # Always show preview for current shred if nothing else triggered, for Shred only
504
- if selected_type == "Shred" and not any(n_clicks):
505
- preview = markdown_table_preview(new_shred)
506
- return files_display, new_shred, preview, loading_msg, generated_doc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
 
508
- # Handle generate document
509
- if any(n_clicks):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
  logging.info(f"Generating document for type: {selected_type}")
511
  inputs = {
512
  "shred": shred,
@@ -541,46 +605,115 @@ def combined_callback(
541
  t.start()
542
  t.join(timeout=60)
543
  generated_doc = result_holder.get('result', 'Error: No document generated.')
544
- # Always update preview for all doc types
545
  if selected_type in spreadsheet_types:
546
  preview = markdown_table_preview(generated_doc)
547
  else:
548
  preview = markdown_narrative_preview(generated_doc)
549
  logging.info("Document preview updated.")
550
- return files_display, new_shred, preview, "", generated_doc
551
-
552
- # For all other doc types, show preview of generated doc if present
553
- if selected_type != "Shred":
554
- if generated_doc not in (None, "", dash.no_update):
555
- if selected_type in spreadsheet_types:
556
- preview = markdown_table_preview(generated_doc)
557
- else:
558
- preview = markdown_narrative_preview(generated_doc)
559
- return files_display, new_shred, preview, loading_msg, generated_doc
560
-
561
- # Default: clear preview for non-shred if nothing else
562
- if selected_type != "Shred":
563
- return files_display, new_shred, html.Div(), loading_msg, generated_doc
 
 
 
 
 
 
 
 
 
 
 
 
 
564
 
565
- return files_display, new_shred, markdown_table_preview(new_shred), loading_msg, generated_doc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
 
567
  @app.callback(
568
  Output("download-document", "data"),
569
  Input("btn-download", "n_clicks"),
570
  State('selected-doc-type', 'data'),
571
  State('store-generated-doc', 'data'),
 
 
 
 
 
 
 
 
 
572
  prevent_initial_call=True
573
  )
574
- def download_document(n_clicks, selected_type, generated_doc):
575
- if not generated_doc:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
  logging.warning("No generated document to download.")
577
  return dash.no_update
578
  if selected_type in spreadsheet_types:
579
- xlsx_io = markdown_tables_to_xlsx(generated_doc)
580
  logging.info("Spreadsheet document prepared for download.")
581
  return dcc.send_bytes(xlsx_io.getvalue(), filename=f"{selected_type}_output.xlsx")
582
  else:
583
- content = generated_doc.encode('utf-8')
584
  logging.info("Narrative document prepared for download.")
585
  return dcc.send_bytes(content, filename=f"{selected_type}_output.md")
586
 
 
6
  from io import BytesIO
7
  import dash
8
  import dash_bootstrap_components as dbc
9
+ from dash import html, dcc, Input, Output, State, callback_context, ALL, MATCH
10
  from dash.dash_table import DataTable
11
  from PyPDF2 import PdfReader
12
  import logging
 
229
  chat_card
230
  ]
231
 
232
+ def get_right_col_content(selected_type, store_data):
233
  controls = []
234
  controls.append(html.Div([
235
  html.Div(className="blinking-dot", style={'margin':'0 auto','width':'16px','height':'16px'}),
 
239
  type="dot",
240
  children=[html.Div(id="loading-output")]
241
  ))
242
+
243
+ # For Shred, show upload
244
  if selected_type == "Shred":
245
  controls.append(
246
  html.Div([
247
  dcc.Upload(
248
+ id={'type': 'upload-doc-type', 'subtype': 'shred', 'index': selected_type},
249
  children=html.Div([
250
  'Drag and Drop or ',
251
  html.A('Select Files')
 
265
  file_list_component()
266
  ])
267
  )
268
+ # For others, show dependency uploads and previews
269
  elif selected_type in doc_dependencies:
270
  sources = doc_dependencies[selected_type]["source"]
271
  for src in sources:
272
  label = ""
273
  loaded_preview = None
274
+ store_var = store_data.get(src)
275
  if src == "shred":
276
  label = "Shred (Requirements)"
277
+ loaded_preview = markdown_table_preview(store_var)
 
278
  elif src == "pink":
279
  label = "Pink Document"
280
+ loaded_preview = markdown_narrative_preview(store_var)
 
281
  elif src == "pink_review":
282
  label = "Pink Review"
283
+ loaded_preview = markdown_table_preview(store_var)
 
284
  elif src == "red":
285
  label = "Red Document"
286
+ loaded_preview = markdown_narrative_preview(store_var)
 
287
  elif src == "red_review":
288
  label = "Red Review"
289
+ loaded_preview = markdown_table_preview(store_var)
 
290
  elif src == "gold":
291
  label = "Gold Document"
292
+ loaded_preview = markdown_narrative_preview(store_var)
 
293
  controls.append(html.Div([
294
  html.Label(f"Upload {label}"),
295
  dcc.Upload(
296
+ id={'type': 'upload-doc-type', 'subtype': src, 'index': selected_type},
297
  children=html.Div(['Drag and Drop or ', html.A('Select File')]),
298
  style={
299
  'width': '100%',
 
307
  },
308
  multiple=False
309
  ),
310
+ html.Div(id={'type': 'uploaded-doc-name', 'subtype': src, 'index': selected_type}),
311
  dbc.RadioItems(
312
+ id={'type': 'radio-doc-source', 'subtype': src, 'index': selected_type},
313
  options=[
314
+ {'label': 'Loaded Document', 'value': 'loaded'}
315
+ ] if store_var else [],
316
+ value='loaded' if store_var else None,
 
317
  inline=True,
318
  className="mb-2"
319
  ),
 
434
  State('store-red', 'data'),
435
  State('store-red-review', 'data'),
436
  State('store-gold', 'data'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  State('store-gold-review', 'data'),
438
  State('store-loe', 'data'),
439
  State('store-virtual-board', 'data'),
 
440
  )
441
+ def update_right_col(selected_type, shred, pink, pink_review, red, red_review, gold, gold_review, loe, virtual_board):
442
+ store_data = {
443
+ "shred": shred,
444
+ "pink": pink,
445
+ "pink_review": pink_review,
446
+ "red": red,
447
+ "red_review": red_review,
448
+ "gold": gold,
449
+ "gold_review": gold_review,
450
+ "loe": loe,
451
+ "virtual_board": virtual_board
452
+ }
453
+ return get_right_col_content(selected_type, store_data)
454
 
455
+ @app.callback(
456
+ [
457
+ Output('store-shred', 'data'),
458
+ Output('file-list', 'children'),
459
+ Output({'type': 'uploaded-doc-name', 'subtype': 'shred', 'index': 'Shred'}, 'children')
460
+ ],
461
+ [
462
+ Input({'type': 'upload-doc-type', 'subtype': 'shred', 'index': 'Shred'}, 'contents')
463
+ ],
464
+ [
465
+ State({'type': 'upload-doc-type', 'subtype': 'shred', 'index': 'Shred'}, 'filename'),
466
+ State('store-shred', 'data'),
467
+ State('file-list', 'children')
468
+ ],
469
+ prevent_initial_call=True
470
+ )
471
+ def handle_shred_upload(contents, filenames, current_shred, file_list):
472
+ if not contents or not filenames:
473
+ raise dash.exceptions.PreventUpdate
474
+ if isinstance(contents, str):
475
+ contents = [contents]
476
+ filenames = [filenames]
477
+ file_previews = []
478
+ latest_text = current_shred
479
+ for content, name in zip(contents, filenames):
480
+ file_text = process_document(content, name)
481
+ uploaded_files[name] = file_text
482
+ latest_text = file_text
483
+ file_previews.append(
484
+ dbc.Row([
485
+ dbc.Col(
486
+ html.Button('×', id={'type': 'remove-file', 'index': name}, style={'marginRight': '5px', 'fontSize': '10px'}),
487
+ width=1
488
+ ),
489
+ dbc.Col(
490
+ html.Span(name, style={'wordBreak': 'break-all'}),
491
+ width=11
492
+ )
493
+ ], id={'type': 'file-row', 'index': name}, align="center", className="mb-1")
494
+ )
495
+ logging.info("Shred document uploaded and stored.")
496
+ return latest_text, file_previews, filenames[-1]
497
 
498
+ # Pattern-matching callback for all dependency uploads
499
+ @app.callback(
500
+ [
501
+ Output('store-pink', 'data'),
502
+ Output('store-pink-review', 'data'),
503
+ Output('store-red', 'data'),
504
+ Output('store-red-review', 'data'),
505
+ Output('store-gold', 'data'),
506
+ Output('store-gold-review', 'data'),
507
+ Output('store-loe', 'data'),
508
+ Output('store-virtual-board', 'data'),
509
+ Output({'type': 'uploaded-doc-name', 'subtype': MATCH, 'index': MATCH}, 'children')
510
+ ],
511
+ [
512
+ Input({'type': 'upload-doc-type', 'subtype': ALL, 'index': ALL}, 'contents')
513
+ ],
514
+ [
515
+ State({'type': 'upload-doc-type', 'subtype': ALL, 'index': ALL}, 'filename')
516
+ ],
517
+ prevent_initial_call=True
518
+ )
519
+ def handle_dep_upload(contents_list, filenames_list):
520
+ stores = [dash.no_update]*8
521
+ upload_name = ""
522
+ if not contents_list or not filenames_list:
523
+ return [dash.no_update]*8 + [upload_name]
524
+ for i, (contents, filenames) in enumerate(zip(contents_list, filenames_list)):
525
+ if contents and filenames:
526
+ if isinstance(contents, str):
527
+ contents = [contents]
528
+ filenames = [filenames]
529
+ for content, name in zip(contents, filenames):
530
+ file_text = process_document(content, name)
531
+ if 'pink' in name.lower():
532
+ stores[0] = file_text
533
+ elif 'pink_review' in name.lower():
534
+ stores[1] = file_text
535
+ elif 'red' in name.lower() and 'review' not in name.lower():
536
+ stores[2] = file_text
537
+ elif 'red_review' in name.lower():
538
+ stores[3] = file_text
539
+ elif 'gold' in name.lower() and 'review' not in name.lower():
540
+ stores[4] = file_text
541
+ elif 'gold_review' in name.lower():
542
+ stores[5] = file_text
543
+ elif 'loe' in name.lower():
544
+ stores[6] = file_text
545
+ elif 'virtual' in name.lower():
546
+ stores[7] = file_text
547
+ upload_name = name
548
+ return stores + [upload_name]
549
 
550
+ @app.callback(
551
+ Output('document-preview', 'children'),
552
+ [
553
+ Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
554
+ Input('selected-doc-type', 'data'),
555
+ Input('store-shred', 'data'),
556
+ Input('store-pink', 'data'),
557
+ Input('store-pink-review', 'data'),
558
+ Input('store-red', 'data'),
559
+ Input('store-red-review', 'data'),
560
+ Input('store-gold', 'data'),
561
+ Input('store-gold-review', 'data'),
562
+ Input('store-loe', 'data'),
563
+ Input('store-virtual-board', 'data'),
564
+ State('store-generated-doc', 'data')
565
+ ],
566
+ prevent_initial_call=True
567
+ )
568
+ def preview_or_generate_doc(n_clicks_list, selected_type, shred, pink, pink_review, red, red_review, gold, gold_review, loe, virtual_board, prev_generated):
569
+ ctx = callback_context
570
+ trigger = ctx.triggered[0]['prop_id'] if ctx.triggered else ""
571
+ logging.info(f"Document preview trigger: {trigger}")
572
+ # If generate button pressed
573
+ if any(n_clicks_list):
574
  logging.info(f"Generating document for type: {selected_type}")
575
  inputs = {
576
  "shred": shred,
 
605
  t.start()
606
  t.join(timeout=60)
607
  generated_doc = result_holder.get('result', 'Error: No document generated.')
608
+ app.server.config['last_generated_doc'] = generated_doc
609
  if selected_type in spreadsheet_types:
610
  preview = markdown_table_preview(generated_doc)
611
  else:
612
  preview = markdown_narrative_preview(generated_doc)
613
  logging.info("Document preview updated.")
614
+ return preview
615
+ # If just switching doc type, show preview of loaded doc (from store)
616
+ else:
617
+ # For Shred, show table preview
618
+ if selected_type == "Shred" and shred:
619
+ return markdown_table_preview(shred)
620
+ # For spreadsheet types, preview from store
621
+ elif selected_type in spreadsheet_types:
622
+ doc_store = {
623
+ "Pink Review": pink_review,
624
+ "Red Review": red_review,
625
+ "Gold Review": gold_review,
626
+ "Virtual Board": virtual_board,
627
+ "LOE": loe
628
+ }
629
+ doc = doc_store.get(selected_type, "")
630
+ return markdown_table_preview(doc)
631
+ # For narrative types, preview from store
632
+ elif selected_type in narrative_types:
633
+ doc_store = {
634
+ "Pink": pink,
635
+ "Red": red,
636
+ "Gold": gold
637
+ }
638
+ doc = doc_store.get(selected_type, "")
639
+ return markdown_narrative_preview(doc)
640
+ return html.Div("No document loaded.")
641
 
642
+ @app.callback(
643
+ Output('store-generated-doc', 'data'),
644
+ [
645
+ Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
646
+ Input('selected-doc-type', 'data'),
647
+ Input('document-preview', 'children')
648
+ ],
649
+ [
650
+ State('store-shred', 'data'),
651
+ State('store-pink', 'data'),
652
+ State('store-pink-review', 'data'),
653
+ State('store-red', 'data'),
654
+ State('store-red-review', 'data'),
655
+ State('store-gold', 'data'),
656
+ State('store-gold-review', 'data'),
657
+ State('store-loe', 'data'),
658
+ State('store-virtual-board', 'data'),
659
+ State('store-generated-doc', 'data')
660
+ ],
661
+ prevent_initial_call=True
662
+ )
663
+ def update_generated_doc(n_clicks_list, selected_type, preview_content, shred, pink, pink_review, red, red_review, gold, gold_review, loe, virtual_board, prev_generated):
664
+ ctx = callback_context
665
+ trigger = ctx.triggered[0]['prop_id'] if ctx.triggered else ""
666
+ if any(n_clicks_list):
667
+ return app.server.config.get('last_generated_doc', prev_generated)
668
+ return prev_generated
669
 
670
  @app.callback(
671
  Output("download-document", "data"),
672
  Input("btn-download", "n_clicks"),
673
  State('selected-doc-type', 'data'),
674
  State('store-generated-doc', 'data'),
675
+ State('store-shred', 'data'),
676
+ State('store-pink', 'data'),
677
+ State('store-pink-review', 'data'),
678
+ State('store-red', 'data'),
679
+ State('store-red-review', 'data'),
680
+ State('store-gold', 'data'),
681
+ State('store-gold-review', 'data'),
682
+ State('store-loe', 'data'),
683
+ State('store-virtual-board', 'data'),
684
  prevent_initial_call=True
685
  )
686
+ def download_document(n_clicks, selected_type, generated_doc, shred, pink, pink_review, red, red_review, gold, gold_review, loe, virtual_board):
687
+ doc = generated_doc
688
+ # If not generated, try to get from store for preview/download
689
+ if not doc:
690
+ if selected_type == "Shred":
691
+ doc = shred
692
+ elif selected_type == "Pink":
693
+ doc = pink
694
+ elif selected_type == "Pink Review":
695
+ doc = pink_review
696
+ elif selected_type == "Red":
697
+ doc = red
698
+ elif selected_type == "Red Review":
699
+ doc = red_review
700
+ elif selected_type == "Gold":
701
+ doc = gold
702
+ elif selected_type == "Gold Review":
703
+ doc = gold_review
704
+ elif selected_type == "LOE":
705
+ doc = loe
706
+ elif selected_type == "Virtual Board":
707
+ doc = virtual_board
708
+ if not doc:
709
  logging.warning("No generated document to download.")
710
  return dash.no_update
711
  if selected_type in spreadsheet_types:
712
+ xlsx_io = markdown_tables_to_xlsx(doc)
713
  logging.info("Spreadsheet document prepared for download.")
714
  return dcc.send_bytes(xlsx_io.getvalue(), filename=f"{selected_type}_output.xlsx")
715
  else:
716
+ content = doc.encode('utf-8')
717
  logging.info("Narrative document prepared for download.")
718
  return dcc.send_bytes(content, filename=f"{selected_type}_output.md")
719